From f7c5875eeb334bc7c0d2788469ee29f2590d0646 Mon Sep 17 00:00:00 2001 From: nav Date: Sat, 11 Apr 2026 06:57:11 +0000 Subject: [PATCH] docs: expand Claude contractor design --- docs/claude/ARCHITECTURE.md | 436 ++++++++++++++++++++++++++++ docs/claude/CLI.md | 307 ++++++++++++++++++++ docs/claude/IMPLEMENTATION.md | 530 ++++++++++++++++++++++++++++++++++ docs/claude/MODEL.md | 238 +++++++++++++++ docs/claude/PLAN.md | 31 ++ 5 files changed, 1542 insertions(+) create mode 100644 docs/claude/ARCHITECTURE.md create mode 100644 docs/claude/CLI.md create mode 100644 docs/claude/IMPLEMENTATION.md create mode 100644 docs/claude/MODEL.md diff --git a/docs/claude/ARCHITECTURE.md b/docs/claude/ARCHITECTURE.md new file mode 100644 index 0000000..879fb71 --- /dev/null +++ b/docs/claude/ARCHITECTURE.md @@ -0,0 +1,436 @@ +# Claude Contractor Architecture + +## Purpose + +Describe the phase 1 architecture for integrating Claude Code into OpenClaw as a contractor-backed agent. + +This document assumes the phase 1 decisions already made: + +- Claude only, Gemini deferred +- plugin-owned CLI root: `openclaw contractor-agents ...` +- contractor-backed agents use a custom primary model +- that custom model bridges OpenClaw turns into Claude Code sessions + +## High-level architecture + +```text +User / Channel + ↓ +OpenClaw routing + agent selection + ↓ +Agent primary model = contractor-claude-bridge + ↓ +ContractorAgent plugin + ├─ contractor metadata resolver + ├─ session map store + ├─ Claude ACP adapter + ├─ input filter / context reducer + └─ response normalizer + ↓ +Claude Code session +``` + +## Core architectural principle + +OpenClaw remains the outer control plane. +Claude Code remains the live reasoning runtime. + +That means: + +### OpenClaw owns + +- agent registration +- workspace ownership +- routing and channel bindings +- visible transcript/session files +- permissions and approvals +- plugin loading and bridge model selection + +### Claude Code owns + +- live session continuity +- evolving reasoning context for a contractor task +- internal coding workflow and execution trajectory + +The plugin exists to bridge those two worlds without forcing both systems to maintain full independent copies of the same conversation state. + +## Main components + +## 1. Contractor metadata resolver + +This component determines whether the current agent is a contractor-backed Claude agent and, if so, what runtime settings apply. + +Expected inputs: + +- OpenClaw agent id +- agent config entry +- plugin config defaults + +Expected outputs: + +- whether contractor runtime is enabled +- contractor kind, currently `claude` +- bridge model id +- backend type, likely ACP +- runtime mode, likely persistent +- workspace path + +### Proposed config direction + +```json5 +{ + agents: { + list: [ + { + id: "contractor-dev", + workspace: "/workspace/contractor-dev", + model: "contractor-claude-bridge", + runtime: { + type: "contractor", + contractor: { + kind: "claude", + backend: "acp", + mode: "persistent" + } + } + } + ] + } +} +``` + +The exact config location can still evolve, but the architecture assumes there is a stable way to resolve contractor runtime settings from agent identity. + +## 2. Bridge model + +The bridge model is the primary execution seam inside OpenClaw. + +Suggested model id: + +- `contractor-claude-bridge` + +### Why it exists + +OpenClaw expects a primary model for each agent. The bridge model satisfies that contract while redirecting actual reasoning work to Claude Code. + +### What it does + +- receive the agent turn as a model request +- identify the target OpenClaw agent and session +- resolve contractor metadata +- call the session map store +- call the Claude ACP adapter +- normalize Claude output into an OpenClaw model response + +### What it does not do + +- behave like a normal stateless LLM provider +- replay the full OpenClaw prompt envelope every turn +- replace OpenClaw routing or channel delivery + +## 3. Session map store + +This component persists the mapping between OpenClaw sessions and Claude sessions. + +Suggested location: + +```text +/.openclaw/contractor-agent/session-map.json +``` + +Suggested record shape: + +```ts +type SessionMapEntry = { + openclawSessionKey: string + agentId: string + contractor: "claude" + claudeSessionId: string + workspace: string + createdAt: string + lastActivityAt: string + state: "active" | "closed" | "orphaned" +} +``` + +### Responsibilities + +- lookup mapping by OpenClaw session key +- create mapping after first successful Claude session creation +- update timestamps and state +- mark sessions orphaned on recoverable failures +- remove or replace mappings during reset flows + +### Non-responsibilities + +- storing full conversation text +- storing OpenClaw config +- being the canonical transcript source + +## 4. Input filter and context reducer + +This is one of the most important parts of the architecture. + +The bridge model should not forward full OpenClaw-managed input blindly to Claude Code. Instead it should convert the incoming turn into a contractor payload. + +### Contractor payload should include + +- latest user message +- OpenClaw session key +- agent id +- workspace path +- minimal runtime hints needed by Claude + +### Contractor payload should exclude or heavily reduce + +- repeated OpenClaw prompt wrappers +- large historical message dumps already represented in Claude session state +- redundant policy text already injected at Claude session creation + +### Why this matters + +Without this reducer, the system drifts into dual-context management: + +- OpenClaw reconstructs context every turn +- Claude maintains its own session context +- both copies diverge over time + +The reducer prevents that by making Claude the primary owner of live context after bootstrap. + +## 5. Claude ACP adapter + +This component talks to Claude Code through the chosen ACP/runtime path. + +Architecture assumption for phase 1: + +- Claude Code is hosted through OpenClaw-compatible ACP infrastructure +- the plugin uses persistent Claude sessions whenever possible + +### Responsibilities + +- create new Claude sessions +- resume existing Claude sessions +- send new user messages to Claude +- collect Claude output +- surface recoverable failures to the bridge model + +### Expected operations + +- `createSession(...)` +- `resumeSession(...)` +- `sendMessage(...)` +- `closeSession(...)` +- `healthCheck(...)` + +Exact API shape depends on implementation details and available runtime hooks. + +## 6. Response normalizer + +Claude output may not match the exact shape OpenClaw expects from a model provider. The normalizer converts it into the response format used by the bridge model. + +### Responsibilities + +- convert plain assistant text +- preserve useful structured output if present +- flatten multi-part response payloads where needed +- translate adapter/runtime failures into OpenClaw-visible model errors + +### Desired outcome + +From OpenClaw's point of view, the result should feel like a normal model completion even though it came from a stateful Claude session. + +## Turn lifecycle + +## A. Agent provisioning lifecycle + +Triggered by: + +```bash +openclaw contractor-agents add --agent-id --workspace --contractor claude +``` + +Flow: + +1. create OpenClaw agent with model `contractor-claude-bridge` +2. write contractor runtime metadata to agent config +3. create contractor state directory +4. initialize empty session map store + +No Claude session is required at provisioning time. + +## B. First conversation turn + +1. OpenClaw routes message to contractor-backed agent +2. OpenClaw invokes primary model `contractor-claude-bridge` +3. bridge model resolves agent metadata +4. bridge model checks session map for current OpenClaw session +5. no mapping found +6. bridge model asks Claude ACP adapter to create a new Claude session +7. plugin injects bootstrap instructions and minimal OpenClaw metadata +8. plugin sends latest user message +9. Claude replies +10. plugin stores mapping and returns normalized output to OpenClaw + +## C. Later conversation turns + +1. OpenClaw invokes `contractor-claude-bridge` +2. metadata resolver confirms contractor-backed Claude agent +3. session map returns mapped Claude session id +4. input reducer extracts newest actionable message +5. Claude ACP adapter resumes session and sends message +6. Claude replies +7. response normalizer emits OpenClaw model response +8. session map timestamp is updated + +## D. Reset flow + +If the user resets the session: + +1. plugin invalidates or replaces mapping for the OpenClaw session key +2. next turn creates a fresh Claude session +3. bootstrap instructions are injected again + +Recommended behavior: + +- keep OpenClaw agent and workspace stable +- replace only contractor session continuity + +## E. Recovery flow + +If Claude session is missing or unusable: + +1. mark mapping as `orphaned` +2. create a new Claude session +3. inject bootstrap instructions +4. optionally pass a minimal recovery summary derived from recent OpenClaw-visible context +5. replace mapping + +This should be treated as a degraded fallback path, not the normal operating path. + +## Bootstrap strategy + +Bootstrap text should be injected when a Claude session is first created. + +It should include: + +- contractor role inside OpenClaw +- workspace root +- expectations for message style back to OpenClaw +- constraints around approvals and tools +- guidance on using session continuity instead of expecting full replay every turn + +It should not be re-injected every turn. + +## Storage design + +## Static config + +Static agent configuration belongs in OpenClaw config and is managed by the contractor CLI. + +Examples: + +- agent id +- workspace +- bridge model id +- contractor kind +- contractor runtime defaults + +## Dynamic state + +Dynamic session mapping belongs in plugin runtime state under the agent workspace. + +Examples: + +- OpenClaw session key to Claude session id mapping +- session state flags +- timestamps + +This separation keeps declarative config and runtime state from bleeding into each other. + +## Why not mirror full Claude state into OpenClaw session files + +Because doing so would: + +- create duplicated context systems +- encourage replay-based turn handling +- increase token cost and failure complexity +- still fail to capture Claude's real internal runtime state perfectly + +Instead: + +- OpenClaw transcript remains the visible conversation record +- Claude session remains the live cognitive state for the contractor runtime + +## Approval and tool interaction + +Phase 1 should keep this conservative. + +Recommended initial stance: + +- start with limited or no complex tool bridging beyond what is required for basic operation +- preserve OpenClaw as the place where approvals and policy are enforced +- design later tool bridging so Claude requests are translated into OpenClaw-hosted tool execution rather than bypassing OpenClaw directly + +## Key risks + +### Risk 1, poor boundary between model and runtime + +If the bridge model tries to imitate a normal provider too literally, implementation gets awkward. It must be treated as a stateful pseudo-model. + +### Risk 2, session mapping drift + +If mappings are lost or stale, the user may silently lose Claude-side continuity. Recovery behavior must be explicit. + +### Risk 3, over-forwarding context + +If too much OpenClaw-managed prompt content is forwarded each turn, the design collapses back into dual-context management. + +### Risk 4, under-specified reset semantics + +Reset, new, and recovery must be clearly defined or the plugin will become unpredictable. + +## Phase 1 implementation priorities + +1. Register bridge model id +2. Implement contractor metadata resolver +3. Implement contractor CLI add flow +4. Implement session map store +5. Implement create/resume/send behavior in Claude ACP adapter +6. Implement response normalization +7. Add reset and recovery semantics + +## References + +### OpenClaw Claude via ACP + +> "ACP sessions let OpenClaw run external coding harnesses (for example ... Claude Code ...) through an ACP backend plugin." + +> "For Claude Code through ACP, the stack is: +> 1. OpenClaw ACP session control plane +> 2. bundled `acpx` runtime plugin +> 3. Claude ACP adapter +> 4. Claude-side runtime/session machinery" + +Source: +- https://docs.openclaw.ai/tools/acp-agents + +### OpenClaw plugin extensibility + +> "Plugins extend OpenClaw with new capabilities: channels, model providers, ... agent tools, or any combination." + +Source: +- https://docs.openclaw.ai/plugins/building-plugins + +### Plugin entry contract + +> "For provider plugins, tool plugins, hook plugins, and anything that is not a messaging channel." + +Source: +- https://docs.openclaw.ai/plugins/sdk-entrypoints + +### Plugin CLI surface + +> "For plugin-owned root CLI commands, prefer `api.registerCli(..., { descriptors: [...] })` ..." + +Source: +- https://docs.openclaw.ai/plugins/sdk-entrypoints diff --git a/docs/claude/CLI.md b/docs/claude/CLI.md new file mode 100644 index 0000000..493a857 --- /dev/null +++ b/docs/claude/CLI.md @@ -0,0 +1,307 @@ +# Contractor Agents CLI Design + +## Purpose + +Define the plugin-owned CLI surface for managing contractor-backed OpenClaw agents. + +Phase 1 supports Claude only. Gemini remains reserved in the command shape but is not implemented yet. + +## Command root + +```bash +openclaw contractor-agents +``` + +This should be implemented as a plugin-owned CLI root, not as a patch to `openclaw agents add`. + +## Why a separate command root + +Current OpenClaw plugin docs show support for plugin CLI registration, but do not document support for extending existing core commands with new flags. + +Relevant doc statements: + +> "Common registration methods: ... `registerCommand` / `registerCli` | CLI commands" + +> "For plugin-owned root CLI commands, prefer `api.registerCli(..., { descriptors: [...] })` ..." + +This suggests the supported extension seam is plugin-owned CLI trees. + +Sources: +- https://docs.openclaw.ai/tools/plugin +- https://docs.openclaw.ai/plugins/sdk-entrypoints + +## Phase 1 command + +```bash +openclaw contractor-agents add --agent-id --workspace --contractor +``` + +Phase 1 restrictions: + +- `--contractor` must currently be `claude` +- `gemini` is accepted only as a reserved future value if desired, but should currently fail with a clear not-yet-implemented message unless you intentionally decide to reject it at parsing time + +## Arguments + +### Required + +- `--agent-id ` +- `--workspace ` +- `--contractor ` + +### Optional, possible later + +Not required for first implementation, but likely useful later: + +- `--bridge-model ` +- `--backend ` +- `--mode ` +- `--json` +- `--non-interactive` + +For now, phase 1 can hardcode the bridge model and runtime defaults. + +## Behavior of `add` + +For: + +```bash +openclaw contractor-agents add --agent-id my-agent --workspace /path/to/ws --contractor claude +``` + +The CLI should do the following. + +### Step 1, validate inputs + +- ensure `agent-id` is provided +- ensure workspace exists or is creatable, based on your desired policy +- ensure contractor kind is supported +- ensure target agent does not already exist unless overwrite behavior is explicitly added later + +### Step 2, create the base OpenClaw agent + +Run the equivalent of: + +```bash +openclaw agents add --workspace --model contractor-claude-bridge --non-interactive +``` + +Important note: + +Earlier review of `openclaw agents add` docs showed these supported options: + +> `--workspace ` +> `--model ` +> `--agent-dir ` +> `--bind ` +> `--non-interactive` +> `--json` + +Source: +- https://docs.openclaw.ai/cli/agents + +This means the contractor CLI can compose the existing agent creation flow rather than replacing it. + +### Step 3, mark the agent as a contractor-backed agent + +After agent creation, write contractor metadata so runtime selection and session bridging can identify this agent as contractor-managed. + +Proposed config direction: + +```json5 +{ + agents: { + list: [ + { + id: "my-agent", + model: "contractor-claude-bridge", + runtime: { + type: "contractor", + contractor: { + kind: "claude", + backend: "acp", + mode: "persistent" + } + } + } + ] + } +} +``` + +Exact schema is still TBD, but the CLI should be the mechanism that writes this state. + +### Step 4, initialize contractor runtime state + +Prepare runtime state for the agent, including a place to store session mappings. + +Suggested path: + +```text +/.openclaw/contractor-agent/ +``` + +Suggested initial files: + +```text +session-map.json +runtime.json +``` + +## Session mapping responsibility + +The CLI does not create Claude sessions immediately unless you explicitly want eager setup. + +Recommended phase 1 behavior: + +- create agent metadata +- create runtime state directory +- initialize empty mapping file +- defer real Claude session creation to first actual agent turn + +This keeps `add` lightweight and avoids unused contractor sessions. + +## Suggested future commands + +These are not required immediately, but the command tree should reserve room for them. + +### Status + +```bash +openclaw contractor-agents status --agent-id +``` + +Show: +- contractor kind +- bridge model id +- runtime backend +- session mapping count +- recent activity + +### List sessions + +```bash +openclaw contractor-agents sessions --agent-id +``` + +Show session mappings for the given contractor-backed agent. + +### Reset mapping + +```bash +openclaw contractor-agents reset-session --agent-id --openclaw-session +``` + +Drop a specific OpenClaw-to-Claude mapping so the next message recreates the Claude session. + +### Disable + +```bash +openclaw contractor-agents disable --agent-id +``` + +Either: +- remove contractor metadata but keep agent +- or mark the contractor runtime disabled + +Semantics TBD. + +## JSON output + +I recommend all subcommands eventually support `--json` so this can be scripted. + +Example success response shape: + +```json +{ + "ok": true, + "agentId": "my-agent", + "workspace": "/path/to/ws", + "contractor": "claude", + "model": "contractor-claude-bridge", + "runtimeStateDir": "/path/to/ws/.openclaw/contractor-agent" +} +``` + +## Failure cases + +The CLI should fail clearly for: + +- unsupported contractor kind +- agent already exists +- workspace invalid or inaccessible +- bridge model plugin not available +- failure in underlying `openclaw agents add` +- failure writing contractor metadata + +If the agent is created successfully but metadata writing fails, the CLI should either: + +- roll back the created agent if safe and simple +- or return a partial-failure error with explicit cleanup instructions + +Phase 1 recommendation: prefer explicit rollback if implementation is reliable. + +## Interaction with the bridge model + +The CLI should not directly run Claude Code or create a Claude contractor session by default. + +Its main job is to provision an agent whose primary model is the contractor bridge model: + +- `contractor-claude-bridge` + +That model then handles runtime dispatch during actual conversation turns. + +## Implementation notes + +The plugin should register a root command owned by the plugin. + +Relevant doc statements: + +> "For plugin-owned root CLI commands, prefer `api.registerCli(..., { descriptors: [...] })` when you want the command to stay lazy-loaded without disappearing from the root CLI parse tree." + +Source: +- https://docs.openclaw.ai/plugins/sdk-entrypoints + +So the intended shape is: + +- plugin registers `contractor-agents` +- plugin owns parsing and handling of its subcommands +- plugin composes `openclaw agents add` behavior internally or via shared config/runtime helpers + +## Open questions + +1. Should the CLI shell out to `openclaw agents add` or use a lower-level config/runtime helper directly? +2. Where should contractor metadata live, `agents.list[].runtime`, plugin config, or both? +3. Should `gemini` be rejected now or accepted with a not implemented error? +4. Should add create the workspace if it does not exist? +5. Should agent creation be rolled back if contractor metadata writing fails? + +## References + +### Plugin CLI support + +> "Common registration methods: ... `registerCommand` / `registerCli` | CLI commands" + +Source: +- https://docs.openclaw.ai/tools/plugin + +### Plugin-owned CLI descriptors + +> "For plugin-owned root CLI commands, prefer `api.registerCli(..., { descriptors: [...] })` ..." + +Source: +- https://docs.openclaw.ai/plugins/sdk-entrypoints + +### Existing agent creation command + +> `agents add [name]` + +> `--workspace ` +> `--model ` +> `--agent-dir ` +> `--bind ` +> `--non-interactive` +> `--json` + +Source: +- https://docs.openclaw.ai/cli/agents diff --git a/docs/claude/IMPLEMENTATION.md b/docs/claude/IMPLEMENTATION.md new file mode 100644 index 0000000..6d3325b --- /dev/null +++ b/docs/claude/IMPLEMENTATION.md @@ -0,0 +1,530 @@ +# 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. + +## 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. diff --git a/docs/claude/MODEL.md b/docs/claude/MODEL.md new file mode 100644 index 0000000..8c4e111 --- /dev/null +++ b/docs/claude/MODEL.md @@ -0,0 +1,238 @@ +# Claude Bridge Model Design + +## Purpose + +Define how Claude-backed contractor agents integrate into OpenClaw using a custom model seam. + +This document is specific to the Claude phase of ContractorAgent. + +## Why this must be a custom model + +OpenClaw expects every agent to have a primary model. For contractor-backed agents, the actual reasoning engine is not a normal OpenClaw LLM provider call, but a Claude Code session managed outside the usual stateless completion flow. + +Therefore the contractor integration should present itself to OpenClaw as a custom model id, while internally acting as a stateful bridge to Claude Code. + +Recommended model id for phase 1: + +- `contractor-claude-bridge` + +## Mental model + +From OpenClaw's perspective: + +- the agent has a normal primary model +- a model request is issued for each turn +- the model returns assistant output + +From the plugin's perspective: + +- the model request is actually a routing event +- the request identifies the OpenClaw session and agent +- the plugin resolves a mapped Claude Code session +- the plugin forwards only the newest actionable turn to Claude Code +- the plugin returns Claude's output as the model response + +So this is a pseudo-model backend with sessionful behavior. + +## Responsibilities of the bridge model + +The bridge model should: + +1. accept model requests for contractor-backed agents +2. identify the current OpenClaw session and contractor agent id +3. resolve or create the mapped Claude Code session +4. filter OpenClaw-managed envelope/context +5. forward only the latest actionable message plus minimal metadata +6. collect Claude Code output +7. normalize that output into an OpenClaw model response +8. persist session mapping metadata + +The bridge model should not: + +- act like a generic stateless LLM provider +- replay full OpenClaw session history every turn unless recovery is required +- try to mirror all Claude internal state into OpenClaw session files + +## Model lifecycle + +### First turn in an OpenClaw session + +1. OpenClaw selects `contractor-claude-bridge` as the agent's primary model +2. bridge model receives the turn input +3. plugin extracts: + - agent id + - OpenClaw session key + - workspace + - latest user message +4. no session mapping exists yet +5. plugin creates a Claude Code session through ACP/runtime integration +6. plugin injects initial contractor instructions and minimal OpenClaw metadata +7. plugin sends the latest user message +8. Claude responds +9. plugin stores the new mapping and returns assistant output to OpenClaw + +### Later turns + +1. bridge model receives next turn +2. plugin resolves existing mapping +3. plugin resumes or continues the Claude Code session +4. plugin forwards latest user message only +5. Claude responds +6. plugin returns normalized response and updates mapping timestamps + +### Reset or new session + +Reset semantics should be explicit. + +Recommended behavior: + +- OpenClaw `/new` or reset-equivalent for this agent should create a fresh Claude session transcript +- keep the OpenClaw agent identity and routing stable +- replace the existing mapping for the OpenClaw session key +- reinject initial contractor instructions + +## Session mapping contract + +Bridge model depends on a persistent mapping store. + +Suggested record shape: + +```ts +type ContractorSessionMapEntry = { + openclawSessionKey: string + agentId: string + contractor: "claude" + claudeSessionId: string + workspace: string + createdAt: string + lastActivityAt: string + state: "active" | "closed" | "orphaned" +} +``` + +## Input filtering + +The bridge model must not blindly forward the entire model input payload to Claude Code. + +Instead it should derive a contractor payload with: + +- latest user-visible message +- minimal agent metadata +- minimal workspace metadata +- optional turn metadata needed for reply formatting or approvals + +It should ignore or strip: + +- repeated long-form system envelope generated by OpenClaw +- redundant chat history already owned by Claude session memory +- internal bookkeeping not useful to Claude Code + +This keeps Claude as the owner of live context. + +## Initial contractor instructions + +When creating a fresh Claude session, inject a stable bootstrap payload that includes: + +- identity of the contractor role inside OpenClaw +- workspace path +- allowed interaction style with OpenClaw +- tool/skill access strategy +- output expectations for OpenClaw replies +- rule that session continuity lives primarily in Claude Code session state + +This injection should happen on session creation and hard resets, not every turn. + +## Output normalization + +Claude output must be normalized before returning to OpenClaw. + +Normalization should handle: + +- plain assistant text +- tool-use related status text +- multi-part responses if Claude adapter streams or chunks +- failures or blocked actions +- explicit clarification requests + +The custom model should emit output in a way OpenClaw can treat as a normal assistant response. + +## Session file interaction + +The current working assumption is: + +- OpenClaw session files remain the system of record for visible conversation state +- Claude session state remains the system of record for internal live reasoning context + +Therefore the bridge model should inject Claude responses back into OpenClaw's normal session response path, rather than bypassing it. + +This means the plugin should preserve OpenClaw's normal transcript behavior while avoiding full upstream context replay. + +## Recovery behavior + +### Missing Claude session + +If the mapped Claude session is missing or invalid: + +1. mark mapping as `orphaned` +2. create a fresh Claude session +3. inject bootstrap context +4. optionally summarize minimal recent OpenClaw-visible context for recovery +5. store replacement mapping + +This should be treated as degraded recovery, not the normal path. + +### Mapping corruption + +If mapping storage cannot be read: + +- fail closed for the turn if data integrity is uncertain +- or create a new Claude session only when policy allows and user impact is acceptable + +## Why not use a normal provider model directly + +Because a normal provider model implies: + +- stateless prompt in +- stateless completion out +- OpenClaw-owned context reconstruction each turn + +That is the opposite of the intended contractor design. + +The custom model seam is useful precisely because it lets OpenClaw keep the agent abstraction while delegating real session continuity to Claude Code. + +## Open questions + +1. Which provider/plugin registration seam is best for the bridge model id? +2. What exact request shape does the plugin receive for a model turn in OpenClaw runtime? +3. Where should bootstrap instructions be stored, generated text, template file, or config? +4. How should streaming behavior be represented if Claude session output arrives incrementally? +5. How should tool requests from Claude be surfaced back into OpenClaw tool execution? + +## References + +### OpenClaw plugin capability + +> "Plugins extend OpenClaw with new capabilities: channels, model providers, ... agent tools, or any combination." + +Source: +- https://docs.openclaw.ai/plugins/building-plugins + +### Plugin entry model + +> "For provider plugins, tool plugins, hook plugins, and anything that is not a messaging channel." + +Source: +- https://docs.openclaw.ai/plugins/sdk-entrypoints + +### Claude Code via ACP + +> "ACP sessions let OpenClaw run external coding harnesses (for example ... Claude Code ...) through an ACP backend plugin." + +> "For Claude Code through ACP, the stack is: +> 1. OpenClaw ACP session control plane +> 2. bundled `acpx` runtime plugin +> 3. Claude ACP adapter +> 4. Claude-side runtime/session machinery" + +Source: +- https://docs.openclaw.ai/tools/acp-agents diff --git a/docs/claude/PLAN.md b/docs/claude/PLAN.md index bd271f4..269aadf 100644 --- a/docs/claude/PLAN.md +++ b/docs/claude/PLAN.md @@ -23,6 +23,16 @@ Desired properties: Use a session proxy runtime rather than replaying full OpenClaw-managed context into Claude Code on every turn. +Important implementation note: in OpenClaw this should be delivered as a **custom model**. + +Reason: + +- OpenClaw expects each agent to have a primary model +- for this special contractor agent, the real primary intelligence is provided by Claude Code +- therefore the most natural integration seam is a custom model that accepts OpenClaw messages, routes them into the mapped Claude Code session, and then injects Claude's response back into OpenClaw session state + +So although the architecture is conceptually a session proxy runtime, the concrete integration form inside OpenClaw should be a **bridge model** or **pseudo-model backend** for Claude-backed agents. + Core idea: - map each OpenClaw session to a Claude Code session @@ -30,6 +40,7 @@ Core idea: - resume or continue that Claude session - forward only the newest actionable message plus minimal metadata - avoid rebuilding full prompt history on every turn +- return the Claude response through the custom model seam so it becomes part of normal OpenClaw agent flow This reduces: @@ -137,6 +148,26 @@ This supports the idea that OpenClaw skills and tooling may be adapted into Clau Source: - https://code.claude.com/docs/en/plugins-reference +## Bridge Model Direction + +The contractor-agent implementation should expose one or more plugin-provided custom model ids for OpenClaw agents to use as their primary model. + +For Claude phase 1, the working assumption is a model id in the shape of: + +- `contractor-claude-bridge` + +Behavior of this bridge model: + +1. receive the agent turn from OpenClaw +2. identify the current OpenClaw session key and target contractor agent +3. resolve or create the mapped Claude Code session +4. filter OpenClaw-managed envelope/context so only the latest actionable message and minimal metadata are forwarded +5. send the turn to Claude Code through the chosen ACP/session continuation path +6. capture Claude's response +7. inject the response back into OpenClaw through the model response path and session file flow + +This approach fits OpenClaw's requirement that every agent has a primary model while still allowing Claude Code to act as the true reasoning engine for contractor-backed agents. + ## Initial CLI Plan Introduce a plugin-owned command: