一键导入
ax-agent
Use when modifying the sandboxed agent process — runner, IPC client, local/IPC tools, tool catalog, prompt building, or identity loading in src/agent/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use when modifying the sandboxed agent process — runner, IPC client, local/IPC tools, tool catalog, prompt building, or identity loading in src/agent/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when modifying logging, error handling, or diagnostic messages — logger setup, transports, error diagnosis patterns in src/logger.ts and src/errors.ts
Use when modifying the trusted host process — server orchestration, message routing, IPC handler, request lifecycle, event streaming, file handling, plugin loading, or agent delegation in src/host/
Use when modifying agent sandbox isolation -- Docker, Apple Container (macOS), or k8s providers in src/providers/sandbox/
Use when modifying IPC protocol between host and agent — schemas, actions, length-prefix framing, or Zod validation in ipc-schemas.ts and ipc-server.ts
AX project architecture and coding skills - use sub-skills for specific subsystems (agent, host, cli, providers, etc.)
Use this skill when the user asks for a capability the agent doesn't yet have — a new integration (Linear, GitHub, Notion, Slack, etc.), a new workflow, a new tool, or "can you check our team's X" where X is a service the agent has no existing connector for. Creates a proper AX skill under .ax/skills/<name>/ with the right frontmatter, credentials, and domain allowlist so the capability becomes permanent after admin approval. Use this skill whenever you'd otherwise be tempted to improvise with one-off scripts, `npm install`, or `execute_script` to reach an external service.
| name | ax-agent |
| description | Use when modifying the sandboxed agent process — runner, IPC client, local/IPC tools, tool catalog, prompt building, or identity loading in src/agent/ |
The agent subsystem runs inside a sandboxed process (no network, no credentials). It receives a user message + history via stdin, builds a system prompt from modular components, registers IPC tools with context-aware filtering, then runs an LLM agent loop that streams text output to stdout. All LLM calls, tool operations (including bash/file ops), and privileged operations route through IPC to the trusted host.
| File | Responsibility | Key Exports |
|---|---|---|
src/agent/runner.ts | Entry point, stdin parse, agent dispatch, IPC transport selection. | run(), parseStdinPayload(), compactHistory(), historyToPiMessages(), AgentConfig, IIPCClient |
src/agent/ipc-client.ts | Length-prefixed Unix socket IPC with heartbeat keep-alive | IPCClient (connect, call, disconnect, reconnect) |
src/agent/http-ipc-client.ts | HTTP-based IPC client for k8s pods (drop-in replacement for IPCClient) | HttpIPCClient, HttpIPCClientOptions |
src/agent/skill-installer.ts | Skill dependency installer — reads SKILL.md install specs, runs missing installs. Not called eagerly; agent runs install commands via bash when it uses a skill. | installSkillDeps() |
src/agent/stream-utils.ts | Shared pi-ai message conversion and stream event utilities | convertPiMessages(), emitStreamEvents() |
src/agent/heartbeat-state.ts | Heartbeat check state persistence (last-run timestamps) | HeartbeatState |
src/agent/local-sandbox.ts | Agent-side local sandbox executor; runs bash/file tools in-container with host audit gate | createLocalSandbox(), LocalSandboxOptions |
src/agent/tool-catalog.ts | Single source of truth for tool metadata, context-aware filtering, plugin commands, and Cowork plugin tools | TOOL_CATALOG, filterTools(), ToolFilterContext, normalizeOrigin(), normalizeIdentityFile() |
src/agent/prompt/modules/commands.ts | Plugin commands prompt module (priority 72) — surfaces installed plugin slash commands | CommandsModule |
src/agent/ipc-tools.ts | Tools that proxy to host via IPC (pi-session runner) | createIPCTools(client, opts) |
src/agent/tools/describe-tools.ts | Agent-side factory stubs for the describe_tools + call_tool meta-tools (indirect dispatch mode). Catalog entries in tool-catalog.ts plumb the real wiring; the factories exist for isolated unit tests and Task 4.x hook points (projection, spill). | createDescribeToolsTool(), createCallToolTool() |
src/host/ipc-handlers/sandbox-tools.ts | IPC handlers for bash/file ops (host-side, subprocess mode only) | createSandboxToolHandlers() |
src/agent/identity-loader.ts | Loads identity files from preloaded stdin payload (or filesystem fallback) | loadIdentityFiles(opts) |
src/agent/agent-setup.ts | Shared setup: prompt building, event subscription, tool filtering | buildSystemPrompt(), subscribeAgentEvents() |
src/agent/prompt/builder.ts | Assembles system prompt from ordered modules | PromptBuilder, PromptResult |
src/agent/prompt/types.ts | PromptContext, PromptModule interface, IdentityFiles | PromptContext, PromptModule, IdentityFiles |
src/agent/ipc-transport.ts | IPC LLM stream function; converts IPC responses to pi-ai events with image block support | createIPCStreamFn() |
src/agent/web-proxy-bridge.ts | TCP-to-Unix-socket bridge for HTTP forward proxy. Loopback listener (127.0.0.1) forwards to host web proxy via mounted Unix socket. Handles both HTTP forwarding and HTTPS CONNECT tunneling. Same pattern as tcp-bridge.ts | startWebProxyBridge(), WebProxyBridge |
src/agent/proxy-stream.ts | Proxy-based LLM stream function; routes via credential-injecting proxy | createProxyStreamFn(), makeProxyErrorMessage() |
src/agent/runners/pi-session.ts | pi-coding-agent runner variant with proxy or IPC LLM transport | runPiSession() |
src/agent/runners/claude-code.ts | Claude Code runner variant with Agent SDK | runClaudeCode(), buildSDKPrompt() |
src/agent/mcp-server.ts | MCP tool registry for claude-code runner | createIPCMcpServer(), MCPServerOptions |
src/agent/git-cli.ts | Native git CLI wrapper (execFile, no shell). Supports separate gitdir via GIT_DIR/GIT_WORK_TREE env vars | gitExec(), gitClone(), gitFetch(), gitResetHard(), gitCommit(), gitPush(), GitOpts |
src/agent/git-sidecar.ts | K8s sidecar HTTP server for git ops. Runs in separate container, manages .git directory. HTTP endpoints: POST /pull, POST /turn-complete, GET /health | startGitSidecar() |
src/agent/git-workspace.ts | Agent-side git workspace wrapper for non-k8s mode. Clone, pull, commit+push lifecycle | GitWorkspace (clone, init, pull, commitAndPush) |
runner.ts parses CLI args (--agent, --ipc-socket, --workspace, --proxy-socket, etc.)IPCClient (Unix socket) later, or use a pre-connected one in listen mode (AX_IPC_LISTEN=1)AX_HOST_URL is set, creates HttpIPCClient with HTTP-based IPC to host's /internal/ipc route. NATS is only used for initial work dispatch (queue groups).IPCClient in listen mode before reading stdin, sets config.ipcClient{message, history, taintRatio, profile, sessionId, ipcToken, identity, ...}) via parseStdinPayload() (or receives work via NATS in k8s mode)applyPayload() populates config and calls ipcClient.setContext() with session/request/user/token fieldsrunPiSession() or runClaudeCode()config.ipcClient if available, otherwise creates a new IPCClient and connectsloadIdentityFiles({ preloaded: config.identity })buildSystemPrompt(config) reads config.skills (populated from the stdin payload — the host derives it live per turn from the git snapshot via getAgentSkills). SkillsModule (priority 70) renders the host-authoritative bullet list with pending/invalid markers from pendingReasons. Plugin commands may also be loaded from installed plugins.buildSystemPrompt() builds both the system prompt AND a ToolFilterContext for context-aware tool filteringlocal-sandbox.ts executes locally with host audit gate (sandbox_approve -> execute -> sandbox_result)sandbox-tools.tstool-catalog.ts): Single source of truth. Defines TOOL_CATALOG (TypeBox-based specs) and filterTools(ctx) for context-aware filtering.tool-dispatch-unification): ToolFilterContext.toolDispatchMode ('direct' | 'indirect', default indirect) controls whether the describe_tools + call_tool meta-tools are registered. The host ships config.tool_dispatch through the stdin payload; the agent threads mode into buildSystemPrompt's returned toolFilter so both the pi-session and claude-code runners pick it up.ToolFilterContext flags (hasHeartbeat, hasSkills, hasWorkspaceTiers, hasGovernance) control which tools are available. Filtering is automatic in both pi-session and claude-code runners.
ipc-tools.ts for pi-session, mcp-server.ts for claude-code): Non-sandbox tools proxy to host via IPC -- memory_*, web_*, audit_query, identity_write, user_write, scheduler_*, skill_propose, request_credential, agent_delegate, image_generate, workspace_*, identity_propose, proposal_list, agent_registry_list. Each calls client.call({action, ...params}). All IPC consumers use the IIPCClient interface (not concrete IPCClient), making them transport-agnostic.request_credential is a standalone tool: Lives in tool-catalog.ts with category: 'credential'. Always available (not filtered by skill presence). Maps to credential_request IPC action. Used when skills or web APIs need env vars the agent doesn't have.local-sandbox.ts executes bash/file ops inside the agent's own container. Protocol: sandbox_approve IPC -> auto-approve well-known network domains (extractNetworkDomains() → web_proxy_approve IPC) -> execute locally (async spawn for bash, readFileSync/writeFileSync for files) -> sandbox_result IPC (best-effort). Uses safePath() for path containment. Enabled when CONTAINER_SANDBOXES.has(config.sandboxType). Bash uses async spawn (not execFileSync) to keep the event loop responsive during command execution.src/host/ipc-handlers/sandbox-tools.ts. Host resolves workspace via workspaceMap (Map<sessionId, path>).{name, label, description, parameters: Type.Object({...}), execute(id, params)}. Parameters use TypeBox (@sinclair/typebox), NOT Zod.tool() from Agent SDK.--proxy-socket is provided, else IPC. Never direct API calls from the agent.{_heartbeat: true, ts}) to prevent timeout on long-running operations. Timeout resets on each heartbeat.type: 'image' (fileId ref) or type: 'image_data' (inline base64). Injected into the last plain-text user message for LLM calls.PromptBuilder holds an ordered list of PromptModule instances, sorted by priority (lower = earlier). Each module implements shouldInclude(ctx), render(ctx), estimateTokens(ctx), and optionally renderMinimal(ctx).
| Module | Priority | Content | Optional |
|---|---|---|---|
| IdentityModule | 0 | SOUL.md, IDENTITY.md, BOOTSTRAP.md, USER.md | No |
| InjectionDefenseModule | 5 | Prompt injection defenses | No |
| SecurityModule | 10 | Taint awareness, identity ownership rules | No |
| ToolStyleModule | 12 | Tool invocation instructions | No |
| MemoryRecallModule | 60 | Memory recall pattern instructions | No |
| SkillsModule | 70 | Host-authoritative skills index (pushed via stdin payload) | Yes |
| CommandsModule | 72 | Installed plugin slash commands | Yes |
| DelegationModule | 75 | Agent delegation instructions + runner selection guide | Yes |
| HeartbeatModule | 80 | HEARTBEAT.md periodic check schedule | Yes |
| RuntimeModule | 90 | Agent type, sandbox type, tool list | No |
| ReplyGateModule | 95 | Reply optionality logic | No |
Budget allocation (budget.ts) can drop optional modules or switch to renderMinimal when context is tight. The PromptBuildResult includes metadata and a ToolFilterContext that runners use to filter tools.
| File | Purpose |
|---|---|
SOUL.md | Core personality, values, voice -- shared across all users |
IDENTITY.md | Self-description, capabilities, evolving self-model |
BOOTSTRAP.md | First-session instructions (shown only when SOUL.md is absent) |
USER.md | Per-user preferences (stored at agentDir/users/<userId>/USER.md) |
HEARTBEAT.md | Periodic self-check schedule and health definitions |
loadIdentityFiles() reads from preloaded identity data (sent via stdin payload from DocumentStore). Falls back to filesystem agentDir if preloaded data unavailable. Returns empty strings for missing files (never throws).
pi-session.ts)Uses createAgentSession() from @mariozechner/pi-coding-agent. Two LLM transport modes:
--proxy-socket): Direct Anthropic SDK calls, credentials injected by proxyUses config.ipcClient (pre-connected IIPCClient) when available, otherwise creates a new IPCClient. Non-sandbox tools route through IPC; sandbox tools route to local-sandbox.ts in container mode or through IPC in subprocess mode. Passes compactHistory() if history exceeds 75% of context window. Integrates with git workspace: clones at startup, commits at turn end. Uses GitWorkspace in non-k8s mode, HTTP sidecar in k8s mode.
claude-code.ts)Uses query() from @anthropic-ai/claude-agent-sdk. Creates:
IIPCClient interface)Uses config.ipcClient (pre-connected IIPCClient) when available. Supports inline image blocks via buildSDKPrompt() which returns either a plain string or an AsyncIterable<SDKUserMessage> with structured content blocks. Integrates with git workspace: clones at startup, commits at turn end. Uses GitWorkspace in non-k8s mode, HTTP sidecar in k8s mode.
Both runners support git-backed workspace persistence when WORKSPACE_REPO_URL is set:
When WORKSPACE_REPO_URL is set and NOT in k8s:
GitWorkspace (git-workspace.ts) clones the repo at startupgitWorkspace.init() configures git usergitWorkspace.pull() fetches latest (handles merge conflicts gracefully)gitWorkspace.commitAndPush() stages all changes and pushesWhen WORKSPACE_REPO_URL is set AND in k8s (AX_HOST_URL set):
git-sidecar.ts runs as a separate container (UID 1001) with exclusive .git access/workspace but NOT .githttp://localhost:9099/pull at turn start → sidecar fetches + resetshttp://localhost:9099/turn-complete at turn end → sidecar commits + pushesKey env vars:
WORKSPACE_REPO_URL — Git clone URL (triggers workspace integration)AX_GIT_SIDECAR_PORT — Sidecar HTTP port (default: 9099)Security: git-cli.ts uses execFile() (no shell) to prevent command injection. All git commands go through this wrapper.
Adding a new tool:
TOOL_CATALOG in src/agent/tool-catalog.ts with TypeBox parametersinjectUserId, timeoutMs)src/agent/ipc-tools.tssrc/agent/mcp-server.ts using Zodsrc/ipc-schemas.ts with .strict()src/host/ipc-server.tssrc/agent/tool-catalog.ts (filterTools) if the tool should be context-dependentAdding a new prompt module:
src/agent/prompt/modules/<name>.ts implementing PromptModule (extend BasePromptModule)PromptBuilder constructor (src/agent/prompt/builder.ts)priority to control ordering (0-100)shouldInclude(ctx) to conditionally include the modulerender(ctx) and optionally renderMinimal(ctx)tests/agent/prompt/modules/ToolFilterContext logic in buildSystemPrompt() and filterTools()Adding a sandbox tool (bash/file ops):
TOOL_CATALOG in src/agent/tool-catalog.ts with category: 'sandbox' and singletonAction: 'sandbox_<name>'src/ipc-schemas.ts with ipcAction('sandbox_<name>', {...}) (needed for both IPC and approve/result calls)src/host/ipc-handlers/sandbox-tools.ts using safePath() for file access (subprocess mode)src/agent/local-sandbox.ts (container mode) — follow the approve -> execute -> report patternsrc/agent/ipc-tools.ts (pi-session, check sandbox category switch) and src/agent/mcp-server.ts (claude-code, conditional on sandbox presence)tests/host/ipc-handlers/sandbox-tools.test.tsbuildSystemPrompt() returns a ToolFilterContext that both runners use to filter the catalog. Excluded prompt modules automatically exclude corresponding tools.ipc-tools.ts (TypeBox) AND mcp-server.ts (Zod). Missing one means that runner type has no access to the tool. Ensure parameter names match via sync tests.createIPCStreamFn() accepts optional imageBlocks and injects them into the last plain-text user message. Both type: 'image' (fileId) and type: 'image_data' (base64) are supported._heartbeat frames in response parsing.Type.Object(...), IPC uses z.strictObject(...).safePath() is mandatory: Every sandbox tool file operation must go through safePath() to prevent workspace escape -- both in local-sandbox.ts (container mode) and sandbox-tools.ts (subprocess mode).{ok: false}).''. Check content length, not for exceptions..ax/skills/ in the git workspace — agents Read them on demand — but the prompt-visible skills index is host-sourced, derived live per turn via getAgentSkills and pushed in the stdin payload. No IPC fetch, no filesystem fallback.filterTools().agent_delegate and runner selection.IIPCClient interface, not concrete IPCClient: All IPC consumers (ipc-tools.ts, mcp-server.ts, runner.ts, local-sandbox.ts) accept the IIPCClient interface so they work with both Unix socket (IPCClient) and HTTP (HttpIPCClient) transports. Never import the concrete class in tool/sandbox code.socket (default Unix socket), http (k8s pods via AX_HOST_URL), listen (Apple Container reverse bridge via AX_IPC_LISTEN=1). The runner creates the appropriate client before stdin read and passes it as config.ipcClient.HttpIPCClient. The old NATSIPCClient and nats-bridge.ts have been removed.AX_WEB_PROXY_SOCKET (container mode) or AX_WEB_PROXY_URL/AX_PROXY_LISTEN_PORT (k8s/subprocess) env vars and start a loopback TCP bridge (web-proxy-bridge.ts) that forwards to the host proxy. Sets HTTP_PROXY/HTTPS_PROXY/http_proxy/https_proxy env vars for child processes (npm, curl, git, etc.). Warning: Do NOT use AX_WEB_PROXY_PORT as an env var name — K8s auto-generates it from the ax-web-proxy Service (AX_WEB_PROXY_PORT=tcp://IP:PORT).local-sandbox.ts exports extractNetworkDomains() which maps known package manager commands (npm, pip, yarn, cargo, go, gem) to their registry domains. The bash() method pre-approves these via web_proxy_approve IPC before executing the command, preventing the proxy governance deadlock.AX_WEB_PROXY_URL) must be in BOTH the k8s pod spec (cold spawn) AND the NATS work payload (warm pool). The runner's parseStdinPayload() extracts webProxyUrl and applyPayload() sets process.env.AX_WEB_PROXY_URL.setContext() after work payload: In NATS mode, the IPC client is connected before the work payload arrives. applyPayload() calls ipcClient.setContext() to set session/request/token fields needed for NATS subject scoping (ipc.request.{requestId}.{token}).git-local (file:// repos at ~/.ax/repos/) for local mode, git-http (HTTP repos via ax-git k8s Service) for k8s mode. Old GCS/local/none workspace providers have been removed.local-sandbox.ts (agent-local execution with sandbox_approve/sandbox_result IPC). Subprocess sandbox routes through IPC to host-side handlers. The localSandbox option in IPCToolsOptions/MCPServerOptions controls this.WORKSPACE_REPO_URL env var is set. Without it, workspace behavior is unchanged..git/ — git operations go through the sidecar HTTP API. The sidecar handles force-push fallback for concurrent sessions.AX_REQUEST_ID binds runner logger reqId: At module load, runner.ts reads process.env.AX_REQUEST_ID and (if present) creates the top-level logger as getLogger().child({ component: 'runner', reqId: AX_REQUEST_ID.slice(-8) }). The sandbox provider injects this env var from SandboxConfig.requestId. A single grep <reqId> reconstructs host -> sandbox provider -> agent runner logs.