{"id":47681753,"url":"https://github.com/denizumutdereli/go-deepagent","last_synced_at":"2026-04-02T14:01:53.689Z","repository":{"id":340884946,"uuid":"1155801593","full_name":"denizumutdereli/go-deepagent","owner":"denizumutdereli","description":"Deepagents for Go, the easiest way to write LLM-based programs in Go","archived":false,"fork":false,"pushed_at":"2026-02-25T12:25:24.000Z","size":232,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-27T06:04:19.410Z","etag":null,"topics":["agent-state","agents","deepagent","langchain","llms"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/denizumutdereli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-11T23:17:30.000Z","updated_at":"2026-02-25T12:25:29.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/denizumutdereli/go-deepagent","commit_stats":null,"previous_names":["denizumutdereli/go-deepagent"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/denizumutdereli/go-deepagent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizumutdereli%2Fgo-deepagent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizumutdereli%2Fgo-deepagent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizumutdereli%2Fgo-deepagent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizumutdereli%2Fgo-deepagent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/denizumutdereli","download_url":"https://codeload.github.com/denizumutdereli/go-deepagent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/denizumutdereli%2Fgo-deepagent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31307462,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-state","agents","deepagent","langchain","llms"],"created_at":"2026-04-02T14:01:29.984Z","updated_at":"2026-04-02T14:01:53.669Z","avatar_url":"https://github.com/denizumutdereli.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-deepagent\n\nA production-grade, recursive multi-agent LLM framework for Go. Build hierarchical AI agent systems with typed state management, concurrent execution, human-in-the-loop controls, and full observability — all with a single recursive config struct.\n\nBuilt on top of [langchaingo](https://github.com/tmc/langchaingo).\n\n[![Go Reference](https://pkg.go.dev/badge/github.com/denizumutdereli/go-deepagent.svg)](https://pkg.go.dev/github.com/denizumutdereli/go-deepagent)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n## Features\n\n### Core\n- **Recursive Agent Hierarchy** — Orchestrator delegates to sub-agents, which can have their own sub-agents. Same config struct at every level.\n- **Multi-Provider LLM Support** — OpenAI, Anthropic (Claude), Google (Gemini), XAI (Grok), Groq, Ollama. Mix providers across agents.\n- **ReAct Execution Loop** — Agents reason, act (call tools), observe results, and iterate until they reach an answer.\n- **Parallel Tool Execution** — Multiple tool calls execute concurrently with goroutines and proper synchronization.\n- **Built-in VFS Tools** — Every agent gets `ls`, `read_file`, `write_file`, `edit_file`, `grep`, `glob` out of the box.\n- **Skills System** — Load domain knowledge from Markdown files (with YAML frontmatter) and inject into agent prompts.\n- **Custom Tools** — Implement the standard `langchaingo/tools.Tool` interface. Agents discover and call them automatically.\n\n### State Management\n- **Typed Agent State** — Structured state container with concurrency-safe reducers (messages: append-only, files: merge-by-path, todos: last-writer-wins, skills metadata: merge-by-name, custom: deep merge).\n- **Thread Lifecycle** — Full thread lifecycle management with status derivation (idle → busy → interrupted → error), busy locks, and self-healing for orphaned runs.\n- **Multitask Strategies** — `reject`, `rollback`, `interrupt`, `enqueue` — controls behavior when a new run is submitted while a thread is busy.\n- **WAL Checkpointing** — Write-ahead log checkpoints with parent chains, pending writes, interrupt state, and time-travel debugging via state history.\n- **Thread Fork** — Clone a thread with all its messages and checkpoints to a new thread ID.\n\n### Middleware Ecosystem\n- **Human-in-the-Loop (HITL)** — Interrupt agent execution before or after specific tool calls for human approval. Per-tool or wildcard interrupt configs.\n- **Model Call Limit** — Configurable per-run LLM call limit with graceful end or throw behavior.\n- **Memory Persistence** — Load AGENTS.md memory files into system prompts with hot-reload support.\n- **Progressive Skills Disclosure** — Only inject skill names and descriptions; agents read full instructions on-demand via `read_file`.\n- **Model Retry** — Exponential backoff retry for failed LLM calls (configurable max retries, backoff multiplier).\n- **Model Fallback** — Automatic fallback to cheaper models on primary model failure (lazy-initialized fallback chain).\n- **Tool Retry** — Retry failed tool calls with configurable strategy.\n- **Tool Result Eviction** — Evict large tool outputs to VFS backend, replace with head/tail preview to stay within context limits.\n- **Patch Tool Calls** — Automatically fix dangling tool calls (AI messages with tool_calls but no corresponding ToolMessage).\n- **Token Usage Tracking** — Per-run token usage accumulation across all LLM calls, thread-safe for concurrent subagents.\n- **Auth Middleware** — Multi-tenant authentication and authorization with pluggable providers (API key, custom). Per-tool action whitelisting.\n- **Tracing** — OpenTelemetry-compatible span instrumentation for agent invocations and tool calls. Pluggable backends (in-memory, noop, or custom).\n\n### Infrastructure\n- **SSE Streaming** — Server-Sent Events with per-run event streams, multiple concurrent subscribers, resumability via monotonic event IDs, and keepalive.\n- **Webhook Support** — Async webhook delivery on run completion/failure with retry, exponential backoff, event type filtering, and runtime registration.\n- **Agent Protocol HTTP Server** — REST API with SSE streaming, background runs, cancellation. LangGraph Studio compatible.\n- **Pluggable Storage** — In-memory (default), MongoDB, Redis (ephemeral runs), or hybrid (MongoDB + Redis). Implement the `Store` interface for your own backend.\n- **Cross-Thread VFS** — Persistent key-value backed virtual filesystem (`afero.Fs` interface) for cross-conversation file storage with namespace isolation.\n- **General-Purpose Subagent** — Convenience factory for creating general-purpose task delegation subagents.\n- **Concurrent Spawn Limits** — Configurable semaphore-based limit on concurrent subagent spawns.\n\n## Installation\n\n```bash\ngo get github.com/denizumutdereli/go-deepagent\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"os\"\n\n    \"github.com/denizumutdereli/go-deepagent/pkg/agent\"\n)\n\nfunc main() {\n    app, err := agent.New(agent.AgentConfig{\n        Name:   \"assistant\",\n        Model:  \"gpt-4.1\",\n        Prompt: \"You are a helpful assistant.\",\n    }, os.Getenv(\"OPENAI_API_KEY\"))\n    if err != nil {\n        panic(err)\n    }\n\n    result, err := app.Process(context.Background(), \"What is the capital of France?\")\n    if err != nil {\n        panic(err)\n    }\n    fmt.Println(result)\n}\n```\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────┐\n│                        Your Application                         │\n├─────────────────────────────────────────────────────────────────┤\n│  pkg/agent            │  pkg/server      │  pkg/store           │\n│  ─────────────────    │  ─────────────   │  ─────────────────   │\n│  App                  │  HTTP Server     │  MemoryStore         │\n│  ReactExecutor        │  SSE Streaming   │  MongoStore          │\n│  AgentState + Reducers│  Background Runs │  RedisStore          │\n│  ThreadManager        │  Cancellation    │  HybridStore         │\n│  CheckpointManager    │                  │                      │\n│  StreamManager        │  pkg/protocol    │                      │\n│  WebhookManager       │  ─────────────   │                      │\n│  MiddlewareChain      │  Thread, Run     │                      │\n│  StoreBackend (VFS)   │  Checkpoint      │                      │\n│  Tracing              │  Message         │                      │\n│  Auth                 │  StoreItem       │                      │\n└─────────────────────────────────────────────────────────────────┘\n```\n\n### Package Overview\n\n| Package | Description |\n|---------|-------------|\n| `pkg/agent` | Core agent engine — ReAct loop, typed state, thread lifecycle, middleware, streaming, webhooks, auth, tracing |\n| `pkg/server` | Agent Protocol HTTP server with SSE streaming, background runs, cancellation |\n| `pkg/store` | Storage backends — in-memory, MongoDB, Redis (ephemeral), hybrid (MongoDB + Redis) |\n| `pkg/protocol` | Shared types — Thread, Run, Checkpoint, Message, StoreItem |\n\n## Agent Configuration\n\nThe entire system is configured with a single recursive struct:\n\n```go\ntype AgentConfig struct {\n    // Identity\n    Name        string // unique name for this agent\n    Description string // shown to parent agent for routing\n\n    // Prompt (required)\n    Prompt string\n\n    // Model — supports \"provider:model\" format\n    Provider    string  // \"openai\", \"anthropic\", \"google\", \"xai\", \"groq\", \"ollama\"\n    Model       string  // \"gpt-4.1\", \"anthropic:claude-sonnet-4-20250514\", \"xai:grok-4-1-fast-reasoning\"\n    APIKey      string  // falls back to env vars if empty\n    BaseURL     string  // custom API endpoint\n    Temperature float64 // 0.0 - 1.0\n\n    // Capabilities\n    Tools   []tools.Tool    // langchaingo tool interface\n    Skills  []Skill         // domain knowledge definitions\n    MaxIter int             // max ReAct iterations (default: 25)\n\n    // Recursive — sub-agents use the SAME struct\n    SubAgents []AgentConfig\n\n    // Infrastructure\n    Middleware []Middleware\n    Backend    Backend       // VFS backend\n    Store      Store         // conversation storage\n}\n```\n\n### Model Format\n\nSpecify providers explicitly or use the `\"provider:model\"` shorthand:\n\n```go\n// Auto-detected as OpenAI\nModel: \"gpt-4.1\"\n\n// Explicit provider\nModel: \"anthropic:claude-sonnet-4-20250514\"\n\n// XAI Grok\nModel: \"xai:grok-4-1-fast-reasoning\"\n\n// Google Gemini\nModel: \"google:gemini-2.5-flash\"\n\n// Ollama (local)\nModel:   \"ollama:llama3\",\nBaseURL: \"http://localhost:11434\",\n```\n\n### Environment Variables\n\nAPI keys are resolved in this order:\n1. `AgentConfig.APIKey` field\n2. Fallback API key passed to `agent.New()`\n3. Environment variables: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY`, `XAI_API_KEY`, `GROQ_API_KEY`\n\n## Multi-Agent Systems\n\nBuild hierarchical agent systems where the orchestrator automatically routes tasks to specialized sub-agents:\n\n```go\napp, err := agent.New(agent.AgentConfig{\n    Name:   \"orchestrator\",\n    Model:  \"gpt-4.1\",\n    Prompt: \"Route tasks to the best sub-agent.\",\n    SubAgents: []agent.AgentConfig{\n        {\n            Name:        \"researcher\",\n            Description: \"Searches the web for information\",\n            Model:       \"xai:grok-4-1-fast-reasoning\",\n            APIKey:      xaiKey,\n            Tools:       []tools.Tool{searchTool},\n            Prompt:      \"You are a web researcher...\",\n        },\n        {\n            Name:        \"coder\",\n            Description: \"Writes and analyzes code\",\n            Model:       \"anthropic:claude-sonnet-4-20250514\",\n            APIKey:      anthropicKey,\n            Prompt:      \"You are a software engineer...\",\n        },\n        {\n            Name:        \"casual\",\n            Description: \"Handles casual conversation\",\n            Model:       \"gpt-4.1-mini\",\n            Prompt:      \"You are a friendly assistant...\",\n        },\n    },\n}, openaiKey)\n```\n\nThe orchestrator automatically gets a `task` tool that delegates to sub-agents based on their descriptions.\n\n## Streaming Events\n\nGet real-time visibility into agent execution:\n\n```go\neventCh := make(chan agent.ReactEvent, 100)\n\ngo func() {\n    for evt := range eventCh {\n        switch evt.Type {\n        case agent.EventIterationStart:\n            fmt.Printf(\"⟳ [%s] iteration %d\\n\", evt.Agent, evt.Iteration)\n        case agent.EventLLMResponse:\n            fmt.Printf(\"💭 [%s] %s\\n\", evt.Agent, evt.Content)\n        case agent.EventToolStart:\n            fmt.Printf(\"🔧 [%s] calling %s\\n\", evt.Agent, evt.ToolName)\n        case agent.EventToolEnd:\n            fmt.Printf(\"✓ [%s] %s done\\n\", evt.Agent, evt.ToolName)\n        case agent.EventFinalAnswer:\n            fmt.Printf(\"✅ [%s] %s\\n\", evt.Agent, evt.Content)\n        }\n    }\n}()\n\nresult, err := app.SendWithEvents(ctx, threadID, \"Analyze this data\", eventCh)\nclose(eventCh)\n```\n\n### Event Types\n\n| Event | Description |\n|-------|-------------|\n| `EventIterationStart` | New ReAct iteration beginning |\n| `EventLLMResponse` | LLM thinking/reasoning output (before tool calls) |\n| `EventToolStart` | Tool invocation starting |\n| `EventToolEnd` | Tool invocation completed with result |\n| `EventFinalAnswer` | Agent has reached its final answer |\n\n## Thread-based Conversations\n\nMaintain conversation history across multiple interactions:\n\n```go\n// Create a thread\nthreadID, err := app.CreateThread(ctx, agent.ThreadConfig{\n    UserID: \"user-123\",\n})\n\n// Send messages — history is managed automatically\nresult1, _ := app.Send(ctx, threadID, \"What is Go?\")\nresult2, _ := app.Send(ctx, threadID, \"How does it handle concurrency?\") // remembers context\n\n// Read thread history\nthread, _ := app.GetThread(ctx, threadID)\nfor _, msg := range thread.Messages {\n    fmt.Printf(\"[%s] %s\\n\", msg.Role, msg.Content)\n}\n\n// List checkpoints (snapshots after each interaction)\ncheckpoints, _ := app.ListCheckpoints(ctx, threadID, 10)\n\n// List all threads\nthreads, _ := app.ListThreads(ctx, \"user-123\", 20, 0)\n```\n\n### Thread Lifecycle\n\nThreads have four states: `idle`, `busy`, `interrupted`, `error`. Status is derived automatically from checkpoint and run state.\n\n```go\ntm := agent.NewThreadManager(store)\n\n// Submit with multitask strategy\nrun, err := tm.SubmitRun(ctx, threadID, \"assistant\", \"Hello\",\n    agent.MultitaskReject,  // or: MultitaskRollback, MultitaskInterrupt, MultitaskEnqueue\n    nil,\n)\n\n// Lock/unlock for execution\nrunCtx, attempt, err := tm.LockRun(ctx, threadID, run.RunID)\ndefer tm.UnlockRun(threadID, run.RunID)\n\n// Cancel a running run\ntm.CancelRun(ctx, threadID, run.RunID, agent.CancelActionInterrupt)\n\n// Fork a thread (copy messages + checkpoints)\nnewThreadID, err := tm.ForkThread(ctx, threadID)\n\n// Derive thread status from checkpoint state\nstatus, _ := tm.DeriveThreadStatus(ctx, threadID, nil)\n// idle | busy | interrupted | error\n```\n\n### WAL Checkpointing\n\nWrite-ahead log checkpoints enable interrupt/resume and time-travel debugging:\n\n```go\ncm := agent.NewCheckpointManager(store)\nbuilder := cm.NewCheckpointBuilder(threadID, state)\n\n// Track pending writes\nbuilder.AddWrite(\"messages\", newMessage, \"task-1\")\nbuilder.AddWrite(\"files\", fileUpdate, \"task-1\")\n\n// Commit checkpoint with parent chain\ncpID, err := builder.Commit(ctx, map[string]interface{}{\"step\": \"tool_call\"})\n\n// Commit interrupt (for HITL)\ncpID, err := builder.CommitInterrupt(ctx, []string{\"write_file\"}, interruptErr)\n\n// Restore state from latest checkpoint\nstate, checkpoint, err := cm.RestoreState(ctx, threadID)\n\n// Time-travel: browse state history\nhistory, err := cm.GetStateHistory(ctx, threadID, 10)\n```\n\n## SSE Stream Manager\n\nPer-run event streaming with pub/sub, resumable event IDs, and concurrent subscribers:\n\n```go\nsm := agent.NewStreamManager()\nsm.CreateStream(runID)\n\n// Publish events (thread-safe, non-blocking for slow subscribers)\nsm.Publish(runID, agent.StreamEventMessages, messageData)\nsm.Publish(runID, agent.StreamEventValues, stateSnapshot)\n\n// Subscribe with resumability (Last-Event-ID reconnection)\nctx, cancel := context.WithCancel(context.Background())\nch, err := sm.Subscribe(ctx, runID, lastEventID)\nfor evt := range ch {\n    sse, _ := evt.MarshalSSE()\n    w.Write([]byte(sse))\n    w.(http.Flusher).Flush()\n}\n\n// End stream + cleanup old streams\nsm.EndStream(runID)\nsm.Cleanup(2 * time.Hour)\n```\n\n## Webhooks\n\nAsync webhook delivery on run completion with retry and backoff:\n\n```go\nwm := agent.NewWebhookManager(\n    agent.WebhookConfig{\n        URL:        \"https://api.example.com/hooks\",\n        MaxRetries: 3,\n        Timeout:    10 * time.Second,\n        Events:     []string{\"run.completed\", \"run.failed\"},\n        Headers:    map[string]string{\"X-API-Key\": \"secret\"},\n    },\n)\ndefer wm.Close()\n\n// Add webhooks at runtime\nwm.AddWebhook(agent.WebhookConfig{URL: \"https://slack.example.com/webhook\"})\n\n// Emit events (non-blocking, async delivery via worker goroutine)\nwm.Emit(agent.WebhookEvent{\n    Type:     \"run.completed\",\n    RunID:    runID,\n    ThreadID: threadID,\n    Status:   \"success\",\n})\n```\n\n## Authentication\n\nMulti-tenant auth with pluggable providers:\n\n```go\n// API key provider\nprovider := agent.NewAPIKeyAuthProvider(map[string]*agent.AuthContext{\n    \"key-123\": {UserID: \"user-1\", OrgID: \"org-1\", Permissions: []string{\"*\"}},\n    \"key-456\": {UserID: \"user-2\", OrgID: \"org-1\", Permissions: []string{\"execute_tool:read_file\"}},\n})\n\n// Auth middleware\nmw := agent.NewAuthMiddleware(agent.AuthMiddlewareConfig{\n    Provider:       provider,\n    RequireAuth:    true,\n    AllowedActions: []string{\"read_file\", \"ls\"}, // no auth needed for read-only tools\n})\n\n// Inject auth context\nctx := agent.WithAuthContext(ctx, \u0026agent.AuthContext{\n    UserID: \"user-1\",\n    OrgID:  \"org-1\",\n})\n```\n\n## Tracing\n\nOpenTelemetry-compatible instrumentation:\n\n```go\n// In-memory tracer (testing/debugging)\ntracer := agent.NewInMemoryTracer()\nmw := agent.NewTracingMiddleware(tracer)\n\n// After execution\nspans := tracer.Spans()\nfor _, s := range spans {\n    fmt.Printf(\"%s [%s] %s → %s\\n\", s.Name, s.Kind, s.StartTime, s.Status)\n}\n\n// Noop tracer (zero overhead in production)\nmw := agent.NewTracingMiddleware(nil) // uses NoopTracer\n\n// Custom tracer (implement the Tracer interface for OpenTelemetry, Datadog, etc.)\ntype Tracer interface {\n    StartSpan(ctx context.Context, name string, kind SpanKind, attrs map[string]interface{}) (context.Context, *Span)\n    EndSpan(span *Span, status SpanStatus, err error)\n    AddEvent(span *Span, name string, attrs map[string]interface{})\n    Flush(ctx context.Context) error\n}\n```\n\n## Custom Tools\n\nImplement the standard `langchaingo/tools.Tool` interface:\n\n```go\ntype WebSearchTool struct {\n    apiKey string\n}\n\nfunc (t *WebSearchTool) Name() string        { return \"web_search\" }\nfunc (t *WebSearchTool) Description() string { return \"Search the web. Input: JSON {\\\"query\\\": \\\"search terms\\\"}\" }\n\nfunc (t *WebSearchTool) Call(ctx context.Context, input string) (string, error) {\n    var args struct {\n        Query string `json:\"query\"`\n    }\n    json.Unmarshal([]byte(input), \u0026args)\n    // ... perform search ...\n    return results, nil\n}\n```\n\n## Skills\n\nLoad domain knowledge from Markdown files with YAML frontmatter:\n\n```markdown\n---\nname: security-audit\ndescription: Smart contract security methodology\n---\n\n# Security Audit Process\n\n1. Check for reentrancy vulnerabilities\n2. Verify access control patterns\n3. Review arithmetic operations for overflow\n...\n```\n\n```go\n// Load from file\nskill, err := agent.SkillFromFile(\"skills/security-audit.md\")\n\n// Load from embedded filesystem\nskill, err := agent.SkillFromEmbed(embedFS, \"skills/security-audit.md\")\n\n// Create inline\nskill := agent.NewSkill(\"math\", \"Math helper\", \"You can solve equations...\")\n\n// Use in agent config\ncfg := agent.AgentConfig{\n    Skills: []agent.Skill{skill},\n    // ...\n}\n```\n\n## Middleware\n\nThe middleware pipeline supports `BeforeInvoke`, `AfterInvoke`, `BeforeToolCall`, `AfterToolCall`, `ModelCallInterceptor` (wraps LLM calls), and `ToolCallInterceptor` (wraps tool calls) hooks.\n\n### Built-in Middleware\n\n```go\n// Execution control\nagent.NewModelCallLimitMiddleware(agent.ModelCallLimitConfig{\n    MaxCalls:     10,\n    ExitBehavior: \"end\", // \"end\" (graceful) or \"throw\" (error)\n})\n\n// Resilience\nagent.NewModelRetryMiddleware(agent.ModelRetryConfig{\n    MaxRetries:       3,\n    InitialBackoff:   time.Second,\n    BackoffMultiplier: 2.0,\n    OnFailure:        \"continue\", // or \"throw\"\n})\nagent.NewModelFallbackMiddleware(agent.ModelFallbackConfig{\n    FallbackModels: []string{\"openai:gpt-4.1-mini\"},\n})\nagent.NewToolRetryMiddleware(agent.ToolRetryConfig{MaxRetries: 3})\n\n// Human-in-the-Loop\nagent.NewHITLMiddleware(agent.HITLConfig{\n    Interrupts: map[string]agent.InterruptConfig{\n        \"write_file\": {Before: true},            // require approval before writes\n        \"*\":          {After: true, Reason: \"audit\"}, // audit all tool results\n    },\n})\n\n// Memory \u0026 context\nagent.NewMemoryMiddleware(agent.MemoryMiddlewareConfig{\n    Sources: []string{\"~/.deepagents/AGENTS.md\", \"./.deepagents/AGENTS.md\"},\n})\nagent.NewToolResultEvictionMiddleware(agent.ToolEvictionConfig{\n    MaxResultTokens: 4000,\n})\nagent.NewPatchToolCallsMiddleware() // fix dangling tool calls\n\n// Observability\nagent.NewTokenUsageMiddleware(tracker, \"gpt-4.1\")\nagent.NewTracingMiddleware(tracer) // OpenTelemetry-compatible\nagent.NewAuthMiddleware(agent.AuthMiddlewareConfig{\n    Provider:    apiKeyProvider,\n    RequireAuth: true,\n})\n\n// Existing\nagent.LoggingMiddleware()\nagent.AnthropicSanitizeMiddleware()\nagent.TodoListMiddleware()\nagent.SummarizationMiddleware(cfg)\n```\n\n### Custom Middleware\n\n```go\ntype MyMiddleware struct{}\n\nfunc (m *MyMiddleware) Name() string { return \"MyMiddleware\" }\n\nfunc (m *MyMiddleware) BeforeInvoke(ctx context.Context, ic *agent.InvokeContext) error {\n    fmt.Printf(\"Agent %s, iteration %d\\n\", ic.AgentName, ic.Iteration)\n    return nil\n}\n\nfunc (m *MyMiddleware) BeforeToolCall(ctx context.Context, tc *agent.ToolCallContext) error {\n    fmt.Printf(\"Tool: %s\\n\", tc.ToolName)\n    return nil\n}\n```\n\n## HTTP Server (Agent Protocol)\n\nStart a production-ready HTTP server compatible with [LangGraph Studio](https://github.com/langchain-ai/langgraph-studio):\n\n```go\nimport \"github.com/denizumutdereli/go-deepagent/pkg/server\"\n\nsrv, err := server.New(server.ServerConfig{\n    App:  app,\n    Port: \"8080\",\n    Runner: server.RunnerConfig{\n        MaxConcurrent:   50,\n        RunTimeout:      5 * time.Minute,\n        ShutdownTimeout: 30 * time.Second,\n    },\n})\n\nsrv.Start()\n```\n\n### Endpoints\n\n```\nGET  /api/health                          Health check\n\nPOST /threads                             Create thread\nPOST /threads/search                      Search threads\nGET  /threads/{id}                        Get thread + messages\nDELETE /threads/{id}                      Delete thread\nGET  /threads/{id}/history                Checkpoint history\n\nPOST /threads/{id}/runs                   Background run\nPOST /threads/{id}/runs/stream            Run + SSE stream\nPOST /threads/{id}/runs/wait              Run + wait for result\n\nPOST /runs                                Stateless background run\nPOST /runs/stream                         Stateless run + SSE stream\nPOST /runs/wait                           Stateless run + wait\n\nGET  /runs/{id}                           Get run status\nGET  /runs/{id}/stream                    Reconnect to SSE stream\nGET  /runs/{id}/wait                      Wait for completion\nPOST /runs/{id}/cancel                    Cancel run\n```\n\n### Custom Router \u0026 Middleware\n\n```go\nr := chi.NewRouter()\nr.Use(myAuthMiddleware)\nr.Get(\"/custom\", myHandler)\n\nsrv, _ := server.New(server.ServerConfig{\n    App:    app,\n    Router: r, // Agent Protocol routes are mounted on your router\n})\n```\n\n### SSE Event Formatting\n\n```go\nsrv, _ := server.New(server.ServerConfig{\n    App: app,\n    SSEFormatter: func(evt agent.ReactEvent) (string, []byte) {\n        // Custom SSE event formatting\n        return string(evt.Type), json.Marshal(evt)\n    },\n})\n```\n\n## Storage\n\n### In-Memory (Default)\n\n```go\n// Automatically used when no Store is provided\napp, _ := agent.New(cfg, apiKey)\n```\n\n### MongoDB\n\n```go\nimport \"github.com/denizumutdereli/go-deepagent/pkg/store\"\n\nms, err := store.ConnectMongo(ctx, \"mongodb://localhost:27017\", \"mydb\")\napp, _ := agent.New(agent.AgentConfig{\n    Store: ms,\n    // ...\n}, apiKey)\n```\n\n### Redis (Ephemeral Runs)\n\n```go\nrs, err := store.NewRedisStore(store.RedisConfig{\n    Addr:   \"localhost:6379\",\n    RunTTL: 2 * time.Hour,   // automatic cleanup\n})\n```\n\n### Hybrid (MongoDB + Redis)\n\nProduction pattern: persistent threads/checkpoints in MongoDB, ephemeral runs in Redis with TTL-based cleanup.\n\n```go\nms, _ := store.ConnectMongo(ctx, \"mongodb://localhost:27017\", \"mydb\")\nrs, _ := store.NewRedisStore(store.DefaultRedisConfig())\nhybrid := store.NewHybridStore(ms, rs)\n\napp, _ := agent.New(agent.AgentConfig{\n    Store: hybrid,\n    // ...\n}, apiKey)\n```\n\n### Custom Store\n\nImplement the `agent.Store` interface:\n\n```go\ntype Store interface {\n    // Threads\n    CreateThread(ctx context.Context, t *protocol.Thread) error\n    GetThread(ctx context.Context, id string) (*protocol.Thread, error)\n    DeleteThread(ctx context.Context, id string) error\n    SetThreadStatus(ctx context.Context, id string, status protocol.ThreadStatus) error\n    AppendMessage(ctx context.Context, threadID string, msg protocol.Message) error\n    SearchThreads(ctx context.Context, userID string, limit, offset int) ([]*protocol.Thread, error)\n\n    // Checkpoints\n    SaveCheckpoint(ctx context.Context, cp *protocol.Checkpoint) error\n    GetLatestCheckpoint(ctx context.Context, threadID string) (*protocol.Checkpoint, error)\n    ListCheckpoints(ctx context.Context, threadID string, limit int, before string) ([]*protocol.Checkpoint, error)\n\n    // Runs\n    CreateRun(ctx context.Context, r *protocol.Run) error\n    GetRun(ctx context.Context, id string) (*protocol.Run, error)\n    UpdateRun(ctx context.Context, r *protocol.Run) error\n    ListRunsByThread(ctx context.Context, threadID string, limit, offset int) ([]*protocol.Run, error)\n}\n```\n\n## Examples\n\n### Researcher\n\nAn interactive CLI with X/Twitter research (via XAI Grok), web search (via Tavily), and casual chat — plus an `--serve` mode for HTTP API.\n\n```bash\ncd examples/researcher\ncp .env.example .env   # fill in your keys\ngo run .\n```\n\nFeatures:\n- 3 sub-agents: researcher (XAI), websearch (Tavily), casual (GPT)\n- Thread management: `new`, `thread`, `threads`, `checkpoints`\n- Server mode: `go run . --serve 8080`\n- MongoDB support: `go run . --store mongodb://localhost:27017/researcher`\n\n### On-Chain Auditor\n\nA blockchain security auditor with real on-chain tools — Etherscan API, Alchemy RPC, ABI decoding — across multiple chains.\n\n```bash\ncd examples/onchain-auditor\ncp .env.example .env   # fill in your keys\ngo run .\n```\n\nFeatures:\n- 4 sub-agents: security-auditor (Gemini), token-analyst (GPT), tx-investigator (GPT), chain-scanner (XAI)\n- Multi-chain support: Ethereum, Polygon, BSC, Arbitrum, Optimism, Base, Avalanche\n- Real on-chain tools: contract source, token transfers, balances, event logs, ABI decoding\n- Security audit skills: SWC attack vectors, DeFi patterns, audit methodology\n\n## Built-in VFS Tools\n\nEvery agent automatically receives these filesystem tools:\n\n| Tool | Description |\n|------|-------------|\n| `ls` | List directory contents |\n| `read_file` | Read file contents |\n| `write_file` | Write content to a file |\n| `edit_file` | Find-and-replace edit |\n| `grep` | Search file contents |\n| `glob` | Find files by pattern |\n\nThe VFS backend is pluggable — default is in-memory (`afero.MemMapFs`), but you can use OS filesystem or any `afero.Fs` implementation.\n\n## Testing\n\n```bash\n# Run all tests\ngo test ./...\n\n# Run with verbose output\ngo test -v ./pkg/agent/...\n\n# Run specific test suites\ngo test -v ./pkg/agent/ -run TestStreamManager\ngo test -v ./pkg/agent/ -run TestWebhookManager\ngo test -v ./pkg/agent/ -run TestAuthMiddleware\ngo test -v ./pkg/agent/ -run TestTracingMiddleware\n\n# E2E tests (require API keys in .env.test)\ncp .env.example .env.test\ngo test -v ./pkg/agent/ -run TestE2E\n```\n\nTest coverage includes:\n- **State reducers** — append-only messages, merge-by-path files, skills metadata merge, subagent clone/merge\n- **Thread lifecycle** — lock/unlock, multitask strategies, retry counters, status derivation, fork\n- **Middleware** — HITL interrupts, model call limits, memory loading, model retry/fallback, tool eviction, patch tool calls, auth, tracing\n- **Streaming** — publish/subscribe, resumability, concurrent access, context cancellation, cleanup\n- **Token usage** — concurrent accumulation, read/write safety\n- **Webhooks** — delivery, retry, event filtering, runtime registration\n- **Store backends** — StoreBackend (VFS), MemoryStore operations\n\n## Docker\n\n```bash\n# Start test dependencies (Redis + MongoDB)\ndocker-compose -f docker-compose.test.yml up -d\n\n# Run integration tests\ngo test ./... -count=1\n\n# Stop\ndocker-compose -f docker-compose.test.yml down\n```\n\n```bash\n# Start MongoDB only (for persistent storage in dev)\ndocker-compose up -d\n\n# Stop\ndocker-compose down\n```\n\n## Concurrency Design\n\ngo-deepagent is designed for high-concurrency production use:\n\n- **`sync.RWMutex`** throughout — read-heavy data structures use RLock for concurrent reads (AgentState, TokenUsage, MemoryMiddleware, AuthProvider, StreamManager)\n- **Lock ordering** — documented and enforced to prevent deadlocks (snapshot-then-apply pattern in state merges, 2-phase cleanup in stream manager)\n- **`sync/atomic`** — lock-free counters for event IDs\n- **Buffered channels** — non-blocking event delivery in StreamManager and WebhookManager\n- **Worker goroutines** — webhook delivery uses a single worker goroutine to prevent HTTP connection storms\n- **Semaphore pattern** — buffered channel limits concurrent subagent spawns\n- **Context propagation** — all long-running operations respect `context.Context` for cancellation\n\n## Contributing\n\nContributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizumutdereli%2Fgo-deepagent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdenizumutdereli%2Fgo-deepagent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdenizumutdereli%2Fgo-deepagent/lists"}