Documentation

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

AreaCurrent stabilityNotes
Runtime purposeStable directionAdapters normalize real coding runtimes into AgentHub runs and events
Event categoriesContract shapedProgress, tool call, tool result, diff, artifact, approval, completion, and failure are expected
Import pathNot stableexample.com/agenthub/adapter is an illustrative placeholder
Public packageNot stableThird-party integration instructions are not yet generally available
Submission flowNot definedThird-party review, signing, compatibility tests, and docs requirements still need product work
Security policyRequiredAdapters 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

Go
// 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

Go
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

Go
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.

Go
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

Go
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

Go
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:

Go
package main

import (
    _ "github.com/your-org/agenthub-myadapter"
    "github.com/tokendancelab/agenthub/cmd/server"
)

func main() {
    server.Run()
}

Event Types

EventMeaning
EventProgressIntermediate status shown in the conversation thread.
EventToolCallThe runtime requested a tool call such as reading a file or running a command.
EventErrorNon-fatal error; the runtime may continue.
EventCompleteThe 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

Go
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.