@aiwg/cli 2026.10.0 → 2026.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agentic/code/providers/capability-matrix.yaml +270 -0
- package/agentic/code/providers/model-capabilities.v1.json +3091 -24
- package/bin/aiwg.mjs +86 -21
- package/dist/src/a2a/codecs.js +9 -0
- package/dist/src/artifacts/corpus-tools/vision-extract.js +105 -15
- package/dist/src/artifacts/repair.js +9 -4
- package/dist/src/channel/manager.mjs +59 -10
- package/dist/src/cli/child-process-result.js +40 -0
- package/dist/src/cli/env.js +38 -16
- package/dist/src/cli/flag-parsing.js +236 -0
- package/dist/src/cli/handlers/decision.js +297 -2
- package/dist/src/cli/handlers/diagnose.js +9 -2
- package/dist/src/cli/handlers/feedback.js +14 -5
- package/dist/src/cli/handlers/init.js +245 -30
- package/dist/src/cli/handlers/mc.js +23 -0
- package/dist/src/cli/handlers/models.js +69 -6
- package/dist/src/cli/handlers/ralph.js +3 -0
- package/dist/src/cli/handlers/refresh.js +16 -0
- package/dist/src/cli/handlers/regenerate.js +14 -0
- package/dist/src/cli/handlers/runtime-info.js +24 -9
- package/dist/src/cli/handlers/script-runner.js +16 -6
- package/dist/src/cli/handlers/session.js +396 -62
- package/dist/src/cli/handlers/sessions.js +146 -40
- package/dist/src/cli/handlers/setup-manifest.js +439 -39
- package/dist/src/cli/handlers/setup.js +139 -39
- package/dist/src/cli/handlers/subcommands.js +3 -1
- package/dist/src/cli/handlers/team.js +18 -8
- package/dist/src/cli/handlers/use.js +13 -0
- package/dist/src/cli/handlers/utilities.js +106 -23
- package/dist/src/cli/prompt-utils.js +72 -0
- package/dist/src/cli/provider-resolution.js +11 -0
- package/dist/src/config/aiwg-config.js +268 -4
- package/dist/src/config/cli.js +146 -19
- package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
- package/dist/src/config/project-artifacts-health.mjs +14 -3
- package/dist/src/config/user-config.js +36 -10
- package/dist/src/decision/adapters/jev.js +80 -28
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/driver.js +4 -0
- package/dist/src/decision/entry.js +38 -6
- package/dist/src/decision/heldout/collector.js +10 -1
- package/dist/src/decision/index.js +3 -0
- package/dist/src/decision/interop.js +60 -8
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/ledger.js +839 -0
- package/dist/src/decision/validate.js +6 -3
- package/dist/src/effects/dispatch-verifier.js +252 -0
- package/dist/src/effects/index.js +1 -0
- package/dist/src/features/installer.js +2 -2
- package/dist/src/flow/graph-metadata.js +5 -0
- package/dist/src/gates/providers/loader.js +3 -1
- package/dist/src/installation/manager-command.mjs +1 -1
- package/dist/src/installation/manager.mjs +17 -2
- package/dist/src/lint/runner.js +2 -2
- package/dist/src/mcp/adapters/codex-runtime.js +360 -56
- package/dist/src/mcp/cli.mjs +587 -77
- package/dist/src/mcp/config-file.mjs +279 -0
- package/dist/src/mcp/config-layers.mjs +348 -0
- package/dist/src/mcp/credentials.mjs +484 -0
- package/dist/src/mcp/grok-build-config.mjs +16 -26
- package/dist/src/mcp/helpers.mjs +106 -2
- package/dist/src/mcp/omp-config.mjs +50 -29
- package/dist/src/mcp/profiles.js +97 -23
- package/dist/src/mcp/profiles.mjs +99 -27
- package/dist/src/mcp/registry.js +264 -57
- package/dist/src/mcp/registry.mjs +198 -45
- package/dist/src/mcp/server.mjs +403 -20
- package/dist/src/mcp/toml-string.mjs +9 -0
- package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
- package/dist/src/mcp/tool-filters.mjs +357 -0
- package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
- package/dist/src/mcp/tools/decision.mjs +58 -0
- package/dist/src/mcp/tools/orchestration.mjs +38 -0
- package/dist/src/mcp/tools/subsystems.mjs +39 -8
- package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
- package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
- package/dist/src/memory/project-registry.js +2 -2
- package/dist/src/mission-protocol/codecs.js +639 -1
- package/dist/src/mission-protocol/types.js +110 -0
- package/dist/src/mission-protocol/worker-status.js +2 -0
- package/dist/src/models/config-loader.js +70 -13
- package/dist/src/models/grok-build-models.js +93 -41
- package/dist/src/models/model-capabilities.v1.json +3091 -24
- package/dist/src/models/model-discovery.js +862 -63
- package/dist/src/models/provider-policy.js +625 -11
- package/dist/src/models/qualification-digest.mjs +17 -0
- package/dist/src/models/resolver.js +259 -0
- package/dist/src/models/worker-direct-readiness.js +529 -0
- package/dist/src/models/worker-dispatch-offer.js +169 -0
- package/dist/src/models/worker-health-readiness.js +131 -0
- package/dist/src/models/worker-installation.mjs +879 -0
- package/dist/src/models/wrapper-route.js +18 -0
- package/dist/src/ops/registry.js +9 -14
- package/dist/src/plugins/standalone-packager.js +4 -3
- package/dist/src/process/bounded-child-process.mjs +387 -0
- package/dist/src/process/cli-command.mjs +75 -0
- package/dist/src/providers/capability-matrix.js +8 -0
- package/dist/src/providers/capability-matrix.yaml +270 -0
- package/dist/src/providers/grok-build-headless.js +43 -21
- package/dist/src/providers/provider-definitions.js +9 -1
- package/dist/src/providers/provider-definitions.mjs +9 -2
- package/dist/src/serve/executor-registry.js +16 -24
- package/dist/src/serve/fleet-mission-conductor.js +9 -0
- package/dist/src/serve/mission-conductor.js +9 -0
- package/dist/src/serve/shared-host-scheduler.js +2 -0
- package/dist/src/serve/worker-invocation.js +538 -0
- package/dist/src/serve/worker-stdin-frame.mjs +107 -0
- package/dist/src/sessions/adapter-registry.js +45 -0
- package/dist/src/sessions/adapters/generic.js +37 -3
- package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
- package/dist/src/sessions/adapters/grokbot.js +423 -24
- package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
- package/dist/src/sessions/adapters/muse.js +68 -29
- package/dist/src/sessions/adapters/pi.js +1 -0
- package/dist/src/sessions/batch-contracts.js +13 -1
- package/dist/src/sessions/batch-import.js +23 -42
- package/dist/src/sessions/candidates.js +47 -11
- package/dist/src/sessions/contracts.js +35 -0
- package/dist/src/sessions/coverage-preflight.js +134 -0
- package/dist/src/sessions/discovery.js +3 -0
- package/dist/src/sessions/grok-build-cli.js +67 -5
- package/dist/src/sessions/importer.js +24 -8
- package/dist/src/sessions/index.js +2 -0
- package/dist/src/sessions/repository.js +54 -13
- package/dist/src/sessions/workspace-discovery.js +99 -37
- package/dist/src/skills/adapters/clawhub.js +10 -8
- package/dist/src/skills/adapters/openclaw.js +10 -7
- package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
- package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
- package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
- package/dist/src/smiths/mcpsmith/example.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
- package/dist/src/storage/backends/logseq.js +68 -7
- package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
- package/dist/src/testing/generators/test-code-generator.js +57 -30
- package/dist/src/uhp/client.js +11 -1
- package/dist/src/uhp/mission.js +5 -0
- package/dist/src/update/checker.mjs +5 -5
- package/dist/src/update/notifier.mjs +54 -2
- package/dist/src/writing/fidelity.js +18 -1
- package/package.json +18 -1
- package/schemas/mission-v1.schema.json +2429 -0
- package/schemas/models/model-catalog.v1.schema.json +301 -0
- package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
- package/schemas/models/model-policy.v1.schema.json +14 -0
- package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
- package/tools/_resolve-impl.mjs +39 -0
- package/tools/agents/deploy-agents.mjs +15 -2
- package/tools/agents/providers/base.mjs +63 -43
- package/tools/agents/providers/grokbot.mjs +11 -12
- package/tools/agents/providers/hermes.mjs +6 -3
- package/tools/agents/providers/omp.mjs +5 -2
- package/tools/agents/providers/openclaw-translator.mjs +19 -5
- package/tools/providers/deepseek-harness-transport.mjs +358 -0
- package/tools/providers/omp-transport.mjs +192 -0
- package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
- package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
- package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
- package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
- package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
- package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
- package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
- package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
- package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
- package/tools/ralph-external/lib/worker-health.mjs +439 -0
- package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude CLI Provider Adapter for External Ralph Loop
|
|
3
|
+
*
|
|
4
|
+
* Extracts existing Claude-specific spawn logic into the ProviderAdapter
|
|
5
|
+
* interface. Direct workers use the version-bound stdin profile below.
|
|
6
|
+
* Legacy Ralph calls retain their prompt transport but deny permission prompts.
|
|
7
|
+
*
|
|
8
|
+
* @implements Plan: Multi-Provider Support for External Ralph Loop
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { join } from 'path';
|
|
12
|
+
import { homedir } from 'os';
|
|
13
|
+
import { ProviderAdapter, registerProvider } from './provider-adapter.mjs';
|
|
14
|
+
import { classifyWorkerHealthFailure, healthEventMatchesBinding } from './worker-health.mjs';
|
|
15
|
+
|
|
16
|
+
/** Help-only evidence: /home/roctinam/.local/bin/claude 2.1.295, 2026-10-09.
|
|
17
|
+
* Exact help excerpts and unavailable controls: docs/providers/claude-direct.md.
|
|
18
|
+
* No inference or credential inspection was performed to establish this syntax.
|
|
19
|
+
*/
|
|
20
|
+
export const interfaceVersion = 1;
|
|
21
|
+
export const CLAUDE_SUPPORTED_VERSIONS = Object.freeze(['2.1.295']);
|
|
22
|
+
export const CLAUDE_DIRECT_EFFORT_VALUES = Object.freeze(['low', 'medium', 'high', 'xhigh', 'max']);
|
|
23
|
+
// Named aliases --help documents (opus/sonnet/fable) plus other known moving
|
|
24
|
+
// pointers: 'default', 'haiku', and the Max-plan 'opusplan' alias. None of
|
|
25
|
+
// these pin an exact snapshot.
|
|
26
|
+
const CLAUDE_MOVING_ALIASES = Object.freeze(['opus', 'sonnet', 'haiku', 'fable', 'default', 'opusplan']);
|
|
27
|
+
const CLAUDE_DATED_SNAPSHOT = /-\d{8}$/;
|
|
28
|
+
// A bare `claude-<family>-<version numbers>` id (e.g. "claude-sonnet-4-5")
|
|
29
|
+
// still resolves to whatever snapshot is currently latest for that family;
|
|
30
|
+
// only a trailing YYYYMMDD date pins an exact, non-moving release.
|
|
31
|
+
const CLAUDE_DATELESS_FAMILY_ID = /^claude-[a-z]+(-\d+)+$/i;
|
|
32
|
+
/** True for any alias or alias-shaped model string that is not a single,
|
|
33
|
+
* exact, non-moving model identifier: named aliases, bracketed modifiers
|
|
34
|
+
* such as "sonnet[1m]", `-latest` suffixes, and dateless `claude-<family>-<version>`
|
|
35
|
+
* pointers. */
|
|
36
|
+
function isClaudeMovingModelPointer(model) {
|
|
37
|
+
return CLAUDE_MOVING_ALIASES.includes(model.toLowerCase()) || /[[\]]/.test(model)
|
|
38
|
+
|| /-latest$/i.test(model)
|
|
39
|
+
|| (CLAUDE_DATELESS_FAMILY_ID.test(model) && !CLAUDE_DATED_SNAPSHOT.test(model));
|
|
40
|
+
}
|
|
41
|
+
const FILE_TOOLS = Object.freeze({
|
|
42
|
+
'read-only': 'Read,Glob,Grep',
|
|
43
|
+
'workspace-write': 'Read,Glob,Grep,Edit,Write',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
/** Reject duplicate keys before vendor-envelope normalization can hide them.
|
|
47
|
+
* JSON.parse first checks syntax; the token walk checks object keys and depth.
|
|
48
|
+
*/
|
|
49
|
+
function strictEvent(line) {
|
|
50
|
+
const value = JSON.parse(line);
|
|
51
|
+
const tokens = line.match(/"(?:[^"\\]|\\.)*"|[{}[\]:,]/g) ?? [];
|
|
52
|
+
const stack = [];
|
|
53
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
54
|
+
const token = tokens[i];
|
|
55
|
+
if (token === '{' || token === '[') {
|
|
56
|
+
stack.push(token === '{' ? new Set() : null);
|
|
57
|
+
if (stack.length > 16) throw new Error('Claude event nesting exceeds bound');
|
|
58
|
+
} else if (token === '}' || token === ']') stack.pop();
|
|
59
|
+
else if (token.startsWith('"') && tokens[i + 1] === ':') {
|
|
60
|
+
const key = JSON.parse(token), keys = stack.at(-1);
|
|
61
|
+
if (!keys || keys.has(key)) throw new Error('Duplicate Claude JSON member');
|
|
62
|
+
keys.add(key);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Extract exactly one successful Claude result envelope from JSONL stdout.
|
|
69
|
+
* This establishes vendor terminal syntax only; trusted exit/stop observation
|
|
70
|
+
* and AIWG claims validation remain required by parseWorkerFinal.
|
|
71
|
+
*/
|
|
72
|
+
export function parseClaudeTerminal(output, maxOutputBytes = 10 * 1024 * 1024) {
|
|
73
|
+
if (!Number.isSafeInteger(maxOutputBytes) || maxOutputBytes < 1 || maxOutputBytes > 10 * 1024 * 1024) {
|
|
74
|
+
throw new Error('Invalid Claude stream bound');
|
|
75
|
+
}
|
|
76
|
+
if (typeof output !== 'string' && !(output instanceof Uint8Array)) throw new Error('Claude output must be bytes or text');
|
|
77
|
+
if (typeof output === 'string' && !output.isWellFormed()) throw new Error('Malformed Claude UTF-8');
|
|
78
|
+
const bytes = typeof output === 'string' ? Buffer.from(output) : output;
|
|
79
|
+
if (bytes.byteLength > maxOutputBytes) throw new Error('Claude stream exceeds byte bound');
|
|
80
|
+
const text = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
81
|
+
let terminal, sessionId;
|
|
82
|
+
for (const line of text.split('\n')) {
|
|
83
|
+
if (!line.trim()) continue;
|
|
84
|
+
if (terminal) throw new Error('Double or trailing Claude terminal');
|
|
85
|
+
const event = strictEvent(line);
|
|
86
|
+
if (!event || typeof event !== 'object' || Array.isArray(event) || typeof event.type !== 'string') {
|
|
87
|
+
throw new Error('Malformed Claude event');
|
|
88
|
+
}
|
|
89
|
+
if (event.session_id !== undefined) {
|
|
90
|
+
if (typeof event.session_id !== 'string' || !event.session_id || (sessionId && sessionId !== event.session_id)) {
|
|
91
|
+
throw new Error('Claude session mismatch');
|
|
92
|
+
}
|
|
93
|
+
sessionId = event.session_id;
|
|
94
|
+
}
|
|
95
|
+
if (event.type === 'error' || event.is_error === true) throw new Error('Claude error event');
|
|
96
|
+
if (!['system', 'assistant', 'user', 'stream_event', 'result'].includes(event.type)) throw new Error('Unknown Claude event');
|
|
97
|
+
if (event.type === 'result') {
|
|
98
|
+
if (event.subtype !== 'success' || event.is_error !== false || typeof event.session_id !== 'string' || !sessionId) throw new Error('Claude terminal failed');
|
|
99
|
+
if (event.structured_output !== undefined) {
|
|
100
|
+
if (!event.structured_output || typeof event.structured_output !== 'object' || Array.isArray(event.structured_output)) {
|
|
101
|
+
throw new Error('Malformed Claude structured output');
|
|
102
|
+
}
|
|
103
|
+
} else if (typeof event.result !== 'string' || !event.result.trim()) throw new Error('Missing Claude final output');
|
|
104
|
+
terminal = event;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (!terminal) throw new Error('Missing Claude terminal');
|
|
108
|
+
return terminal;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export class ClaudeAdapter extends ProviderAdapter {
|
|
112
|
+
observeHealthEvent(event, source, binding) {
|
|
113
|
+
if (source.kind === 'stderr' || !event || typeof event !== 'object') return null;
|
|
114
|
+
if (!healthEventMatchesBinding({ sessionId: event.session_id }, binding)) return null;
|
|
115
|
+
if (source.kind === 'session-log' && (!binding.sessionId || event.session_id !== binding.sessionId)) return null;
|
|
116
|
+
if (event.type === 'system' && event.subtype === 'init') {
|
|
117
|
+
return { kind: 'setup', sessionId: event.session_id };
|
|
118
|
+
}
|
|
119
|
+
// A full assistant message demonstrates accepted model work. Setup and
|
|
120
|
+
// partial token frames alone do not. Completed tools advance progress.
|
|
121
|
+
if (event.type === 'assistant' && event.message?.role === 'assistant') {
|
|
122
|
+
return { kind: 'accepted', key: event.message.id };
|
|
123
|
+
}
|
|
124
|
+
if (event.type === 'user' && Array.isArray(event.message?.content)) {
|
|
125
|
+
const results = event.message.content.filter(part => part.type === 'tool_result' && typeof part.tool_use_id === 'string');
|
|
126
|
+
if (results.some(part => part.is_error === true)) return { kind: 'error', reasonCode: 'tool-failed' };
|
|
127
|
+
if (results.length) return { kind: 'progress', key: results.map(part => part.tool_use_id).sort().join(':') };
|
|
128
|
+
}
|
|
129
|
+
if (event.type === 'stream_event') return { kind: 'activity' };
|
|
130
|
+
if (event.type === 'error') return { ...classifyWorkerHealthFailure(event, { source, provider: 'claude' }), fatal: true };
|
|
131
|
+
if (event.type === 'result') {
|
|
132
|
+
if (event.is_error === true || (typeof event.subtype === 'string' && event.subtype !== 'success')) {
|
|
133
|
+
return { ...classifyWorkerHealthFailure({ type: 'error', message: event.errors?.join(' ') ?? event.result }, { source, provider: 'claude' }), fatal: true };
|
|
134
|
+
}
|
|
135
|
+
if (event.subtype === 'success') return { kind: 'terminal', success: true, key: event.uuid ?? 'result' };
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
/** Direct workers accept only fixed non-prompting, non-bypass controls.
|
|
140
|
+
* --restricted confines file tools; an external OS sandbox remains required.
|
|
141
|
+
* Commands/code, child agents, customizations and inherited MCP are disabled.
|
|
142
|
+
*/
|
|
143
|
+
buildWorkerArgs(invocation, _profile, permissions = {}) {
|
|
144
|
+
const model = invocation?.selected?.model;
|
|
145
|
+
if (typeof model !== 'string' || !model || model.startsWith('-') || model.includes('\0')) {
|
|
146
|
+
throw new Error('Claude worker requires an explicit model');
|
|
147
|
+
}
|
|
148
|
+
if (isClaudeMovingModelPointer(model)) {
|
|
149
|
+
throw new Error('Claude worker requires an exact model identifier, not a moving alias');
|
|
150
|
+
}
|
|
151
|
+
const effort = invocation?.selected?.effort?.value;
|
|
152
|
+
if (!CLAUDE_DIRECT_EFFORT_VALUES.includes(effort)) throw new Error('Claude worker requires an explicit effort');
|
|
153
|
+
const sandbox = permissions.sandbox ?? 'workspace-write';
|
|
154
|
+
if (!Object.hasOwn(FILE_TOOLS, sandbox)) throw new Error('Unavailable Claude sandbox mode');
|
|
155
|
+
if (!['never', 'dontAsk'].includes(permissions.approval ?? 'never')) throw new Error('Claude worker denies prompting or bypass approval modes');
|
|
156
|
+
if (typeof invocation.cwd !== 'string' || !invocation.cwd.startsWith('/') || invocation.cwd.includes('\0')) {
|
|
157
|
+
throw new Error('Claude worker requires an absolute workspace');
|
|
158
|
+
}
|
|
159
|
+
if (typeof invocation.brief !== 'string' || !invocation.brief || invocation.brief.includes('\0')) {
|
|
160
|
+
throw new Error('Claude worker requires a non-empty brief');
|
|
161
|
+
}
|
|
162
|
+
const tools = FILE_TOOLS[sandbox];
|
|
163
|
+
return ['--print', '--input-format', 'text', '--output-format', 'stream-json', '--verbose',
|
|
164
|
+
'--model', model, '--effort', effort, '--permission-mode', 'dontAsk', '--permission-prompts', 'none',
|
|
165
|
+
'--restricted', '--safe-mode', '--strict-mcp-config', '--mcp-config', '{"mcpServers":{}}',
|
|
166
|
+
'--tools', tools, '--allowedTools', tools, '--disallowedTools', 'Agent,Task,Bash,PowerShell,REPL,WebFetch',
|
|
167
|
+
'--disable-slash-commands', '--no-chrome', '--no-session-persistence'];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @returns {string} */
|
|
171
|
+
getBinary() {
|
|
172
|
+
return 'claude';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** @returns {string} */
|
|
176
|
+
getName() {
|
|
177
|
+
return 'claude';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Claude supports all capabilities.
|
|
182
|
+
* @returns {import('./provider-adapter.mjs').ProviderCapabilities}
|
|
183
|
+
*/
|
|
184
|
+
getCapabilities() {
|
|
185
|
+
return {
|
|
186
|
+
streamJson: true,
|
|
187
|
+
sessionResume: true,
|
|
188
|
+
budgetControl: true,
|
|
189
|
+
systemPrompt: true,
|
|
190
|
+
agentMode: true,
|
|
191
|
+
mcpConfig: true,
|
|
192
|
+
maxTurns: true,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Build args for the main headless session (async spawn).
|
|
198
|
+
*
|
|
199
|
+
* Reproduces the exact argument construction from SessionLauncher.buildArgs().
|
|
200
|
+
*
|
|
201
|
+
* @param {import('./provider-adapter.mjs').SessionArgs} options
|
|
202
|
+
* @returns {string[]}
|
|
203
|
+
*/
|
|
204
|
+
buildSessionArgs(options) {
|
|
205
|
+
const args = [
|
|
206
|
+
'--permission-mode', 'dontAsk',
|
|
207
|
+
'--permission-prompts', 'none',
|
|
208
|
+
'--print',
|
|
209
|
+
'--output-format', 'stream-json',
|
|
210
|
+
// stream-json always requires --verbose (CLI enforces this)
|
|
211
|
+
'--verbose',
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
// Session tracking
|
|
215
|
+
if (options.sessionId) {
|
|
216
|
+
args.push('--session-id', options.sessionId);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Model selection (Claude passes through directly)
|
|
220
|
+
if (options.model) {
|
|
221
|
+
args.push('--model', this.mapModel(options.model));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Budget control
|
|
225
|
+
if (options.budget) {
|
|
226
|
+
args.push('--max-budget-usd', String(options.budget));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Max turns control
|
|
230
|
+
if (options.maxTurns) {
|
|
231
|
+
args.push('--max-turns', String(options.maxTurns));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// MCP configuration
|
|
235
|
+
if (options.mcpConfig) {
|
|
236
|
+
const configJson = typeof options.mcpConfig === 'string'
|
|
237
|
+
? options.mcpConfig
|
|
238
|
+
: JSON.stringify(options.mcpConfig);
|
|
239
|
+
args.push('--mcp-config', configJson);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// System prompt injection
|
|
243
|
+
if (options.systemPrompt) {
|
|
244
|
+
args.push('--append-system-prompt', options.systemPrompt);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// The prompt itself (must be last)
|
|
248
|
+
args.push(options.prompt);
|
|
249
|
+
|
|
250
|
+
return args;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Build args for short analysis calls (spawnSync).
|
|
255
|
+
*
|
|
256
|
+
* Used by OutputAnalyzer, StateAssessor, and ClaudePromptGenerator.
|
|
257
|
+
*
|
|
258
|
+
* @param {import('./provider-adapter.mjs').AnalysisArgs} options
|
|
259
|
+
* @returns {string[]}
|
|
260
|
+
*/
|
|
261
|
+
buildAnalysisArgs(options) {
|
|
262
|
+
const args = [
|
|
263
|
+
'--permission-mode', 'dontAsk',
|
|
264
|
+
'--permission-prompts', 'none',
|
|
265
|
+
'--print',
|
|
266
|
+
'--output-format', 'json',
|
|
267
|
+
];
|
|
268
|
+
|
|
269
|
+
// Model selection
|
|
270
|
+
if (options.model) {
|
|
271
|
+
args.push('--model', this.mapModel(options.model));
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Agent flag (for specialized analysis agents)
|
|
275
|
+
if (options.agent) {
|
|
276
|
+
args.push('--agent', options.agent);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// The analysis prompt (must be last)
|
|
280
|
+
args.push(options.prompt);
|
|
281
|
+
|
|
282
|
+
return args;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Claude model names pass through directly.
|
|
287
|
+
* opus/sonnet/haiku are native Claude model aliases.
|
|
288
|
+
*
|
|
289
|
+
* @param {string} genericModel
|
|
290
|
+
* @returns {string}
|
|
291
|
+
*/
|
|
292
|
+
mapModel(genericModel) {
|
|
293
|
+
// Claude accepts opus, sonnet, haiku natively
|
|
294
|
+
return genericModel;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Environment overrides for headless Claude sessions.
|
|
299
|
+
* @returns {Object<string, string>}
|
|
300
|
+
*/
|
|
301
|
+
getEnvOverrides() {
|
|
302
|
+
const overrides = {
|
|
303
|
+
CI: 'true',
|
|
304
|
+
};
|
|
305
|
+
if (process.env.CLAUDE_CODE_DISABLE_1M_CONTEXT === undefined) {
|
|
306
|
+
overrides.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
|
307
|
+
}
|
|
308
|
+
return overrides;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Get Claude session transcript path.
|
|
313
|
+
*
|
|
314
|
+
* Claude stores transcripts at:
|
|
315
|
+
* ~/.claude/projects/{encoded-path}/{session-id}.jsonl
|
|
316
|
+
*
|
|
317
|
+
* Path encoding: Replace `/` with `-`, prepend `-`
|
|
318
|
+
* Example: /foo/bar → -foo-bar
|
|
319
|
+
*
|
|
320
|
+
* @param {string} sessionId
|
|
321
|
+
* @param {string} workingDir
|
|
322
|
+
* @returns {string}
|
|
323
|
+
*/
|
|
324
|
+
getTranscriptPath(sessionId, workingDir) {
|
|
325
|
+
const encodedPath = workingDir.replace(/\//g, '-');
|
|
326
|
+
return join(
|
|
327
|
+
homedir(),
|
|
328
|
+
'.claude',
|
|
329
|
+
'projects',
|
|
330
|
+
encodedPath,
|
|
331
|
+
`${sessionId}.jsonl`
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** Direct candidate extraction is separate from legacy regex analysis parsing.
|
|
337
|
+
* Only the direct worker profile enforces Claude's real stream-json terminal
|
|
338
|
+
* shape (subtype/is_error/session_id). Legacy `claude` callers, including
|
|
339
|
+
* Ralph's existing analysis/session paths and the generic cross-provider
|
|
340
|
+
* worker-finals contract, keep the shared, provider-agnostic strict parser
|
|
341
|
+
* inherited from ProviderAdapter.
|
|
342
|
+
*/
|
|
343
|
+
export class ClaudeDirectAdapter extends ClaudeAdapter {
|
|
344
|
+
async parseWorkerFinal(output, options = {}) {
|
|
345
|
+
// MCP envelopes retain the shared strict parser. Every other input must be
|
|
346
|
+
// Claude's own stream-json transcript with a validated terminal result: the
|
|
347
|
+
// direct worker never writes a separate final file, so a bare claims
|
|
348
|
+
// document (e.g. a worker-written final file handed over as 'json') is
|
|
349
|
+
// rejected instead of bypassing terminal validation.
|
|
350
|
+
if (options.format === 'mcp') return super.parseWorkerFinal(output, options);
|
|
351
|
+
const terminal = parseClaudeTerminal(output, options.maxOutputBytes);
|
|
352
|
+
const final = terminal.structured_output === undefined ? terminal.result : JSON.stringify(terminal.structured_output);
|
|
353
|
+
return super.parseWorkerFinal(final, { ...options, format: 'json' });
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** Synchronous candidate extraction for the installed driver's existing hook.
|
|
357
|
+
* Strict completion always reparses original bytes via parseWorkerFinal.
|
|
358
|
+
* Single normalized claims files also pass through this hook.
|
|
359
|
+
*/
|
|
360
|
+
parseOutput(stdout) {
|
|
361
|
+
try {
|
|
362
|
+
const text = String(stdout).trim();
|
|
363
|
+
if (!text.includes('\n')) {
|
|
364
|
+
const value = strictEvent(text);
|
|
365
|
+
if (value && typeof value === 'object' && !Array.isArray(value) && value.type === undefined) return value;
|
|
366
|
+
}
|
|
367
|
+
const terminal = parseClaudeTerminal(stdout);
|
|
368
|
+
if (terminal.structured_output !== undefined) return terminal.structured_output;
|
|
369
|
+
try {
|
|
370
|
+
const claims = strictEvent(terminal.result);
|
|
371
|
+
if (claims && typeof claims === 'object' && !Array.isArray(claims)) return claims;
|
|
372
|
+
} catch { /* Legacy analysis results may contain plain text. */ }
|
|
373
|
+
return terminal;
|
|
374
|
+
} catch { return null; }
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Legacy imports register in Ralph under claude; installed workers use the
|
|
379
|
+
// distinct claude-direct factory so either load order is safe. Digest-addressed
|
|
380
|
+
// imports register only through the loader's approved factory.
|
|
381
|
+
if (!new URL(import.meta.url).searchParams.has('sha256')) {
|
|
382
|
+
registerProvider('claude', () => new ClaudeAdapter());
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function register(factory) {
|
|
386
|
+
factory('claude-direct', () => new ClaudeDirectAdapter());
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export default ClaudeAdapter;
|