docs: expand Claude contractor design

This commit is contained in:
nav
2026-04-11 06:57:11 +00:00
parent ca91c1de41
commit f7c5875eeb
5 changed files with 1542 additions and 0 deletions

436
docs/claude/ARCHITECTURE.md Normal file
View File

@@ -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
<agent-workspace>/.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 <agent-id> --workspace <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

307
docs/claude/CLI.md Normal file
View File

@@ -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 <agent-id> --workspace <workspace> --contractor <claude|gemini>
```
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 <agent-id>`
- `--workspace <workspace>`
- `--contractor <claude|gemini>`
### Optional, possible later
Not required for first implementation, but likely useful later:
- `--bridge-model <model-id>`
- `--backend <acp-backend>`
- `--mode <persistent|oneshot>`
- `--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 <agent-id> --workspace <workspace> --model contractor-claude-bridge --non-interactive
```
Important note:
Earlier review of `openclaw agents add` docs showed these supported options:
> `--workspace <dir>`
> `--model <id>`
> `--agent-dir <dir>`
> `--bind <channel[:accountId]>`
> `--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
<workspace>/.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 <agent-id>
```
Show:
- contractor kind
- bridge model id
- runtime backend
- session mapping count
- recent activity
### List sessions
```bash
openclaw contractor-agents sessions --agent-id <agent-id>
```
Show session mappings for the given contractor-backed agent.
### Reset mapping
```bash
openclaw contractor-agents reset-session --agent-id <agent-id> --openclaw-session <session-key>
```
Drop a specific OpenClaw-to-Claude mapping so the next message recreates the Claude session.
### Disable
```bash
openclaw contractor-agents disable --agent-id <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 <dir>`
> `--model <id>`
> `--agent-dir <dir>`
> `--bind <channel[:accountId]>`
> `--non-interactive`
> `--json`
Source:
- https://docs.openclaw.ai/cli/agents

View File

@@ -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<SessionMapEntry | null>
put(entry: SessionMapEntry): Promise<void>
markOrphaned(openclawSessionKey: string): Promise<void>
remove(openclawSessionKey: string): Promise<void>
list(agentId: string): Promise<SessionMapEntry[]>
}
```
## 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<void>
```
## 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<CreateClaudeSessionResult>
sendMessage(input: SendClaudeMessageInput): Promise<SendClaudeMessageResult>
closeSession?(sessionId: string): Promise<void>
}
```
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 <agent-id> --workspace <workspace> --model contractor-claude-bridge --non-interactive`
- translate failures into contractor CLI errors
Possible export:
```ts
export async function createBaseAgent(...): Promise<void>
```
## 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<void>
```
## 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.

238
docs/claude/MODEL.md Normal file
View File

@@ -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

View File

@@ -23,6 +23,16 @@ Desired properties:
Use a session proxy runtime rather than replaying full OpenClaw-managed context into Claude Code on every turn. 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: Core idea:
- map each OpenClaw session to a Claude Code session - map each OpenClaw session to a Claude Code session
@@ -30,6 +40,7 @@ Core idea:
- resume or continue that Claude session - resume or continue that Claude session
- forward only the newest actionable message plus minimal metadata - forward only the newest actionable message plus minimal metadata
- avoid rebuilding full prompt history on every turn - 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: This reduces:
@@ -137,6 +148,26 @@ This supports the idea that OpenClaw skills and tooling may be adapted into Clau
Source: Source:
- https://code.claude.com/docs/en/plugins-reference - 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 ## Initial CLI Plan
Introduce a plugin-owned command: Introduce a plugin-owned command: