Files
ContractorAgent/docs/claude/IMPLEMENTATION.md

13 KiB

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

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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:

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

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

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

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.