{"id":47607494,"url":"https://github.com/danieliser/agentruntime","last_synced_at":"2026-04-01T19:37:07.256Z","repository":{"id":344794774,"uuid":"1183123067","full_name":"danieliser/agentruntime","owner":"danieliser","description":"Spawn, stream, and steer AI agents across execution runtimes","archived":false,"fork":false,"pushed_at":"2026-03-23T04:04:37.000Z","size":25872,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-24T00:11:15.754Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/danieliser.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":"2026-03-16T09:46:51.000Z","updated_at":"2026-03-23T04:04:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/danieliser/agentruntime","commit_stats":null,"previous_names":["danieliser/agentruntime"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/danieliser/agentruntime","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieliser%2Fagentruntime","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieliser%2Fagentruntime/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieliser%2Fagentruntime/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieliser%2Fagentruntime/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danieliser","download_url":"https://codeload.github.com/danieliser/agentruntime/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danieliser%2Fagentruntime/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31291159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-01T13:12:26.723Z","status":"ssl_error","status_checked_at":"2026-04-01T13:12:25.102Z","response_time":53,"last_error":"SSL_read: 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":[],"created_at":"2026-04-01T19:37:05.843Z","updated_at":"2026-04-01T19:37:07.245Z","avatar_url":"https://github.com/danieliser.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# agentruntime\n\nagentruntime is a Go daemon and library for running coding agents behind one consistent API. Today that means `agentd` creates and tracks sessions, the runtime launches a v2 `agentruntime-sidecar`, and the sidecar talks to Claude Code or Codex, normalizes their output into a shared event stream, and feeds that stream back through replay buffers and persistent NDJSON logs. The same control plane works locally on the host or inside Docker containers, with Docker adding config materialization and a managed egress proxy.\n\n## Architecture\n\n```text\nclient\n  -\u003e POST /sessions on agentd\n  -\u003e GET /ws/sessions/:id or GET /sessions/:id/logs\n\nagentd\n  -\u003e session manager + replay buffer + NDJSON log writer\n  -\u003e runtime: local or docker\n\nruntime\n  -\u003e launches agentruntime-sidecar\n  -\u003e local: host process\n  -\u003e docker: agentruntime-agent container on managed network + squid proxy\n\nagentruntime-sidecar\n  -\u003e starts Claude Code or Codex\n  -\u003e speaks /ws using prompt|interrupt|steer|context|mention commands\n  -\u003e emits normalized events: agent_message|tool_use|tool_result|result|progress|system|error|exit\n\nagent CLI\n  -\u003e raw CLI output\n  -\u003e normalized by sidecar\n```\n\n## Installation\n\n### Via pip (no Go required)\n\n```bash\npip install agentruntime-agentd\n```\n\nThis installs the pre-built `agentd` binary for your platform. After installation, `agentd` is available on your PATH:\n\n```bash\nagentd --port 8090 --runtime local\n```\n\nFor programmatic use:\n\n```python\nfrom agentruntime_agentd import get_binary_path\n\nbinary = get_binary_path()  # absolute path to the agentd binary\n```\n\n### From source\n\n## Quick Start\n\nThe default `local` runtime needs both binaries: `agentd` and `agentruntime-sidecar`.\n\n```bash\ngo build -o agentd ./cmd/agentd\ngo build -o agentruntime-sidecar ./cmd/sidecar\n```\n\nRun the daemon with the sidecar binary on `PATH`:\n\n```bash\nPATH=\"$PWD:$PATH\" ./agentd --port 8090 --runtime local\n```\n\nCreate a prompt-mode session:\n\n```bash\nSESSION_JSON=$(curl -sS http://127.0.0.1:8090/sessions \\\n  -H 'content-type: application/json' \\\n  -d \"{\n    \\\"agent\\\": \\\"claude\\\",\n    \\\"prompt\\\": \\\"Reply with exactly hello from agentruntime.\\\",\n    \\\"work_dir\\\": \\\"$PWD\\\"\n  }\")\n\nprintf '%s\\n' \"$SESSION_JSON\" | jq .\nSESSION_ID=$(printf '%s' \"$SESSION_JSON\" | jq -r '.session_id')\n```\n\nStream output over the daemon WebSocket bridge:\n\n```bash\nwebsocat \"ws://127.0.0.1:8090/ws/sessions/$SESSION_ID?since=0\"\n```\n\nIf you prefer polling instead of WebSockets, read the NDJSON stream incrementally:\n\n```bash\ncurl -sS \"http://127.0.0.1:8090/sessions/$SESSION_ID/logs?cursor=0\"\n```\n\nOr, use the interactive `attach` command to connect to a running session with terminal I/O:\n\n```bash\nagentd attach $SESSION_ID\n```\n\nThe attach command supports:\n\n- `--port` (default 8090): Daemon port\n- `--since N` (default 0): Replay offset to start from\n- `--no-replay`: Skip replay history and only show live output\n\nStdin modes in attach:\n\n- Regular text lines are sent as stdin\n- Lines starting with `/steer ` are sent as steering commands\n- Lines starting with `/interrupt` send an interrupt signal\n- Ctrl+C sends interrupt (first time) or detaches (second time)\n\n## Docker\n\nBuild the bundled container images:\n\n```bash\n./docker/build.sh\n```\n\nThat script builds:\n\n- `agentruntime-agent:latest`\n- `agentruntime-proxy:latest`\n\nYou can also build them manually:\n\n```bash\ndocker build \\\n  --build-arg HOST_UID=\"$(id -u)\" \\\n  --build-arg HOST_GID=\"$(id -g)\" \\\n  -t agentruntime-agent:latest \\\n  -f docker/Dockerfile.agent \\\n  .\n\ndocker build \\\n  -t agentruntime-proxy:latest \\\n  -f docker/Dockerfile.proxy \\\n  docker\n```\n\nRun the daemon in Docker mode:\n\n```bash\ngo build -o agentd ./cmd/agentd\n./agentd --port 8090 --runtime docker\n```\n\nWhat happens in Docker mode:\n\n- `agentd` creates the managed Docker network `agentruntime-agents` if needed.\n- `agentd` starts the proxy sidecar container `agentruntime-proxy` if needed.\n- agent containers get `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` injected automatically.\n- the runtime starts `agentruntime-agent:latest`, which already contains `agentruntime-sidecar`, `claude`, and `codex`.\n- Claude and Codex config is materialized into per-session homes under the daemon data directory and mounted into the container.\n\nThe default Docker image is `agentruntime-agent:latest`, so a minimal Docker-backed request is still just:\n\n```bash\ncurl -sS http://127.0.0.1:8090/sessions \\\n  -H 'content-type: application/json' \\\n  -d \"{\n    \\\"agent\\\": \\\"codex\\\",\n    \\\"prompt\\\": \\\"List the top-level files in this repo.\\\",\n    \\\"work_dir\\\": \\\"$PWD\\\"\n  }\"\n```\n\n## API Reference\n\n### HTTP endpoints\n\n| Method | Path | Purpose |\n| --- | --- | --- |\n| `GET` | `/health` | Daemon health and active runtime name |\n| `POST` | `/sessions` | Create a session from `SessionRequest` |\n| `GET` | `/sessions` | List all known sessions |\n| `GET` | `/sessions/:id` | Raw session snapshot from the session manager |\n| `GET` | `/sessions/:id/info` | Session summary plus host paths and convenience URLs |\n| `GET` | `/sessions/:id/logs?cursor=N` | Incremental replay/log polling; returns `Agentruntime-Log-Cursor` header |\n| `GET` | `/sessions/:id/log` | Full persisted NDJSON log download |\n| `DELETE` | `/sessions/:id` | Kill the session and mark it completed/failed |\n| `GET` | `/ws/sessions/:id?since=N` | Daemon WebSocket bridge for replay plus stdin |\n\n### `POST /sessions`\n\n`POST /sessions` accepts `SessionRequest` JSON and returns:\n\n```json\n{\n  \"session_id\": \"7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"task_id\": \"optional-task-id\",\n  \"agent\": \"claude\",\n  \"runtime\": \"local\",\n  \"status\": \"running\",\n  \"ws_url\": \"ws://127.0.0.1:8090/ws/sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"log_url\": \"http://127.0.0.1:8090/sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458/logs\"\n}\n```\n\nRules enforced by the daemon today:\n\n- `agent` is required.\n- `prompt` is required unless `interactive` is `true`.\n- `runtime`, if present, must match the daemon runtime selected at startup.\n- `work_dir` is shorthand for a writable mount to `/workspace`.\n- `work_dir` is validated: must be absolute, must exist, must be a directory, must not contain sensitive paths (`.ssh`, `.gnupg`, `.aws`, `.kube`, `.docker`, `.config/gcloud`, `Library/Keychains`), must not contain `..` traversal, must not be `/`.\n\n### `GET /sessions/:id`\n\nThis returns the raw session snapshot from `pkg/session`, for example:\n\n```json\n{\n  \"id\": \"7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"task_id\": \"optional-task-id\",\n  \"agent_name\": \"claude\",\n  \"runtime_name\": \"local\",\n  \"session_dir\": \"/Users/me/.local/share/agentruntime/claude-sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"tags\": {\n    \"repo\": \"agentruntime\"\n  },\n  \"state\": \"running\",\n  \"created_at\": \"2026-03-17T07:00:00Z\"\n}\n```\n\n### `GET /sessions/:id/info`\n\nThis returns a friendlier API shape with URLs and host paths:\n\n```json\n{\n  \"session_id\": \"7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"agent\": \"claude\",\n  \"runtime\": \"local\",\n  \"status\": \"running\",\n  \"created_at\": \"2026-03-17T07:00:00Z\",\n  \"session_dir\": \"/Users/me/.local/share/agentruntime/claude-sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"log_file\": \"/Users/me/.local/share/agentruntime/logs/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458.jsonl\",\n  \"ws_url\": \"ws://127.0.0.1:8090/ws/sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458\",\n  \"log_url\": \"http://127.0.0.1:8090/sessions/7c4f3c3e-8a63-4fe2-baf3-d72b0b7d6458/logs\"\n}\n```\n\n### Daemon WebSocket bridge: `/ws/sessions/:id`\n\nThis is the public daemon bridge. It is replay-buffer based and intentionally simpler than the sidecar protocol.\n\nClient to daemon:\n\n- `stdin`: `{ \"type\": \"stdin\", \"data\": \"next line of input\\n\" }`\n- `ping`: `{ \"type\": \"ping\" }`\n- `resize`: `{ \"type\": \"resize\", \"cols\": 120, \"rows\": 40 }`\n\nDaemon to client:\n\n- `connected`\n- `stdout`\n- `replay`\n- `pong`\n- `error`\n- `exit`\n\nFor sidecar-backed sessions, the `stdout` and `replay` payloads are NDJSON event lines produced by the sidecar.\n\n## WS Protocol\n\nThe v2 sidecar has its own WebSocket protocol on `/ws`. Both the local runtime and Docker runtime use it internally, and you can also use it directly if you run `agentruntime-sidecar` yourself.\n\nCommand envelope:\n\n```json\n{\n  \"type\": \"prompt\",\n  \"data\": {\n    \"content\": \"Fix the failing handler.\"\n  }\n}\n```\n\nEvent envelope:\n\n```json\n{\n  \"type\": \"agent_message\",\n  \"data\": {\n    \"text\": \"Looking at the handler now.\",\n    \"delta\": true\n  },\n  \"offset\": 284,\n  \"timestamp\": 1773732712345\n}\n```\n\n### Command types\n\n| Type | Payload | Meaning |\n| --- | --- | --- |\n| `prompt` | `{ \"content\": \"...\" }` | Start a turn or send the first user request |\n| `interrupt` | none | Interrupt the active turn |\n| `steer` | `{ \"content\": \"...\" }` | Redirect an in-flight turn without starting over from scratch |\n| `context` | `{ \"text\": \"...\", \"filePath\": \"/workspace/file.go\" }` | Inject selected text plus its file path |\n| `mention` | `{ \"filePath\": \"/workspace/file.go\", \"lineStart\": 12, \"lineEnd\": 30 }` | Inject an IDE-style file mention/range |\n\n### Event types\n\n| Type | Meaning |\n| --- | --- |\n| `agent_message` | Normalized agent text output; includes streaming deltas and final messages |\n| `tool_use` | Normalized tool invocation start |\n| `tool_result` | Normalized tool completion |\n| `result` | Turn/session result summary |\n| `progress` | Intermediate progress from the agent |\n| `system` | Lifecycle or stderr-style system notices |\n| `error` | Protocol or backend error |\n| `exit` | Sidecar process exit notification |\n\n### Normalized payloads\n\n`agent_message` data:\n\n```json\n{\n  \"text\": \"partial or final text\",\n  \"delta\": true,\n  \"model\": \"optional-model-name\",\n  \"usage\": {\n    \"input_tokens\": 123,\n    \"output_tokens\": 45\n  },\n  \"turn_id\": \"optional-turn-id\",\n  \"item_id\": \"optional-item-id\"\n}\n```\n\n`tool_use` data:\n\n```json\n{\n  \"id\": \"tool-call-id\",\n  \"name\": \"Bash\",\n  \"server\": \"optional-mcp-server\",\n  \"input\": {\n    \"command\": \"git status\"\n  }\n}\n```\n\n`tool_result` data:\n\n```json\n{\n  \"id\": \"tool-call-id\",\n  \"name\": \"Bash\",\n  \"output\": \"main.go\\nREADME.md\\n\",\n  \"is_error\": false,\n  \"duration_ms\": 12\n}\n```\n\n`result` data:\n\n```json\n{\n  \"session_id\": \"optional-agent-session-id\",\n  \"turn_id\": \"optional-turn-id\",\n  \"status\": \"success\",\n  \"cost_usd\": 0.0012,\n  \"duration_ms\": 1840,\n  \"num_turns\": 1,\n  \"usage\": {\n    \"input_tokens\": 123,\n    \"output_tokens\": 45\n  }\n}\n```\n\n`exit` data:\n\n```json\n{\n  \"code\": 0,\n  \"error_detail\": \"optional error message\",\n  \"error_category\": \"auth_error\",\n  \"retryable\": false\n}\n```\n\nError categories (set when the agent session ends with a detectable error):\n\n| Category | Meaning | Retryable |\n| --- | --- | --- |\n| `model_not_found` | Requested model does not exist or is inaccessible | No |\n| `auth_error` | Authentication or API key failure | No |\n| `permission_denied` | Insufficient permissions | No |\n| `rate_limit` | API rate limit exceeded | Yes |\n| `duplicate_session` | Session ID already in use | Yes |\n| `upstream_api_error` | Provider API error (500, 503, 529) | Yes |\n| `startup_crash` | Agent produced zero tokens and minimal output — likely crashed before doing work | No |\n\n`error_category` and `retryable` are omitted when the session exits cleanly.\n\nNotes:\n\n- `offset` is a replay byte offset. Reconnect with `?since=\u003coffset\u003e` to replay from that point.\n- not every agent emits every event type on every run.\n- Claude emits streaming deltas today.\n- Claude emits `tool_use` events; Codex emits both `tool_use` and `tool_result`.\n\n## Context Injection\n\nContext injection is a sidecar v2 feature, not a daemon `/ws/sessions/:id` feature. To use it directly, run the sidecar and talk to its `/ws` endpoint.\n\nStart a sidecar for Claude:\n\n```bash\nSIDECAR_PORT=9090 \\\nAGENT_CMD='[\"claude\"]' \\\n./agentruntime-sidecar\n```\n\nSend a text selection:\n\n```json\n{\n  \"type\": \"context\",\n  \"data\": {\n    \"text\": \"func handleCreateSession(...) { ... }\",\n    \"filePath\": \"/workspace/pkg/api/handlers.go\"\n  }\n}\n```\n\nSend a file mention:\n\n```json\n{\n  \"type\": \"mention\",\n  \"data\": {\n    \"filePath\": \"/workspace/README.md\",\n    \"lineStart\": 1,\n    \"lineEnd\": 40\n  }\n}\n```\n\nCurrent behavior:\n\n- Claude wires `context` and `mention` into the embedded MCP IDE bridge.\n- Codex accepts those commands at the sidecar layer but currently logs a warning and does not inject them into the app-server session.\n\n## Modes\n\n### Prompt vs interactive\n\n- Prompt mode: set `interactive` to `false` or omit it, and include `prompt`. The daemon starts the agent, sends the initial request, and closes stdin for one-shot execution.\n- Interactive mode: set `interactive` to `true`. The daemon keeps stdin open, and the agent stays alive for follow-up input. On the daemon bridge, follow-up input uses `stdin`. On the sidecar `/ws`, follow-up control uses `prompt`, `interrupt`, and `steer`.\n- `pty` is separate from `interactive`. It asks the runtime for a PTY/TTY allocation; it does not change the sidecar protocol.\n\n### Local vs docker\n\n- Local: `./agentd --runtime local`. The runtime starts `agentruntime-sidecar` on the host and connects to it over localhost.\n- Docker: `./agentd --runtime docker`. The runtime starts `agentruntime-agent:latest`, waits for the sidecar health endpoint, then connects to the container over its published port.\n- Legacy local pipe mode still exists as `./agentd --runtime local-pipe`, but it bypasses sidecar v2 and does not provide normalized events. New integrations should use `local`.\n\n## Documentation\n\n- [ARCHITECTURE.md](ARCHITECTURE.md) — System architecture and design decisions\n- [docs/IMPLEMENTATION-GUIDE.md](docs/IMPLEMENTATION-GUIDE.md) — Developer reference (session lifecycle, event schema, field reference)\n- [docs/architecture-flows.md](docs/architecture-flows.md) — Detailed sequence diagrams\n- [docs/guides/lifecycle-hooks.md](docs/guides/lifecycle-hooks.md) — Container lifecycle hooks (pre_init, post_init, sidecar, post_run)\n- [docs/guides/hooks.md](docs/guides/hooks.md) — Claude Code tool-use hooks\n- [docs/specs/](docs/specs/) — Design specs (historical)\n- [docs/research/](docs/research/) — Protocol research references\n\n## Configuration\n\n`SessionRequest` is the shared request shape used by HTTP, the Go client, and `agentd dispatch --config`.\n\n```json\n{\n  \"task_id\": \"optional-task-id\",\n  \"name\": \"optional-label\",\n  \"tags\": {\n    \"repo\": \"agentruntime\",\n    \"ticket\": \"DOCS-12\"\n  },\n  \"agent\": \"claude\",\n  \"runtime\": \"local\",\n  \"model\": \"optional-model\",\n  \"prompt\": \"Fix the flaky test.\",\n  \"timeout\": \"5m\",\n  \"pty\": false,\n  \"interactive\": false,\n  \"resume_session\": \"optional-agent-native-session-id\",\n  \"work_dir\": \"/absolute/path\",\n  \"mounts\": [\n    {\n      \"host\": \"/absolute/path\",\n      \"container\": \"/workspace\",\n      \"mode\": \"rw\"\n    }\n  ],\n  \"volumes\": [\n    \"/host/hooks:/hooks:ro\"\n  ],\n  \"lifecycle\": {\n    \"pre_init\": \"/hooks/setup.sh\",\n    \"post_init\": \"/hooks/warmup.sh\",\n    \"sidecar\": \"/hooks/watchdog.sh\",\n    \"post_run\": \"/hooks/cleanup.sh\",\n    \"hook_timeout\": 30\n  },\n  \"claude\": {\n    \"settings_json\": {},\n    \"claude_md\": \"# extra instructions\",\n    \"mcp_json\": {},\n    \"credentials_path\": \"~/.claude/credentials.json\",\n    \"memory_path\": \"~/.claude/projects\",\n    \"output_format\": \"stream-json\"\n  },\n  \"codex\": {\n    \"config_toml\": {},\n    \"instructions\": \"# extra instructions\",\n    \"approval_mode\": \"suggest\"\n  },\n  \"mcp_servers\": [\n    {\n      \"name\": \"docs\",\n      \"type\": \"http\",\n      \"url\": \"http://${HOST_GATEWAY}:8080\",\n      \"token\": \"optional-token\"\n    }\n  ],\n  \"env\": {\n    \"OPENAI_API_KEY\": \"set-me\"\n  },\n  \"container\": {\n    \"image\": \"agentruntime-agent:latest\",\n    \"memory\": \"4g\",\n    \"cpus\": 2,\n    \"security_opt\": [\n      \"label=disable\"\n    ]\n  }\n}\n```\n\nFields that matter most in practice:\n\n- `agent`: currently `claude` or `codex` for the v2 sidecar path.\n- `prompt` plus `interactive`: choose one-shot or interactive behavior.\n- `work_dir` or writable `mounts`: controls `/workspace`. For Docker runtime, omitting `work_dir` means no host volume is mounted — the agent works inside the container's own filesystem.\n- `volumes`: convenience string array using Docker's `host:container[:mode]` syntax. Merged with `mounts`.\n- `lifecycle`: container lifecycle hooks — `pre_init`, `post_init`, `sidecar`, `post_run`. See [lifecycle hooks guide](docs/guides/lifecycle-hooks.md).\n- `claude` and `codex`: file materialization into `~/.claude` or `~/.codex`. If omitted, the daemon infers a default empty config block from the `agent` field so credentials and config files are still materialized. Explicitly sending `\"codex\": {}` or `\"claude\": {}` is equivalent but makes the intent clear.\n- `mcp_servers`: merged into Claude MCP config and sanitized during materialization.\n- `env`: explicit env vars for the runtime.\n- `container.image`, `container.memory`, `container.cpus`, `container.security_opt`: Docker-specific controls that are applied today.\n\nImportant implementation notes:\n\n- `work_dir` is shorthand for `{ \"host\": work_dir, \"container\": \"/workspace\", \"mode\": \"rw\" }`.\n- If both `work_dir` and `mounts` are present, both are used.\n- `runtime` is optional and must match the daemon runtime if you send it.\n- `${HOST_GATEWAY}` is resolved inside MCP server URLs during materialization.\n- The schema currently accepts a few forward-compatible fields that are not wired through end-to-end by `agentd` yet: top-level `name`, `model`, and `timeout`; `claude.output_format`; `codex.approval_mode`; and `container.network`.\n\n### Example: local Claude prompt mode\n\n```json\n{\n  \"agent\": \"claude\",\n  \"prompt\": \"Summarize the architecture of this repo in one paragraph.\",\n  \"work_dir\": \"/Users/me/Toolkit/agentruntime\",\n  \"claude\": {\n    \"claude_md\": \"Stay focused on this repository.\"\n  }\n}\n```\n\n### Example: Docker Codex interactive mode\n\n```yaml\nagent: codex\ninteractive: true\nwork_dir: /Users/me/Toolkit/agentruntime\ncodex:\n  instructions: |\n    You are working inside the agentruntime repository.\ncontainer:\n  image: agentruntime-agent:latest\n  memory: 4g\n  cpus: 2\nenv:\n  OPENAI_API_KEY: ${OPENAI_API_KEY}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieliser%2Fagentruntime","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanieliser%2Fagentruntime","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanieliser%2Fagentruntime/lists"}