mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-22 04:51:41 +00:00
Embedded runs targeting a CLI runtime provider fall through to the openclaw
harness and call the provider API directly with the runtime's credentials
(cli_runtime_passthrough_openclaw). Anthropic routes direct anthropic-messages
calls on subscription OAuth tokens to metered extra-usage billing — this is
long-standing behavior, not a recent change. Without extra-usage balance,
every such run (e.g. active-memory recall) fails with a billing error; with
extra-usage enabled, the run silently draws paid metered usage instead of the
plan limits the CLI runtime was configured for. Only CLI-backed execution
runs on plan limits for those credentials.
Add an opt-in RunEmbeddedAgentParams.cliBackendDispatch: "subscription-auth"
that dispatches the run through runCliAgent as a one-shot turn when the
provider is claude-cli, a CLI backend is registered, and the ordered auth
profile selection for the passthrough resolves to a subscription (oauth/token)
credential or nothing rather than an API key; resolution stays on stored
credential metadata, with no credential materialization or refresh on the
per-turn path. The dispatch translates toolsAllow into the selectable-backend
surface (native: [], allowlisted loopback MCP tools; wildcard allowlists stay
MCP-only), runs with a fresh CLI process (no live-session reuse; session-
scoped bundle-MCP retirement on run end rather than the process-wide loopback
close), bridges CLI tool result events to onAgentToolResult with native-path
semantics (normalizeToolName + isToolResultError), and drops CLI session
bindings from the result.
The selectable-backend MCP list now also bounds the loopback MCP grant
server-side: the grant carries a per-run gateway tool allowlist enforced in
scoped tool resolution, so tools outside the run's allowlist can be neither
listed nor called even under CLI bypass permission modes where
--allowedTools is advisory.
active-memory recall opts in so recall works on claude-cli subscription-only
instances and stops drawing metered extra usage where it previously could.
Scoped to claude-cli; other CLI runtimes keep the passthrough until their
direct-API contract is verified.
The dispatch also mirrors the run into the run's session transcript through the session
accessor (user turn, tool call/result records as they stream, final assistant
snapshot at run end) so transcript consumers keep parity with embedded runs:
active-memory's persistTranscripts, timeout partial-text salvage, and the
live terminal-search watcher that polls the session file mid-run.
Post-review hardening: canonical anthropic/<model> refs whose configured
agentRuntime is claude-cli resolve through the runtime policy before the
dispatch gate (they previously stayed on the failing passthrough); restricted
dispatches serve an exclusive loopback-only MCP bundle so user/plugin MCP
servers stay outside the run's tool universe; and the transcript recorder
flushes the latest assistant snapshot the moment the run aborts, so timeout
salvage sees partial text even while the killed CLI child is still settling.
Recalls routed to the claude-cli runtime default to a 45s budget (measured
CLI-dispatched runs take 14-20s, over the plain 15s default); explicit
timeoutMs config always wins.
Transcript mirror keeps bare-array tool_result content (claude stream-json
echoes MCP results without a {content} wrapper); dropping it classified every
successful recall as no_relevant_memory.
CLI dispatch resolves inside session/global lane admission so dispatched
runs obey the same lifecycle, placement, and concurrency gates as native
embedded runs.
LOC-ratchet offsets move the loopback grant-context builders to
cli-runner/mcp-grant-context.ts and dedupe the run/prep stage-summary
emitters into attempt-stage-timing.ts; unused type exports dropped and the
now-used stream-message baseline entry removed.
The recall timeout default now consumes the runner's own dispatch
eligibility through a new plugin-runtime seam
(agent.resolveCliBackendDispatchEligibility): API-key and missing-backend
routes keep the passthrough and its plain 15s default.
Eligibility honors an explicitly pinned authProfileId (the credential the
run executes on) before ordered profile selection, in both directions.
Transcript mirror composes buildAssistantMessage + buildUsageWithNoCost
directly; main trimmed the zero-usage wrapper export (ab0ccc244b) before
this branch's usage landed.
Dispatch eligibility is provider-owned: the anthropic plugin's claude-cli
backend declares CliBackendPlugin.subscriptionAuthDispatch and core reads
the registered descriptor instead of a core provider allowlist.
Dispatch fails closed on tool policy (only non-empty named allowlists are
expressible on the CLI surface; deny-all, wildcards, absent allowlists, and
disableTools/modelRun keep the passthrough), threads the pinned
authProfileId into CLI runtime resolution, and emits onExecutionStarted at
the admitted dispatch boundary.
197 lines
8.7 KiB
JSON
197 lines
8.7 KiB
JSON
{
|
|
"id": "active-memory",
|
|
"activation": {
|
|
"onStartup": true
|
|
},
|
|
"name": "Active Memory",
|
|
"description": "Runs a bounded blocking memory sub-agent before eligible conversational replies and injects relevant memory into prompt context.",
|
|
"configSchema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"enabled": { "type": "boolean" },
|
|
"agents": {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
},
|
|
"model": { "type": "string" },
|
|
"modelFallback": { "type": "string" },
|
|
"modelFallbackPolicy": {
|
|
"type": "string",
|
|
"enum": ["default-remote", "resolved-only"]
|
|
},
|
|
"allowedChatTypes": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string",
|
|
"enum": ["direct", "group", "channel", "explicit"]
|
|
}
|
|
},
|
|
"allowedChatIds": {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
},
|
|
"deniedChatIds": {
|
|
"type": "array",
|
|
"items": { "type": "string" }
|
|
},
|
|
"thinking": {
|
|
"type": "string",
|
|
"enum": ["off", "minimal", "low", "medium", "high", "xhigh", "adaptive"]
|
|
},
|
|
"fastMode": {
|
|
"anyOf": [
|
|
{ "type": "boolean" },
|
|
{ "type": "string", "enum": ["auto"] }
|
|
]
|
|
},
|
|
"timeoutMs": { "type": "integer", "minimum": 250, "maximum": 120000 },
|
|
"setupGraceTimeoutMs": { "type": "integer", "minimum": 0, "maximum": 30000 },
|
|
"queryMode": {
|
|
"type": "string",
|
|
"enum": ["message", "recent", "full"]
|
|
},
|
|
"promptStyle": {
|
|
"type": "string",
|
|
"enum": [
|
|
"balanced",
|
|
"strict",
|
|
"contextual",
|
|
"recall-heavy",
|
|
"precision-heavy",
|
|
"preference-only"
|
|
]
|
|
},
|
|
"toolsAllow": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "string",
|
|
"pattern": "^(?!\\*$)(?![Gg][Rr][Oo][Uu][Pp]:).+"
|
|
},
|
|
"maxItems": 32
|
|
},
|
|
"promptOverride": { "type": "string" },
|
|
"promptAppend": { "type": "string" },
|
|
"maxSummaryChars": { "type": "integer", "minimum": 40, "maximum": 1000 },
|
|
"recentUserTurns": { "type": "integer", "minimum": 0, "maximum": 4 },
|
|
"recentAssistantTurns": { "type": "integer", "minimum": 0, "maximum": 3 },
|
|
"recentUserChars": { "type": "integer", "minimum": 40, "maximum": 1000 },
|
|
"recentAssistantChars": { "type": "integer", "minimum": 40, "maximum": 1000 },
|
|
"logging": { "type": "boolean" },
|
|
"persistTranscripts": { "type": "boolean" },
|
|
"transcriptDir": { "type": "string" },
|
|
"cacheTtlMs": { "type": "integer", "minimum": 1000, "maximum": 120000 },
|
|
"circuitBreakerMaxTimeouts": { "type": "integer", "minimum": 1, "maximum": 20 },
|
|
"circuitBreakerCooldownMs": { "type": "integer", "minimum": 5000, "maximum": 600000 },
|
|
"qmd": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"searchMode": {
|
|
"type": "string",
|
|
"enum": ["inherit", "search", "vsearch", "query"]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"uiHints": {
|
|
"enabled": {
|
|
"label": "Active Memory Recall",
|
|
"help": "Globally enable or pause Active Memory recall while keeping the plugin command available."
|
|
},
|
|
"agents": {
|
|
"label": "Target Agents",
|
|
"help": "Explicit agent ids that may use active memory."
|
|
},
|
|
"model": {
|
|
"label": "Memory Model",
|
|
"help": "Provider/model used for the blocking memory sub-agent."
|
|
},
|
|
"modelFallback": {
|
|
"label": "Fallback Memory Model",
|
|
"help": "Optional provider/model to use if no explicit plugin model, session model, or agent primary model resolves."
|
|
},
|
|
"modelFallbackPolicy": {
|
|
"label": "Model Fallback Policy",
|
|
"help": "Deprecated compatibility field. modelFallback is only the chain-resolution last resort when no explicit plugin model, session model, or agent primary model resolves; it is not runtime failover."
|
|
},
|
|
"allowedChatTypes": {
|
|
"label": "Allowed Chat Types",
|
|
"help": "Choose which session types may run Active Memory. Defaults to direct-message style sessions only, but explicit portal/webchat sessions can also be enabled."
|
|
},
|
|
"allowedChatIds": {
|
|
"label": "Allowed Chat IDs",
|
|
"help": "Optional explicit allowlist of chat/user IDs (e.g. Feishu chat_id oc_xxx, open_id ou_xxx, Telegram chat id, Slack channel id). When non-empty, Active Memory only runs for sessions whose conversation id is in the list, across **every** chat type at once (direct, group, channel). Setting this narrows every allowed chat type simultaneously — if you want 'all directs + only specific groups', use allowedChatTypes: ['group'] + allowedChatIds: [<group ids>] and rely on direct chats being matched via the direct session id (e.g. the user's open_id) instead. Leave empty to fall back to allowedChatTypes alone."
|
|
},
|
|
"deniedChatIds": {
|
|
"label": "Denied Chat IDs",
|
|
"help": "Optional explicit denylist of chat/user IDs. Sessions whose resolved conversation id matches the list are skipped even when the chat type is allowed. Applied after allowedChatIds."
|
|
},
|
|
"timeoutMs": {
|
|
"label": "Timeout (ms)",
|
|
"help": "Recall work budget on the main lane. Defaults to 15000 ms, or 45000 ms when the recall is eligible for CLI-backend dispatch on subscription-only claude-cli auth (CLI-backed recalls include process startup); API-key routes keep 15000 ms. Explicit values always win. Before recall, the hook allows up to 1500 ms for session/config preflight. After recall starts, it reserves another fixed 1500 ms only for abort settlement and transcript recovery."
|
|
},
|
|
"setupGraceTimeoutMs": {
|
|
"label": "Setup Grace Timeout (ms)",
|
|
"help": "Advanced: extra recall-work budget for cold embedded-run setup. Defaults to 0. The separate 1500 ms preflight cap and 1500 ms post-recall completion allowance still apply."
|
|
},
|
|
"queryMode": {
|
|
"label": "Query Mode",
|
|
"help": "Choose whether the blocking memory sub-agent sees only the latest user message, a small recent tail, or the full conversation."
|
|
},
|
|
"promptStyle": {
|
|
"label": "Prompt Style",
|
|
"help": "Choose how eager or strict the blocking memory sub-agent should be when deciding whether to return memory."
|
|
},
|
|
"toolsAllow": {
|
|
"label": "Allowed Memory Tools",
|
|
"help": "Advanced: tool names the blocking memory sub-agent may use. Defaults to memory_search and memory_get, or memory_recall when plugins.slots.memory selects memory-lancedb; configure this for other non-core memory providers. Wildcards, group entries, and core agent tools are ignored."
|
|
},
|
|
"thinking": {
|
|
"label": "Thinking Override",
|
|
"help": "Advanced: optional thinking level for the blocking memory sub-agent. Defaults to off for speed."
|
|
},
|
|
"fastMode": {
|
|
"label": "Fast Mode Override",
|
|
"help": "Advanced: optional fast-mode override for the blocking memory sub-agent. Leave unset to inherit normal agent, session, and model defaults."
|
|
},
|
|
"promptOverride": {
|
|
"label": "Prompt Override",
|
|
"help": "Advanced: replace the default Active Memory sub-agent instructions. Conversation context is still appended."
|
|
},
|
|
"promptAppend": {
|
|
"label": "Prompt Append",
|
|
"help": "Advanced: append extra operator instructions after the default Active Memory sub-agent instructions."
|
|
},
|
|
"maxSummaryChars": {
|
|
"label": "Max Summary Characters",
|
|
"help": "Maximum total characters allowed in the active-memory summary."
|
|
},
|
|
"logging": {
|
|
"label": "Enable Logging",
|
|
"help": "Emit active memory timing and result logs."
|
|
},
|
|
"circuitBreakerMaxTimeouts": {
|
|
"label": "Circuit Breaker Max Timeouts",
|
|
"help": "Skip recall after this many consecutive timeouts for the same agent/model. Resets on a successful recall or after the cooldown expires. Default: 3."
|
|
},
|
|
"circuitBreakerCooldownMs": {
|
|
"label": "Circuit Breaker Cooldown (ms)",
|
|
"help": "How long to skip recall after the circuit breaker trips, in milliseconds. Default: 60000 (1 minute)."
|
|
},
|
|
"persistTranscripts": {
|
|
"label": "Persist Transcripts",
|
|
"help": "Keep blocking memory sub-agent session transcripts on disk in a separate plugin-owned directory."
|
|
},
|
|
"transcriptDir": {
|
|
"label": "Transcript Directory",
|
|
"help": "Relative directory under the agent sessions folder used when transcript persistence is enabled."
|
|
},
|
|
"qmd.searchMode": {
|
|
"label": "QMD Search Mode",
|
|
"help": "Override the QMD search mode used by the blocking memory sub-agent. Defaults to fast lexical search; use inherit to match the main memory backend setting."
|
|
}
|
|
}
|
|
}
|