# Claude Contractor Implementation Plan ## Purpose Turn the architecture decisions into an implementation-oriented plan for phase 1. This document is intentionally practical. It focuses on file structure, core modules, interfaces, and build order for the Claude-only phase. ## Phase 1 implementation goal Deliver a working Claude-backed contractor agent in OpenClaw that can: 1. be provisioned through `openclaw contractor-agents add` 2. use `contractor-claude-bridge` as its primary model 3. map an OpenClaw session to a Claude Code session 4. forward the latest user message into Claude Code 5. return Claude output through normal OpenClaw response flow Gemini is out of scope for this phase. ## Proposed repository structure ```text ContractorAgent/ ├── docs/ │ ├── claude/ │ │ ├── PLAN.md │ │ ├── MODEL.md │ │ ├── CLI.md │ │ ├── ARCHITECTURE.md │ │ └── IMPLEMENTATION.md │ └── gemini/ │ └── .gitkeep ├── src/ │ ├── index.ts │ ├── config/ │ │ ├── schema.ts │ │ └── contractor-config.ts │ ├── cli/ │ │ ├── register-cli.ts │ │ ├── contractor-agents-add.ts │ │ └── contractor-agents-status.ts │ ├── model/ │ │ ├── register-bridge-model.ts │ │ ├── contractor-claude-bridge.ts │ │ ├── input-filter.ts │ │ └── response-normalizer.ts │ ├── contractor/ │ │ ├── metadata-resolver.ts │ │ ├── runtime-state.ts │ │ ├── session-map-store.ts │ │ ├── bootstrap.ts │ │ └── recovery.ts │ ├── claude/ │ │ ├── acp-adapter.ts │ │ ├── claude-session.ts │ │ └── claude-types.ts │ ├── openclaw/ │ │ ├── agent-config-writer.ts │ │ ├── agents-add-runner.ts │ │ └── session-context.ts │ └── types/ │ ├── contractor.ts │ ├── session-map.ts │ └── model.ts ├── package.json ├── openclaw.plugin.json └── tsconfig.json ``` Not every file needs to exist on day 1, but this is a good target shape. ## Module responsibilities ## 1. `src/index.ts` Plugin entry point. Responsibilities: - export the OpenClaw plugin entry - register the bridge model - register plugin-owned CLI commands - register any supporting services needed by the plugin Expected shape: ```ts export default definePluginEntry({ id: "contractor-agent", name: "Contractor Agent", description: "Claude-backed contractor agents for OpenClaw", register(api) { // register bridge model // register CLI // register helpers/services if needed }, }) ``` ## 2. `src/model/register-bridge-model.ts` Registers the custom model id used by contractor-backed agents. Primary concern: - expose `contractor-claude-bridge` to OpenClaw as a valid model selection target This is the key seam that makes contractor-backed agents look normal to OpenClaw. ## 3. `src/model/contractor-claude-bridge.ts` Core bridge model implementation. Responsibilities: - receive the model request from OpenClaw - identify agent id and session key - resolve contractor metadata - resolve or create Claude session mapping - filter input - call Claude adapter - normalize result - return OpenClaw-compatible model output This file is the heart of the system. ## 4. `src/model/input-filter.ts` Convert raw OpenClaw model input into the payload actually sent to Claude. Possible exports: ```ts export function extractLatestActionableMessage(...) export function buildClaudeTurnPayload(...) ``` This module should be pure and testable. ## 5. `src/model/response-normalizer.ts` Convert Claude-side output into OpenClaw-compatible model response data. Possible exports: ```ts export function normalizeClaudeResponse(...) export function normalizeClaudeError(...) ``` This should also be mostly pure and testable. ## 6. `src/contractor/metadata-resolver.ts` Read agent config and decide whether the current agent is a contractor-backed Claude agent. Responsibilities: - inspect agent config - return structured contractor runtime metadata - reject unsupported or misconfigured contractor setups Possible export: ```ts export function resolveContractorAgentMetadata(...): ContractorAgentMetadata ``` ## 7. `src/contractor/session-map-store.ts` Read and write the OpenClaw session to Claude session mapping file. Responsibilities: - find by OpenClaw session key - insert new mapping - update timestamps - replace mapping on reset/recovery - mark mapping orphaned Possible interface: ```ts export interface SessionMapStore { get(openclawSessionKey: string): Promise put(entry: SessionMapEntry): Promise markOrphaned(openclawSessionKey: string): Promise remove(openclawSessionKey: string): Promise list(agentId: string): Promise } ``` ## 8. `src/contractor/runtime-state.ts` Resolve and initialize plugin runtime state directories. Responsibilities: - compute state directory from workspace - ensure directories exist - provide path helpers for session-map and related files Possible exports: ```ts export function getContractorStateDir(workspace: string): string export function getSessionMapPath(workspace: string): string export async function ensureContractorStateDir(workspace: string): Promise ``` ## 9. `src/contractor/bootstrap.ts` Create the initial contractor bootstrap payload for new Claude sessions. Responsibilities: - generate initial instruction text - include workspace metadata - include contractor role expectations - avoid repeating per-turn content Possible export: ```ts export function buildClaudeBootstrap(...): string ``` ## 10. `src/contractor/recovery.ts` Handle missing or invalid Claude sessions. Responsibilities: - mark mappings orphaned - trigger fresh Claude session creation - optionally build a minimal recovery summary Possible export: ```ts export async function recoverClaudeSession(...) ``` ## 11. `src/claude/acp-adapter.ts` Claude runtime adapter used by the bridge model. Responsibilities: - create Claude session - resume Claude session - send message to Claude - close Claude session if needed Possible interface: ```ts export interface ClaudeAcpAdapter { createSession(input: CreateClaudeSessionInput): Promise sendMessage(input: SendClaudeMessageInput): Promise closeSession?(sessionId: string): Promise } ``` This should hide ACP/runtime-specific details from the bridge model. ## 12. `src/openclaw/agents-add-runner.ts` Encapsulate the logic for creating base OpenClaw agents from the contractor CLI. Responsibilities: - run or emulate `openclaw agents add --workspace --model contractor-claude-bridge --non-interactive` - translate failures into contractor CLI errors Possible export: ```ts export async function createBaseAgent(...): Promise ``` ## 13. `src/openclaw/agent-config-writer.ts` Write contractor metadata into OpenClaw config after the base agent is created. Responsibilities: - update target agent config entry - write contractor runtime stanza - preserve unrelated config Possible export: ```ts export async function markAgentAsClaudeContractor(...): Promise ``` ## 14. `src/cli/register-cli.ts` Register the plugin-owned CLI root. Responsibilities: - register `openclaw contractor-agents` - attach subcommands such as `add`, later `status`, `sessions`, `reset-session` ## 15. `src/cli/contractor-agents-add.ts` Implementation of the phase 1 provisioning flow. Responsibilities: - parse args - validate contractor kind - create base agent - write contractor metadata - initialize runtime state directory - initialize empty session map - return human-readable or JSON result ## Primary types ## `src/types/contractor.ts` ```ts export type ContractorKind = "claude" | "gemini" export type ContractorAgentMetadata = { agentId: string contractor: "claude" bridgeModel: string workspace: string backend: "acp" mode: "persistent" | "oneshot" } ``` ## `src/types/session-map.ts` ```ts export type SessionMapState = "active" | "closed" | "orphaned" export type SessionMapEntry = { openclawSessionKey: string agentId: string contractor: "claude" claudeSessionId: string workspace: string createdAt: string lastActivityAt: string state: SessionMapState } export type SessionMapFile = { version: 1 sessions: SessionMapEntry[] } ``` ## `src/types/model.ts` ```ts export type ContractorBridgeModelRequest = { agentId: string openclawSessionKey: string workspace: string latestUserMessage: string rawInput: unknown } ``` Exact fields depend on how OpenClaw provider/model requests are exposed in runtime. ## Implementation notes from probe testing See BRIDGE_MODEL_FINDINGS.md for full details. Key constraints that affect implementation: - The sidecar MUST support SSE streaming (`stream: true` always sent by OpenClaw). Non-streaming responses cause OpenClaw to drop assistant history from subsequent turns. - OpenClaw sends the full system prompt (~28K chars) rebuilt on every turn. The input filter must strip this and extract only the latest user message. - User message format from OpenClaw: `[Day YYYY-MM-DD HH:MM TZ] message text` - Claude session continuation uses UUID from `@anthropic-ai/claude-agent-sdk` `message.session_id`. Resume via `options: { resume: sessionId }`. - Custom model is registered via `openclaw.json` provider config, not a plugin SDK call. The install script must write the provider entry and set `baseUrl` to the sidecar port. ## Revised milestone order Milestone 3 (bridge model) is moved before Milestone 2 (CLI) because the bridge sidecar is the load-bearing component. Everything else depends on confirming it works end-to-end. ## Suggested implementation order ## Milestone 1, project skeleton Create: - package metadata - plugin manifest - `src/index.ts` - empty docs-backed source tree Success condition: - plugin package structure exists - build can run ## Milestone 2, CLI provisioning path Implement: - `register-cli.ts` - `contractor-agents-add.ts` - `agents-add-runner.ts` - `agent-config-writer.ts` - `runtime-state.ts` - empty `session-map-store.ts` Success condition: - `openclaw contractor-agents add ... --contractor claude` provisions a contractor-marked agent successfully ## Milestone 3, bridge model registration Implement: - `register-bridge-model.ts` - minimal `contractor-claude-bridge.ts` Success condition: - OpenClaw recognizes `contractor-claude-bridge` as a valid model id ## Milestone 4, session mapping and Claude dispatch Implement: - session-map CRUD - metadata resolver - initial ACP adapter - input filter - response normalizer Success condition: - a contractor-backed agent can receive a real turn and return a Claude-generated response ## Milestone 5, bootstrap and recovery Implement: - `bootstrap.ts` - `recovery.ts` - reset semantics Success condition: - first-turn session creation and broken-session recovery are both handled predictably ## Testing strategy ## Unit-test first Best candidates for unit tests: - metadata resolver - input filter - response normalizer - session-map-store read/write/update behavior - bootstrap payload builder ## Integration-test later Integration targets: - contractor CLI add flow - bridge model request path - session creation and reuse - reset and orphan recovery ## Failure handling rules The implementation should clearly separate: ### Provisioning failures Examples: - invalid workspace - base agent creation failed - contractor metadata write failed Recommended behavior: - fail loudly - roll back base agent creation if practical and safe ### Runtime failures Examples: - Claude session missing - ACP adapter unavailable - response normalization failed Recommended behavior: - mark runtime mapping state explicitly - recover when safe - surface user-visible error when recovery is not possible ## Minimum viable internal contracts To prevent overdesign, phase 1 only needs a few stable internal boundaries: 1. bridge model -> metadata resolver 2. bridge model -> session map store 3. bridge model -> Claude ACP adapter 4. contractor CLI -> base agent creator 5. contractor CLI -> agent config writer If these boundaries are clean, later Gemini support can reuse most of the same architecture. ## Design choices to keep intentionally simple in phase 1 - single contractor kind implemented, Claude - JSON file-backed session map - no aggressive streaming support initially - no large-scale tool bridge yet - no complicated migration logic yet ## Things to avoid early - overabstracting for Gemini before Claude works - mixing static config with dynamic session map state - rebuilding full OpenClaw context every turn - making the bridge model pretend to be a normal stateless LLM provider ## Open questions to resolve during implementation 1. What exact OpenClaw plugin/provider registration API should the bridge model use? 2. How should the plugin call underlying agent creation, shelling out to CLI vs internal config mutation? 3. What exact payload does OpenClaw pass to a custom model implementation? 4. What exact continuation identifier should be stored for Claude sessions? 5. How should Claude tool requests later be bridged into OpenClaw-hosted tool execution? ## Immediate next coding step If starting implementation now, I would begin with: 1. plugin manifest and `src/index.ts` 2. CLI registration for `contractor-agents add` 3. base agent creation + contractor metadata write 4. empty state dir and empty session map initialization That gets provisioning working before the runtime bridge itself is added.