{"id":44860475,"url":"https://github.com/voocel/agentcore","last_synced_at":"2026-04-13T10:02:14.550Z","repository":{"id":282731818,"uuid":"949007383","full_name":"voocel/agentcore","owner":"voocel","description":"A minimal, composable Go library for building AI agent applications.","archived":false,"fork":false,"pushed_at":"2026-04-11T10:50:53.000Z","size":771,"stargazers_count":32,"open_issues_count":0,"forks_count":11,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-04-11T12:25:00.494Z","etag":null,"topics":["agent","agentcore","agents","ai","go","llm","multi-agent","multi-agent-systems","workflows"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/voocel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"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":"2025-03-15T13:18:31.000Z","updated_at":"2026-04-11T10:51:01.000Z","dependencies_parsed_at":"2025-12-31T00:06:47.018Z","dependency_job_id":null,"html_url":"https://github.com/voocel/agentcore","commit_stats":null,"previous_names":["voocel/mas","voocel/agentcore"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/voocel/agentcore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voocel%2Fagentcore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voocel%2Fagentcore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voocel%2Fagentcore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voocel%2Fagentcore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/voocel","download_url":"https://codeload.github.com/voocel/agentcore/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/voocel%2Fagentcore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31747180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T09:16:15.125Z","status":"ssl_error","status_checked_at":"2026-04-13T09:16:05.023Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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","agentcore","agents","ai","go","llm","multi-agent","multi-agent-systems","workflows"],"created_at":"2026-02-17T09:09:45.415Z","updated_at":"2026-04-13T10:02:14.545Z","avatar_url":"https://github.com/voocel.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AgentCore\n\n**AgentCore** is a minimal, composable Go library for building AI agent applications.\n\n[English](README.md) | [中文](README_CN.md)\n\n## Install\n\n```bash\ngo get github.com/voocel/agentcore\n```\n\n## Design Philosophy\n\nA restrained core with open extensibility tends to be more reliable than a complex all-in-one solution. Fewer built-ins, more possibilities.\n\n## Stability\n\n- Keep `Agent`, `AgentLoop`, `Event`, `Tool`, and `Message` stable first\n- Behavioral changes should come with tests first; `examples/` and internal implementation details are not stable API\n\n## Architecture\n\n```\nagentcore/            Agent core (types, loop, agent, events, subagent)\nagentcore/llm/        LLM adapters (OpenAI, Anthropic, Gemini via litellm)\nagentcore/tools/      Built-in tools: read, write, edit, bash\nagentcore/context/    Context runtime — projection, rewrite, overflow recovery\n```\n\nCore design:\n\n- **Standalone loop** (`loop.go`) — free function, all dependencies injected via parameters. Double loop: inner processes tool calls + steering, outer handles follow-up\n- **Stateful Agent** (`agent.go`) — sole consumer of loop events, updates internal state then dispatches to external listeners\n- **Event stream** — single `\u003c-chan Event` output drives any UI (TUI, Web, Slack, logging)\n- **Two-stage pipeline** — `TransformContext` (prune/inject) → `ConvertToLLM` (filter to LLM messages)\n- **SubAgent tool** (`subagent.go`) — multi-agent via tool invocation, four modes: single, parallel, chain, background\n- **Context runtime** (`context/`) — projection, committed rewrite, and overflow recovery near the context window limit\n\n## Quick Start\n\n### Single Agent\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"os\"\n\n    \"github.com/voocel/agentcore\"\n    \"github.com/voocel/agentcore/llm\"\n    \"github.com/voocel/agentcore/permission\"\n    \"github.com/voocel/agentcore/tools\"\n)\n\nfunc main() {\n    model, err := llm.NewOpenAIModel(\"gpt-5-mini\", os.Getenv(\"OPENAI_API_KEY\"))\n    if err != nil {\n        panic(err)\n    }\n\n    agent := agentcore.NewAgent(\n        agentcore.WithModel(model),\n        agentcore.WithSystemPrompt(\"You are a helpful coding assistant.\"),\n        agentcore.WithTools(\n            tools.NewRead(\".\"),\n            tools.NewWrite(\".\"),\n            tools.NewEdit(\".\"),\n            tools.NewBash(\".\"),\n        ),\n        agentcore.WithPermissionEngine(permission.NewEngine(permission.EngineConfig{\n            Workspace: \".\",\n            Mode:      permission.ModeBalanced,\n        })),\n    )\n\n    agent.Subscribe(func(ev agentcore.Event) {\n        if ev.Type == agentcore.EventMessageEnd {\n            if msg, ok := ev.Message.(agentcore.Message); ok \u0026\u0026 msg.Role == agentcore.RoleAssistant {\n                fmt.Println(msg.Content)\n            }\n        }\n    })\n\n    agent.Prompt(\"List the files in the current directory.\")\n    agent.WaitForIdle()\n}\n```\n\nFor stricter control, pass a custom decision engine with `agentcore.WithPermissionEngine(...)`.\n\n```go\nengine := permission.NewEngine(permission.EngineConfig{\n    Workspace: \".\",\n    Mode:      permission.ModeStrict,\n    Roots: permission.FilesystemRoots{\n        ReadRoots:  []string{\".\"},\n        WriteRoots: []string{\".\"},\n    },\n})\n```\n\n### Multi-Agent (SubAgent Tool)\n\nSub-agents are invoked as regular tools with isolated contexts:\n\n```go\nmodel, _ := llm.NewOpenAIModel(\"gpt-5-mini\", apiKey)\n\nscout := agentcore.SubAgentConfig{\n    Name:         \"scout\",\n    Description:  \"Fast codebase reconnaissance\",\n    Model:        model,\n    SystemPrompt: \"Quickly explore and report findings. Be concise.\",\n    Tools:        []agentcore.Tool{tools.NewRead(\".\"), tools.NewBash(\".\")},\n    MaxTurns:     5,\n}\n\nworker := agentcore.SubAgentConfig{\n    Name:         \"worker\",\n    Description:  \"General-purpose executor\",\n    Model:        model,\n    SystemPrompt: \"Implement tasks given to you.\",\n    Tools:        []agentcore.Tool{tools.NewRead(\".\"), tools.NewWrite(\".\"), tools.NewEdit(\".\"), tools.NewBash(\".\")},\n}\n\nagent := agentcore.NewAgent(\n    agentcore.WithModel(model),\n    agentcore.WithTools(agentcore.NewSubAgentTool(scout, worker)),\n)\n```\n\nFour execution modes via tool call:\n\n```jsonc\n// Single: one agent, one task\n{\"agent\": \"scout\", \"task\": \"Find all API endpoints\"}\n\n// Parallel: concurrent execution\n{\"tasks\": [{\"agent\": \"scout\", \"task\": \"Find auth code\"}, {\"agent\": \"scout\", \"task\": \"Find DB schema\"}]}\n\n// Chain: sequential with {previous} context passing\n{\"chain\": [{\"agent\": \"scout\", \"task\": \"Find auth code\"}, {\"agent\": \"worker\", \"task\": \"Refactor based on: {previous}\"}]}\n\n// Background: async execution, returns immediately, notifies on completion\n{\"agent\": \"worker\", \"task\": \"Run full test suite\", \"background\": true, \"description\": \"Running tests\"}\n```\n\n### Steering \u0026 Follow-Up\n\n```go\n// Interrupt mid-run (delivered after current tool, remaining tools skipped)\nagent.Steer(agentcore.UserMsg(\"Stop and focus on tests instead.\"))\n\n// Queue for after the agent finishes\nagent.FollowUp(agentcore.UserMsg(\"Now run the tests.\"))\n\n// Cancel immediately\nagent.Abort()\n```\n\n### Event Stream\n\nAll lifecycle events flow through a single channel — subscribe to drive any UI:\n\n```go\nagent.Subscribe(func(ev agentcore.Event) {\n    switch ev.Type {\n    case agentcore.EventMessageStart:    // assistant starts streaming\n    case agentcore.EventMessageUpdate:   // streaming token delta\n    case agentcore.EventMessageEnd:      // message complete\n    case agentcore.EventToolExecStart:   // tool execution begins\n    case agentcore.EventToolExecEnd:     // tool execution ends\n    case agentcore.EventError:           // error occurred\n    }\n})\n```\n\n### Structured Tool Progress\n\nLong-running tools can emit structured progress updates instead of ad-hoc JSON:\n\n```go\nagentcore.ReportToolProgress(ctx, agentcore.ProgressPayload{\n    Kind:    agentcore.ProgressSummary,\n    Agent:   \"worker\",\n    Tool:    \"bash\",\n    Summary: \"worker → bash\",\n})\n```\n\nSubscribers should read `ev.Progress` directly for tool progress updates:\n\n```go\nagent.Subscribe(func(ev agentcore.Event) {\n    if ev.Type == agentcore.EventToolExecUpdate \u0026\u0026 ev.Progress != nil {\n        fmt.Printf(\"[%s] %s\\n\", ev.Progress.Kind, ev.Progress.Summary)\n    }\n})\n```\n\n### Swappable Models\n\nWhen a model needs to change at runtime, wrap it with `SwappableModel`. The swap takes effect on the next call. `SubAgentConfig.Model` is resolved at the start of each sub-agent run, so the same wrapper also works for sub-agents.\n\n```go\ndefaultModel, _ := llm.NewOpenAIModel(\"gpt-5-mini\", apiKey)\nsw := agentcore.NewSwappableModel(defaultModel)\n\nagent := agentcore.NewAgent(agentcore.WithModel(sw))\n\nnextModel, _ := llm.NewOpenAIModel(\"gpt-5\", apiKey)\nsw.Swap(nextModel) // next turn uses the new model\n```\n\n### Custom LLM (StreamFn)\n\nSwap the LLM call with a proxy, mock, or custom implementation:\n\n```go\nagent := agentcore.NewAgent(\n    agentcore.WithStreamFn(func(ctx context.Context, req *agentcore.LLMRequest) (*agentcore.LLMResponse, error) {\n        // Route to your own proxy/gateway\n        return callMyProxy(ctx, req)\n    }),\n)\n```\n\n### Context Compaction\n\nAuto-summarize conversation history when approaching the context window limit. Use the built-in context manager:\n\n```go\nimport (\n    \"github.com/voocel/agentcore\"\n    agentctx \"github.com/voocel/agentcore/context\"\n)\n\nengine := agentctx.NewDefaultEngine(model, 128000)\n\nagent := agentcore.NewAgent(\n    agentcore.WithModel(model),\n    agentcore.WithContextManager(engine),\n)\n```\n\n`NewAgent` auto-wires `ConvertToLLM`, token estimation, and context window from the context manager when available.\n\nOn each LLM call, the context manager first builds a projected prompt view for the next model request. When a rewrite should become the new runtime baseline, it can return `ShouldCommit=true` with `CommitMessages`, and the loop will replace the in-memory baseline before continuing.\n\nWhen usage exceeds `ContextWindow - ReserveTokens` (default 16384), compaction:\n\n1. Keeps recent messages (default 20000 tokens)\n2. Summarizes older messages via LLM into a structured checkpoint (Goal / Progress / Key Decisions / Next Steps)\n3. Tracks file operations (read/write/edit paths) across compacted messages\n4. Supports incremental updates — subsequent compactions update the existing summary rather than re-summarizing\n\n### Context Pipeline\n\nFor simpler transform-only pipelines, `WithContextPipeline` / `WithTransformContext` still work:\n\n```go\nagent := agentcore.NewAgent(\n    // Stage 1: prune old messages, inject external context\n    agentcore.WithTransformContext(func(ctx context.Context, msgs []agentcore.AgentMessage) ([]agentcore.AgentMessage, error) {\n        if len(msgs) \u003e 100 {\n            msgs = msgs[len(msgs)-50:]\n        }\n        return msgs, nil\n    }),\n    // Stage 2: filter to LLM-compatible messages\n    agentcore.WithConvertToLLM(func(msgs []agentcore.AgentMessage) []agentcore.Message {\n        var out []agentcore.Message\n        for _, m := range msgs {\n            if msg, ok := m.(agentcore.Message); ok {\n                out = append(out, msg)\n            }\n        }\n        return out\n    }),\n)\n```\n\n## Built-in Tools\n\n| Tool | Description |\n|------|-------------|\n| `read` | Read file contents with head truncation (2000 lines / 50KB) |\n| `write` | Write file with auto-mkdir |\n| `edit` | Exact text replacement with fuzzy match, BOM/line-ending normalization, unified diff output |\n| `bash` | Execute shell commands with tail truncation (2000 lines / 50KB) |\n\n## Runtime Injection\n\nUse `Inject(msg)` when the caller's intent is \"deliver this as soon as the current\nagent state allows\" without manually branching on running vs idle state.\n\n```go\nresult, err := agent.Inject(agentcore.UserMsg(\"Re-check unfinished tasks before stopping.\"))\nif err != nil {\n    panic(err)\n}\nfmt.Println(result.Disposition)\n```\n\n`Inject` has three outcomes:\n\n- `steered_current_run`: the agent is running, so the message was queued into the current run's steering path\n- `resumed_idle_run`: the agent was idle with an assistant-tail conversation, so the message was queued and `Continue()` was started immediately\n- `queued`: the message was queued, but no run was started\n\nUse the lower-level APIs when you need stricter control:\n\n- `Steer(msg)`: queue for the steering path without any idle auto-resume logic\n- `FollowUp(msg)`: queue for after the current run stops\n- prompt-side injection: keep this in the application layer if the message must be merged into the next explicit user prompt rather than the agent queues\n\n## API Reference\n\n### Agent\n\n| Method | Description |\n|--------|-------------|\n| `NewAgent(opts...)` | Create agent with options |\n| `Prompt(input)` | Start new conversation turn |\n| `PromptMessages(msgs...)` | Start turn with arbitrary AgentMessages |\n| `Continue()` | Resume from current context |\n| `Inject(msg)` | Deliver message via steer / idle resume / queue, depending on current state |\n| `Steer(msg)` | Inject steering message mid-run |\n| `FollowUp(msg)` | Queue message for after completion |\n| `Abort()` | Cancel current execution |\n| `AbortSilent()` | Cancel without emitting abort marker |\n| `WaitForIdle()` | Block until agent finishes |\n| `Subscribe(fn)` | Register event listener |\n| `State()` | Snapshot of current state |\n| `ExportMessages()` | Export messages for serialization |\n| `ImportMessages(msgs)` | Import deserialized messages |\n\n## License\n\nApache License 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoocel%2Fagentcore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvoocel%2Fagentcore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvoocel%2Fagentcore/lists"}