Files
ContractorAgent/plugin/core/openclaw/agent-config-writer.ts
hzhang 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

59 lines
2.4 KiB
TypeScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
type OpenClawConfig = Record<string, unknown>;
type AgentEntry = Record<string, unknown>;
const CLAUDE_CONTRACTOR_MODEL = "contractor-agent/contractor-claude-bridge";
const GEMINI_CONTRACTOR_MODEL = "contractor-agent/contractor-gemini-bridge";
function readConfig(): { config: OpenClawConfig; configPath: string } {
const configPath = path.join(os.homedir(), ".openclaw", "openclaw.json");
const raw = fs.readFileSync(configPath, "utf8");
return { config: JSON.parse(raw) as OpenClawConfig, configPath };
}
function writeConfig(configPath: string, config: OpenClawConfig): void {
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
}
/**
* Verify that an agent is configured with the contractor bridge model.
* Called after createBaseAgent() — the model is already set by `openclaw agents add`,
* so this is just a sanity check that throws if something went wrong.
*
* We intentionally do NOT write a custom `runtime.type` — OpenClaw's schema only
* allows "embedded" or "acp", and contractor agents are identified by their model.
*/
function markContractorAgent(agentId: string, expectedModel: string, configPath: string, config: OpenClawConfig): void {
const agents = (config.agents as { list?: AgentEntry[] } | undefined)?.list;
if (!agents) throw new Error("agents.list not found in openclaw.json");
const agent = agents.find((a) => a.id === agentId);
if (!agent) throw new Error(`agent ${agentId} not found in openclaw.json`);
if (agent.model !== expectedModel) {
throw new Error(
`agent ${agentId} model is "${String(agent.model)}", expected "${expectedModel}"`,
);
}
// Ensure tools are enabled so OpenClaw includes tool definitions in every
// chat completions request — needed for the MCP proxy to expose them to Claude/Gemini.
if (!agent.tools) {
agent.tools = { profile: "full" };
writeConfig(configPath, config);
}
}
export function markAgentAsClaudeContractor(agentId: string, _workspace: string): void {
const { config, configPath } = readConfig();
markContractorAgent(agentId, CLAUDE_CONTRACTOR_MODEL, configPath, config);
}
export function markAgentAsGeminiContractor(agentId: string, _workspace: string): void {
const { config, configPath } = readConfig();
markContractorAgent(agentId, GEMINI_CONTRACTOR_MODEL, configPath, config);
}