531 lines
13 KiB
Markdown
531 lines
13 KiB
Markdown
# 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.
|