437 lines
12 KiB
Markdown
437 lines
12 KiB
Markdown
# 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
|