Compare commits

..

3 Commits

Author SHA1 Message Date
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
2 changed files with 32 additions and 22 deletions

View File

@@ -17,7 +17,8 @@ export type OpenAITool = {
export type ClaudeDispatchOptions = { export type ClaudeDispatchOptions = {
prompt: string; prompt: string;
/** System prompt passed via --system-prompt on every invocation (stateless, not stored in session) */ /** Appended to Claude Code's built-in system prompt via --append-system-prompt on every invocation.
* Stateless: not persisted in session file, fully replaces any prior appended content on resume. */
systemPrompt?: string; systemPrompt?: string;
workspace: string; workspace: string;
agentId?: string; agentId?: string;
@@ -97,7 +98,7 @@ export async function* dispatchToClaude(
workspace, workspace,
agentId = "", agentId = "",
resumeSessionId, resumeSessionId,
permissionMode = "bypassPermissions", permissionMode = "default",
openclawTools, openclawTools,
bridgePort = 18800, bridgePort = 18800,
bridgeApiKey = "", bridgeApiKey = "",
@@ -111,15 +112,15 @@ export async function* dispatchToClaude(
prompt, prompt,
"--output-format", "stream-json", "--output-format", "stream-json",
"--verbose", "--verbose",
"--permission-mode", permissionMode, "--allowedTools", "Bash Edit Write Read Glob Grep WebFetch WebSearch NotebookEdit Monitor TodoWrite mcp__openclaw__*",
"--dangerously-skip-permissions",
]; ];
// --system-prompt is stateless (not persisted in session file) and fully // --append-system-prompt appends to Claude Code's built-in system prompt rather
// replaces any prior system prompt on each invocation, including resumes. // than replacing it, preserving the full agent SDK instructions (tool use behavior,
// We pass it every turn so skills/persona stay current. // memory management, etc.). The appended bootstrap (persona + skills) is stateless:
// not persisted in the session file, takes effect every invocation including resumes.
if (systemPrompt) { if (systemPrompt) {
args.push("--system-prompt", systemPrompt); args.push("--append-system-prompt", systemPrompt);
} }
if (resumeSessionId) { if (resumeSessionId) {

View File

@@ -321,21 +321,8 @@ export function createBridgeServer(config: BridgeServerConfig): http.Server {
return; return;
} }
// Find the tool registration by name // Build tool execution context (needed before tool lookup for factory instantiation).
const toolReg = pluginRegistry.tools.find((t) => t.names.includes(toolName));
if (!toolReg) {
sendJson(res, 200, { error: `Tool '${toolName}' not registered in OpenClaw plugin registry` });
return;
}
// Build tool execution context.
// config is required by memory tools (resolveMemoryToolContext checks it).
// Read from globalThis where index.ts stores api.config on every registration.
// sessionKey must be in OpenClaw's canonical format "agent:<agentId>:<rest>" so that
// parseAgentSessionKey() can extract the agentId for memory tool context resolution.
const liveConfig = (_G["_contractorOpenClawConfig"] ?? null) as Record<string, unknown> | null; const liveConfig = (_G["_contractorOpenClawConfig"] ?? null) as Record<string, unknown> | null;
// Construct a canonical session key so memory tools can resolve the agentId from it.
// Format: "agent:<agentId>:direct:bridge"
const canonicalSessionKey = execAgentId ? `agent:${execAgentId}:direct:bridge` : undefined; const canonicalSessionKey = execAgentId ? `agent:${execAgentId}:direct:bridge` : undefined;
const toolCtx = { const toolCtx = {
config: liveConfig ?? undefined, config: liveConfig ?? undefined,
@@ -345,6 +332,28 @@ export function createBridgeServer(config: BridgeServerConfig): http.Server {
sessionKey: canonicalSessionKey, sessionKey: canonicalSessionKey,
}; };
// Find tool by name. Some plugins register tools via factory functions without
// declaring names upfront (names array is empty). For those, instantiate the
// factory and match by the returned tool's .name property.
let toolReg = pluginRegistry.tools.find((t) => t.names.includes(toolName));
if (!toolReg) {
for (const candidate of pluginRegistry.tools) {
if (candidate.names.length > 0) continue;
try {
const inst = candidate.factory(toolCtx);
const items = Array.isArray(inst) ? inst : [inst];
if (items.some((t) => (t as { name?: string }).name === toolName)) {
toolReg = candidate;
break;
}
} catch { /* skip */ }
}
}
if (!toolReg) {
sendJson(res, 200, { error: `Tool '${toolName}' not registered in OpenClaw plugin registry` });
return;
}
// Instantiate the tool via its factory // Instantiate the tool via its factory
const toolOrTools = toolReg.factory(toolCtx); const toolOrTools = toolReg.factory(toolCtx);
const toolInstance = Array.isArray(toolOrTools) const toolInstance = Array.isArray(toolOrTools)