Commit Graph

16 Commits

Author SHA1 Message Date
h z
4e015c677b Merge pull request 'fix(bridge): scope CLI sessions per OpenClaw session and reset on /new' (#2) from fix/per-openclaw-session-cli-mapping into main
Reviewed-on: #2
2026-04-29 07:27:09 +00:00
zhi
992f4d8703 fix(bridge): scope CLI sessions per OpenClaw session and reset on /new
The bridge was keying claudeSessionId by agentId alone, so every Discord
channel, DM, and cron run for a single agent shared one Claude CLI
session. Two consequences in the wild:

  - Cross-channel context bleed: 8.7MB session for `developer` mixed
    references from channels 1474327736242798612 and 1498579994044010566
    plus the operator DM all in one --resume thread.
  - `/new` had no effect on the CLI side. OpenClaw rotated its session
    file but the bridge kept --resume-ing the same long-lived
    claudeSessionId, eventually crossing the 1M model context (debug log
    showed `prompt is too long: 1179616 tokens > 1000000 maximum`).

Changes:

  * input-filter: extract `chat_id` from the Conversation-info
    untrusted-metadata block (scanning all messages, since runtimeOnly
    turns put it in the system prompt) and detect bare `/new`/`/reset`
    via the BARE_SESSION_RESET_PROMPT_BASE marker. Add buildSessionKey
    `${agentId}::${chatId}` and resolveDispatchPrompt fallback for the
    empty user message that OpenClaw sends on bare resets.

  * server: use the composite session key for getSession/putSession;
    on bareSessionReset, removeSession before dispatching so the CLI
    starts a fresh session; on a CLI result_error (typically
    prompt_too_long) drop the entry too so the next turn doesn't
    re-resume into the poisoned context.

  * claude/sdk-adapter: surface CLI terminal errors via a new
    `result_error` event (carries reason + sessionId) so the bridge
    can react instead of just streaming the synthetic
    "Prompt is too long" assistant text and silently re-using the
    same session.

  * index: convert register() to synchronous (OpenClaw rejects async
    register with "plugin register must be synchronous"); replace the
    pre-bind port probe with a server-level EADDRINUSE handler.

  * .gitignore: ignore node_modules/ and dist/.
2026-04-28 12:32:37 +00:00
zhi
6be8d47982 fix(claude-adapter): commit turn on result event, dont block on process exit
Previously dispatchToClaude awaited child.on(close) before yielding the done
event. Claude CLIs Bash tool occasionally leaves ssh/bash grandchildren alive
(e.g. a GUI app that ignores SIGPIPE on the remote end of a piped ssh command);
that kept claude -p alive past end-of-turn, which kept the bridge SSE stream
open, which kept OpenClaw from committing the turn to its session jsonl.

Switch to emitting done as soon as the terminal result stream-json event
arrives. Spawn claude in its own process group (detached:true) and schedule
a best-effort SIGTERM/SIGKILL sweep of leaked descendants; temp-file cleanup
runs asynchronously on actual process close.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 08:52:57 +00:00
zhi
e73a7ea049 fix: support root execution and factory-registered tool lookup
- Replace --dangerously-skip-permissions with --allowedTools whitelist
  to support running Claude Code as root (root blocks the former flag)
- Fix /mcp/execute tool lookup for plugins that register tools via
  factory functions (e.g. padded-cell pcexec) where the global registry
  names array is empty — now falls back to instantiating factories and
  matching by returned tool name

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-17 12:23:43 +00:00
49af2129ae --append 2026-04-12 14:05:56 +01:00
h z
9cd90f7213 Merge pull request 'feat/plugin-services-restructure' (#1) from feat/plugin-services-restructure into main
Reviewed-on: #1
2026-04-11 20:38:30 +00:00
6ae795eea8 chore: add .gitignore and untrack internal Claude dev notes
- Add .gitignore covering node_modules, .idea, credential files
- Untrack docs/claude/LESSONS_LEARNED.md and OPENCLAW_PLUGIN_DEV.md
  (internal dev notes, not part of the plugin repo)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:28:56 +01:00
07a0f06e2e refactor: restructure to plugin/ + services/ layout and add per-turn bootstrap injection
- Migrate src/ → plugin/ (plugin/core/, plugin/web/, plugin/commands/)
  and src/mcp/ → services/ per OpenClaw plugin dev spec
- Add Gemini CLI backend (plugin/core/gemini/sdk-adapter.ts) with GEMINI.md
  system-prompt injection
- Inject bootstrap as stateless system prompt on every turn instead of
  first turn only: Claude via --system-prompt, Gemini via workspace/GEMINI.md;
  eliminates isFirstTurn branch, keeps skills in sync with OpenClaw snapshots
- Fix session-map-store defensive parsing (sessions ?? []) to handle bare {}
  reset files without crashing on .find()
- Add docs/TEST_FLOW.md with E2E test scenarios and expected outcomes
- Add docs/claude/BRIDGE_MODEL_FINDINGS.md with contractor-probe results

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 21:21:32 +01:00
eee62efbf1 feat: add openclaw origin-name alias to MCP tool descriptions
Claude Code sees tools as mcp__openclaw__<name> but skills/instructions
reference the original OpenClaw name (e.g. "memory_search"). Each tool
description now includes a disambiguation line:

  [openclaw tool: <name>] If any skill or instruction refers to "<name>",
  this is the tool to call.

This ensures Claude can correctly map skill references to the right tool
even when the MCP prefix makes the name appear different.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:25:33 +01:00
7a779c8560 feat: relay MCP tool calls to OpenClaw plugin registry instead of reimplementing
When Claude Code calls an MCP tool via /mcp/execute, the bridge now:
1. Looks up the tool in OpenClaw's global plugin registry (getGlobalPluginRegistry)
2. Calls the tool's factory with proper context (config from globalThis, canonical
   session key "agent:<agentId>:direct:bridge" for memory agentId resolution)
3. Executes the real tool implementation and returns the AgentToolResult text

This replaces the manual memory_search/memory_get implementations in memory-handlers.ts
with a generic relay that works for any registered OpenClaw tool. The agentId is now
propagated from dispatchToClaude through the MCP server env (AGENT_ID) to /mcp/execute.

The OpenClaw config is stored in globalThis._contractorOpenClawConfig during plugin
registration (index.ts api.config) since getRuntimeConfigSnapshot() uses module-level
state not shared across bundle boundaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 17:09:02 +01:00
f9d43477bf feat: implement memory_search/memory_get MCP tools and fix resume MCP disconnect
Key fixes:
- Pass --mcp-config on every turn (not just first): MCP server exits with each
  claude -p process, so --resume also needs a fresh MCP server
- Pass openclawTools on every turn for the same reason
- Add WORKSPACE env var to MCP server so execute requests include workspace path
- Implement memory_search (keyword search over workspace/memory/*.md + MEMORY.md)
- Implement memory_get (line-range read from workspace memory files)
- Both are workspace-aware, resolved per-request via request body workspace field

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 15:55:44 +01:00
1d9b765a6d docs: document Claude Code identity/SOUL.md conflict analysis (14.9)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 15:46:51 +01:00
fb1ec1b5c4 feat: inject SOUL.md/IDENTITY.md persona and detect workspace context files
- input-filter: scan workspace for SOUL.md, IDENTITY.md, MEMORY.md etc. on each turn
- bootstrap: when these files exist, instruct Claude to Read them at session start
- This gives the contractor agent its OpenClaw persona (SOUL.md embody, IDENTITY.md fill)
- Memory note added to bootstrap when workspace has memory files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 15:41:25 +01:00
76a7931f97 feat: implement MCP proxy for OpenClaw tool access in contractor agent
Complete the MCP tool call chain:
- contractor-agent bridge exposes /mcp/execute endpoint for tool callbacks
- openclaw-mcp-server.mjs proxies OpenClaw tool defs to Claude as MCP tools
- sdk-adapter passes --mcp-config on first turn with all OpenClaw tools
- tool-test plugin registers contractor_echo in globalThis tool handler map
- agent-config-writer auto-sets tools.profile=full so OpenClaw sends tool defs
- Fix --mcp-config arg ordering: prompt must come before <configs...> flag

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-11 13:05:03 +01:00
nav
f7c5875eeb docs: expand Claude contractor design 2026-04-11 06:57:11 +00:00
nav
ca91c1de41 docs: add initial ContractorAgent planning 2026-04-11 06:39:54 +00:00