Adapter Contract Preview
The examples on this page are illustrative until the public Go SDK package is stable. They describe the Agent Runtime adapter contract inside Edge Server and do not imply a current stable import path.
Runtime adapters connect execution engines such as Claude Code, Codex, OpenCode, or a custom agent runtime to AgentHub.
Not a stable public SDK
Treat this page as an adapter contract preview. It is useful for understanding Edge Server integration boundaries, event normalization, cancellation, and approval behavior. It is not yet a third-party package guide, a marketplace submission guide, or a compatibility guarantee.
Overview
Each runtime adapter should provide three things:
- Capability metadata so AgentHub can select an appropriate profile/runtime pair.
- Lifecycle control so Edge can start, cancel, time out, and inspect a run.
- Structured events so Desktop/Web/Hub can render progress, tool calls, diffs, artifacts, approvals, and completion state.
The adapter is not the user-facing Agent Profile. A profile is product configuration; an adapter is execution machinery.
Stability Boundary
| Area | Current stability | Notes |
|---|---|---|
| Runtime purpose | Stable direction | Adapters normalize real coding runtimes into AgentHub runs and events |
| Event categories | Contract shaped | Progress, tool call, tool result, diff, artifact, approval, completion, and failure are expected |
| Import path | Not stable | example.com/agenthub/adapter is an illustrative placeholder |
| Public package | Not stable | Third-party integration instructions are not yet generally available |
| Submission flow | Not defined | Third-party review, signing, compatibility tests, and docs requirements still need product work |
| Security policy | Required | Adapters must respect workspace allowlist, cancellation, redaction, and approval policy |
Before this page becomes a stable SDK reference, it needs a published package path, versioning policy, conformance tests, migration notes, and a documented third-party review process.
Before Copying These Examples
The code below is shaped like an SDK guide so the contract is readable, but it is not copy-ready production integration material.| Question | Current answer |
|---|---|
| Can I import this path? | No. example.com/agenthub/adapter is a placeholder. |
| Can I publish a third-party adapter today? | Not as a stable ecosystem submission. The review and packaging process is not defined. |
| Are event names stable? | The run-scoped vocabulary in API And Events is the public direction; schemas still need versioned conformance tests. |
| Where do provider keys go? | Local/server-side secret storage only. Do not put API keys in frontend code, doc screenshots, or adapter examples pasted into issues. |
| What blocks stable SDK status? | Published module path, semantic versioning, conformance tests, migration policy, security review, and submission checklist. |
If you are implementing inside the AgentHub repository, use the owning Edge Server interfaces and tests. If you are designing an external adapter, treat this page as product contract context until the public package exists.
AgentAdapter Interface
// AgentAdapter defines the contract every runtime adapter must implement.
type AgentAdapter interface {
Name() string
Capabilities() []Capability
Execute(ctx context.Context, task Task, events chan<- AgentEvent) (*AgentResult, error)
Validate(config AgentConfig) error
}
Implementing An Adapter
Step 1: Define Your Agent
package myadapter
import "example.com/agenthub/adapter" // illustrative placeholder
type MyAgent struct {
config adapter.AgentConfig
client *http.Client
}
func New(config adapter.AgentConfig) *MyAgent {
return &MyAgent{
config: config,
client: &http.Client{Timeout: 30 * time.Minute},
}
}
func (a *MyAgent) Name() string {
return "my-agent"
}
Step 2: Declare Capabilities
func (a *MyAgent) Capabilities() []adapter.Capability {
return []adapter.Capability{
{
Name: "code_review",
Description: "Reviews code for bugs, style, and security issues.",
Priority: 8,
},
{
Name: "refactoring",
Description: "Suggests and applies code refactorings.",
Priority: 6,
},
}
}
Step 3: Implement Execute
Execute receives a task, streams progress events, and returns a final result.
func (a *MyAgent) Execute(
ctx context.Context,
task adapter.Task,
events chan<- adapter.AgentEvent,
) (*adapter.AgentResult, error) {
defer close(events)
events <- adapter.AgentEvent{
Type: adapter.EventProgress,
Message: "Analyzing task...",
}
prompt := buildPrompt(task)
events <- adapter.AgentEvent{
Type: adapter.EventProgress,
Message: "Generating response...",
}
response, err := a.callLLM(ctx, prompt)
if err != nil {
return nil, fmt.Errorf("llm call failed: %w", err)
}
return &adapter.AgentResult{
Content: response.Text,
ToolCalls: response.ToolCalls,
TokensUsed: response.Usage.TotalTokens,
CompletedAt: time.Now(),
}, nil
}
Step 4: Validate Configuration
func (a *MyAgent) Validate(config adapter.AgentConfig) error {
if config.APIKey == "" {
return fmt.Errorf("api_key is required")
}
if config.Model == "" {
return fmt.Errorf("model is required")
}
return nil
}
Step 5: Register Your Adapter
package myadapter
import "example.com/agenthub/adapter" // illustrative placeholder
func init() {
adapter.Register("my-agent", func(config adapter.AgentConfig) (adapter.AgentAdapter, error) {
if err := (&MyAgent{}).Validate(config); err != nil {
return nil, err
}
return New(config), nil
})
}
Then import the adapter from the server entrypoint:
package main
import (
_ "github.com/your-org/agenthub-myadapter"
"github.com/tokendancelab/agenthub/cmd/server"
)
func main() {
server.Run()
}
Event Types
| Event | Meaning |
|---|---|
EventProgress | Intermediate status shown in the conversation thread. |
EventToolCall | The runtime requested a tool call such as reading a file or running a command. |
EventError | Non-fatal error; the runtime may continue. |
EventComplete | The current task completed. |
Best Practices
- Stream progress: users need to see that work is still moving.
- Respect cancellation: always honor
ctx.Done(). - Make tools idempotent: retries should be safe.
- Return structured output: JSON-shaped results are easier to render in cards and audit logs.
- Keep configuration minimal: require only necessary fields such as API key and model name.
Example: Minimal HTTP Adapter
package httpadapter
import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"example.com/agenthub/adapter" // illustrative placeholder
)
type HTTPAgent struct {
config adapter.AgentConfig
endpoint string
}
func New(config adapter.AgentConfig) *HTTPAgent {
endpoint := config.Extra["endpoint"]
if endpoint == "" {
endpoint = "https://api.example.com/v1/chat"
}
return &HTTPAgent{config: config, endpoint: endpoint}
}
func (a *HTTPAgent) Name() string { return "http-agent" }
func (a *HTTPAgent) Capabilities() []adapter.Capability {
return []adapter.Capability{{
Name: "general", Description: "General-purpose assistant.",
}}
}
func (a *HTTPAgent) Execute(
ctx context.Context,
task adapter.Task,
events chan<- adapter.AgentEvent,
) (*adapter.AgentResult, error) {
defer close(events)
events <- adapter.AgentEvent{Type: adapter.EventProgress, Message: "Calling LLM..."}
body := map[string]interface{}{
"model": a.config.Model,
"messages": []map[string]string{{"role": "user", "content": task.Prompt}},
}
buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(body)
req, _ := http.NewRequestWithContext(ctx, "POST", a.endpoint, buf)
req.Header.Set("Authorization", "Bearer "+a.config.APIKey)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
var result struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("decode failed: %w", err)
}
return &adapter.AgentResult{
Content: result.Choices[0].Message.Content,
CompletedAt: time.Now(),
}, nil
}
func (a *HTTPAgent) Validate(config adapter.AgentConfig) error {
if config.APIKey == "" {
return fmt.Errorf("api_key is required")
}
return nil
}
Next Steps
- Use Architecture to understand where adapters run.
- Use API And Events to keep event shapes consistent.
- Use Quickstart to validate a local runtime flow before proposing an adapter.
- Submit adapter proposals after the public SDK package and contribution flow are stable.