@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,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Muse Code (`muse`) provider adapter for headless External Ralph sessions.
|
|
3
|
+
*
|
|
4
|
+
* Drives `muse exec` — Meta's documented headless surface — with JSONL events
|
|
5
|
+
* (`--json`), headless resume (`--session-id`), and transcript/export
|
|
6
|
+
* (`muse export`) support. Optional and post-experimental per
|
|
7
|
+
* `docs/architecture/adr-muse-provider-target.md` ("Headless Ralph"): no
|
|
8
|
+
* `muse exec` contract is assumed beyond what is evidenced below, and the
|
|
9
|
+
* adapter disables entirely via `AIWG_MUSE_RALPH_ENABLED=0` without affecting
|
|
10
|
+
* `aiwg use --provider muse` (the deploy/writer path lives in `src/` and
|
|
11
|
+
* never consults this registry).
|
|
12
|
+
*
|
|
13
|
+
* Flag evidence (all Meta docs at https://dev.meta.ai/docs/muse-code/
|
|
14
|
+
* unless noted):
|
|
15
|
+
* - `muse exec [OPTIONS] [PROMPT]` — headless entrypoint; "takes one
|
|
16
|
+
* prompt, runs it to completion, and exits" (extending). The prompt is
|
|
17
|
+
* positional and must come last. Options are parsed by `exec`, not by
|
|
18
|
+
* the `muse` root — flags placed before the sub-command are silently
|
|
19
|
+
* ignored — so every adapter-built argv starts with `exec`.
|
|
20
|
+
* - `--json` — "emit JSONL events" (extending; configuration lists it as
|
|
21
|
+
* headless-only).
|
|
22
|
+
* - `--prompt-file <path>` — headless-only (configuration).
|
|
23
|
+
* - `--max-model-steps <n>` — headless-only, "cap the run" (configuration).
|
|
24
|
+
* - `--session-id <uuid>` — "To continue an interrupted job
|
|
25
|
+
* non-interactively, use `exec` with the session id":
|
|
26
|
+
* `muse exec --session-id <uuid> "Continue the task."` (extending).
|
|
27
|
+
* There is no headless resume flag; re-passing the id continues the
|
|
28
|
+
* session. `muse resume` is interactive-only.
|
|
29
|
+
* - `--model <id>`, `--reasoning-effort <level>` — common to both launch
|
|
30
|
+
* surfaces (configuration).
|
|
31
|
+
* - `--approval-mode <mode>` — common to both; "`muse exec` accepts both
|
|
32
|
+
* approval flags" (configuration). Opt-in only: the adapter sets NO
|
|
33
|
+
* approval posture by default (#230 out of scope: `--yolo` defaults).
|
|
34
|
+
* - `muse export --session <uuid> --out <path>` — offline,
|
|
35
|
+
* byte-deterministic transcript projection; `--last` and `--redacted`
|
|
36
|
+
* skip the picker / redact payload strings (cookbook:
|
|
37
|
+
* audit-agent-sessions). Export "never modifies the log".
|
|
38
|
+
* - Exit codes: 0 = turn completes, 1 = fails or is cancelled (including a
|
|
39
|
+
* `--max-model-steps` limit), 2 = usage error, 130/143 on SIGINT/SIGTERM
|
|
40
|
+
* (extending).
|
|
41
|
+
* - Sandbox: Muse Code ships an OS-enforced sandbox (bubblewrap/seccomp +
|
|
42
|
+
* namespaces on Linux, seatbelt on macOS) that is ON by default; `--yolo`
|
|
43
|
+
* disables approval AND sandboxing — "use it only on trusted code in a
|
|
44
|
+
* disposable, isolated container" (extending). The adapter never passes
|
|
45
|
+
* `--yolo`. See docs/providers/muse-ralph-exec.md for the CI sandbox
|
|
46
|
+
* (bubblewrap/user-namespace) requirements this implies.
|
|
47
|
+
*
|
|
48
|
+
* Verified against an installed Muse Code 1.4.0 (2026-09-25):
|
|
49
|
+
* - `--json` emits one record envelope per line: `{schema_version, id,
|
|
50
|
+
* stream, sequence, recorded_at, record_type, durability, causation_id,
|
|
51
|
+
* payload_type, payload_schema_version, payload}` — the same envelope as
|
|
52
|
+
* the native session log. `run.output.delta` carries streamed `text`;
|
|
53
|
+
* the run ends with `run.terminal.<state>` whose payload carries
|
|
54
|
+
* `terminal` (`completed`/`failed`), the final `text`, and a `reason`
|
|
55
|
+
* on failure. `parseOutput()` reads exactly those fields.
|
|
56
|
+
* - Exit codes 0 (completed), 1 (run failed, e.g. an unknown model:
|
|
57
|
+
* "model `x` does not exist or you lack access"), 2 (usage error).
|
|
58
|
+
* - `--session-id <uuid>` pins the new session's id (the native log lands
|
|
59
|
+
* in `$XDG_DATA_HOME/muse/sessions/YYYY/MM/DD/<uuid>/`), and re-passing
|
|
60
|
+
* the same id continues that session with its context.
|
|
61
|
+
* - A prompt starting with `-` is parsed as an option (exit 2) unless it
|
|
62
|
+
* follows `--`, so the positional prompt is always preceded by `--`.
|
|
63
|
+
* - Plain mode (no `--json`) prints only the final answer text on stdout.
|
|
64
|
+
*
|
|
65
|
+
* Not assumed:
|
|
66
|
+
* - Transcript paths. The native log root is evidenced, but its layout is
|
|
67
|
+
* internal (retained frames, omission markers), so `getTranscriptPath()`
|
|
68
|
+
* returns null and transcripts come from `muse export`, the documented
|
|
69
|
+
* projection.
|
|
70
|
+
* - A pinned CLI version. Unlike pi, no qualified-version list exists yet;
|
|
71
|
+
* `isAvailable()` checks only that `muse --version` exits 0.
|
|
72
|
+
*
|
|
73
|
+
* @issue #230
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
import { ProviderAdapter, registerProvider } from './provider-adapter.mjs';
|
|
77
|
+
import { classifyWorkerHealthFailure, healthEventMatchesBinding } from './worker-health.mjs';
|
|
78
|
+
import path from 'node:path';
|
|
79
|
+
import { readFileSync } from 'node:fs';
|
|
80
|
+
|
|
81
|
+
const museEffortValues = JSON.parse(readFileSync(new URL('../../../agentic/code/providers/model-capabilities.v1.json', import.meta.url), 'utf8')).providers.muse.effortValues;
|
|
82
|
+
|
|
83
|
+
// Syntax inspected only; neither help nor echo qualifies authenticated execution.
|
|
84
|
+
export const MUSE_DIRECT_VERSION = '1.4.4-R5419.1';
|
|
85
|
+
|
|
86
|
+
/** Quota observations are candidates, not a retry/admission decision. Unknown
|
|
87
|
+
* scope/reset stay null; the caller binds the account/quota domain separately. */
|
|
88
|
+
export function museQuotaEvidence(payload) {
|
|
89
|
+
const failure = classifyWorkerHealthFailure({ type: 'error', message: payload?.reason }, { provider: 'muse' });
|
|
90
|
+
if (failure?.reasonCode !== 'RATE_LIMIT') return null;
|
|
91
|
+
const text = typeof payload.reason === 'string' ? payload.reason : '';
|
|
92
|
+
const reset = typeof payload.reset_at === 'string' ? payload.reset_at
|
|
93
|
+
: /reset(?:[_ -]at)?["\s:=]+(\d{4}-\d\d-\d\dT[\d:.]+Z)/i.exec(text)?.[1];
|
|
94
|
+
return { reasonCode: failure.reasonCode,
|
|
95
|
+
quotaScope: typeof payload.quota_scope === 'string' && /^[a-zA-Z0-9_.:-]{1,128}$/.test(payload.quota_scope) ? payload.quota_scope : null,
|
|
96
|
+
resetAt: reset && Number.isFinite(Date.parse(reset)) ? new Date(reset).toISOString() : null,
|
|
97
|
+
retryAfterSeconds: failure.retryAfterSeconds ?? null };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Whether the muse exec Ralph adapter is registered. Enabled by default;
|
|
102
|
+
* set `AIWG_MUSE_RALPH_ENABLED` to `0`, `false`, `no`, or `off` to disable
|
|
103
|
+
* the adapter without affecting `aiwg use --provider muse`.
|
|
104
|
+
*
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
export function isMuseRalphEnabled() {
|
|
108
|
+
const raw = String(process.env.AIWG_MUSE_RALPH_ENABLED ?? '').trim().toLowerCase();
|
|
109
|
+
return !['0', 'false', 'no', 'off'].includes(raw);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export class MuseAdapter extends ProviderAdapter {
|
|
113
|
+
observeHealthEvent(event, source, binding) {
|
|
114
|
+
if (source.kind === 'stderr' || !event || typeof event !== 'object' || event.record_type !== 'event') return null;
|
|
115
|
+
if (event.stream?.kind !== 'session' || typeof event.stream.id !== 'string') return null;
|
|
116
|
+
const payload = event.payload;
|
|
117
|
+
if (!payload || typeof payload !== 'object' || typeof payload.run_id !== 'string' || !payload.run_id) return null;
|
|
118
|
+
if (!healthEventMatchesBinding({ sessionId: event.stream.id, runId: payload.run_id }, binding)) return null;
|
|
119
|
+
// Native session records require an already bound session and run. Reading
|
|
120
|
+
// a log from the same provider does not authorize unrelated session traffic.
|
|
121
|
+
if (source.kind === 'session-log' && (!binding.sessionId || event.stream.id !== binding.sessionId
|
|
122
|
+
|| !binding.runId || payload.run_id !== binding.runId)) return null;
|
|
123
|
+
if (event.payload_type === 'run.lifecycle.started' && payload.kind === 'run_started') {
|
|
124
|
+
return { kind: 'accepted', sessionId: event.stream.id, runId: payload.run_id, key: event.id };
|
|
125
|
+
}
|
|
126
|
+
// A terminal/delta cannot establish a new session/run binding.
|
|
127
|
+
if (!binding.sessionId || !binding.runId) return null;
|
|
128
|
+
if (event.payload_type === 'run.output.delta' && payload.kind === 'run_output_delta' && typeof payload.text === 'string') {
|
|
129
|
+
return { kind: 'activity', key: event.id };
|
|
130
|
+
}
|
|
131
|
+
if (event.payload_type === 'run.terminal.completed' && payload.kind === 'run_terminal' && payload.terminal === 'completed') {
|
|
132
|
+
return { kind: 'terminal', success: true, key: event.id };
|
|
133
|
+
}
|
|
134
|
+
if (event.payload_type === 'run.terminal.failed' && payload.kind === 'run_terminal' && payload.terminal === 'failed') {
|
|
135
|
+
return { ...classifyWorkerHealthFailure({ type: 'error', message: payload.reason }, { source, provider: 'muse' }),
|
|
136
|
+
...museQuotaEvidence(payload), fatal: true };
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
getBinary() {
|
|
141
|
+
return process.env.AIWG_MUSE_BIN || 'muse';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getName() {
|
|
145
|
+
return 'muse';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/** Direct group path, separate from legacy session defaults. Policy is checked
|
|
149
|
+
* by resolveWorkerPlan/prepareWorkerInvocation, including contributor/ultra
|
|
150
|
+
* denial. Never map an explicit model away or fall back to a CLI default.
|
|
151
|
+
* The host binds the schema's absolute path in the existing control mapping
|
|
152
|
+
* and includes its bytes in installation.runtimeFiles for qualification. */
|
|
153
|
+
buildWorkerArgs(invocation, profile, permissions) {
|
|
154
|
+
const { selected, cwd, briefPath, health } = invocation;
|
|
155
|
+
const model = selected?.model, effort = selected?.effort?.value;
|
|
156
|
+
if (typeof model !== 'string' || !model || model.includes('\0')) throw new Error('Muse requires an explicit resolved model');
|
|
157
|
+
if (typeof effort !== 'string' || !museEffortValues.includes(effort)) throw new Error('Muse requires a declared explicit resolved effort');
|
|
158
|
+
for (const value of [cwd, briefPath]) {
|
|
159
|
+
if (typeof value !== 'string' || !path.isAbsolute(value) || value.includes('\0') || value.split(path.sep).includes('..')) {
|
|
160
|
+
throw new Error('Muse requires absolute workspace and prompt-file paths');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
let schema;
|
|
164
|
+
try { schema = JSON.parse(profile.controls.structuredOutput.mapping); } catch { /* unavailable mapping blocks */ }
|
|
165
|
+
if (!Array.isArray(schema) || schema.length !== 2 || schema[0] !== '--output-schema'
|
|
166
|
+
|| typeof schema[1] !== 'string' || !path.isAbsolute(schema[1]) || schema[1].includes('\0') || schema[1].split(path.sep).includes('..')) {
|
|
167
|
+
throw new Error('Muse requires a host-bound output-schema control mapping');
|
|
168
|
+
}
|
|
169
|
+
if (!permissions || !['read-only', 'workspace-write', 'external'].includes(permissions.sandbox)
|
|
170
|
+
|| !Number.isSafeInteger(health?.maxSteps) || health.maxSteps < 1) throw new Error('Muse requires bounded steps and sandbox authority');
|
|
171
|
+
// Trust is a host-owned, qualification-bound control, like the schema.
|
|
172
|
+
// Brief contents and CLI defaults cannot opt into workspace instructions.
|
|
173
|
+
let trust;
|
|
174
|
+
try { trust = JSON.parse(profile.controls.trust.mapping); } catch { /* fail closed below */ }
|
|
175
|
+
if (!Array.isArray(trust) || !(trust.length === 0 || trust.length === 1 && trust[0] === '--trust-workspace')) {
|
|
176
|
+
throw new Error('Muse requires an explicit host-controlled workspace trust mapping');
|
|
177
|
+
}
|
|
178
|
+
const args = ['exec', '--json', '--provider', 'meta', '--model', model, '--reasoning-effort', effort,
|
|
179
|
+
'--workspace', cwd, ...trust, '--approval-mode', 'never', '--approval-judge', 'off', '--max-model-steps', String(health.maxSteps),
|
|
180
|
+
...schema, '--prompt-file', briefPath];
|
|
181
|
+
if (permissions.sandbox === 'read-only') args.push('--disable-write', '--disable-shell');
|
|
182
|
+
return args;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
getCapabilities() {
|
|
186
|
+
return {
|
|
187
|
+
streamJson: true, // --json (evidenced: JSONL events on stdout)
|
|
188
|
+
sessionResume: true, // --session-id (evidenced: headless resume)
|
|
189
|
+
budgetControl: false, // no --max-budget-usd equivalent documented
|
|
190
|
+
systemPrompt: false, // no --append-system-prompt documented for exec
|
|
191
|
+
agentMode: false, // no --agent flag documented
|
|
192
|
+
mcpConfig: false, // MCP lives in ~/.config/muse/settings.json; no exec flag
|
|
193
|
+
// maxTurns maps to --max-model-steps (evidenced, headless-only); the
|
|
194
|
+
// capability name follows the base-class contract, the argv does not
|
|
195
|
+
// invent a --max-turns flag muse would reject with exit code 2.
|
|
196
|
+
maxTurns: true,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Build argv for a headless session: `muse exec [OPTIONS] [PROMPT]`.
|
|
202
|
+
* The prompt is positional and always last (evidenced). No approval
|
|
203
|
+
* posture is set unless the caller opts in via `approvalMode` — `--yolo`
|
|
204
|
+
* is never emitted (#230 out of scope).
|
|
205
|
+
*
|
|
206
|
+
* @param {import('./provider-adapter.mjs').SessionArgs & {
|
|
207
|
+
* reasoningEffort?: string, approvalMode?: string, promptFile?: string
|
|
208
|
+
* }} options
|
|
209
|
+
* @returns {string[]}
|
|
210
|
+
*/
|
|
211
|
+
buildSessionArgs(options, { json = true } = {}) {
|
|
212
|
+
// Sub-command-first: options are parsed by `exec`, never by the root.
|
|
213
|
+
const args = json ? ['exec', '--json'] : ['exec'];
|
|
214
|
+
|
|
215
|
+
// Session pin / headless resume (evidenced).
|
|
216
|
+
if (options.sessionId) args.push('--session-id', options.sessionId);
|
|
217
|
+
|
|
218
|
+
// Model and reasoning depth (evidenced, common to both surfaces).
|
|
219
|
+
const model = options.model ? this.mapModel(options.model) : null;
|
|
220
|
+
if (model) args.push('--model', model);
|
|
221
|
+
if (options.reasoningEffort) args.push('--reasoning-effort', options.reasoningEffort);
|
|
222
|
+
|
|
223
|
+
// Step cap (evidenced, headless-only). The capability is `maxTurns`;
|
|
224
|
+
// muse's flag is --max-model-steps, so translate rather than warn.
|
|
225
|
+
if (options.maxTurns) args.push('--max-model-steps', String(options.maxTurns));
|
|
226
|
+
|
|
227
|
+
// Approval posture is operator opt-in only (evidenced flag, no default).
|
|
228
|
+
if (options.approvalMode) args.push('--approval-mode', options.approvalMode);
|
|
229
|
+
|
|
230
|
+
if (options.budget) this.warnUnsupported('budgetControl', 'Budget control');
|
|
231
|
+
if (options.systemPrompt) this.warnUnsupported('systemPrompt', 'System prompt');
|
|
232
|
+
if (options.agent) this.warnUnsupported('agentMode', 'Agent mode');
|
|
233
|
+
if (options.mcpConfig) this.warnUnsupported('mcpConfig', 'MCP configuration');
|
|
234
|
+
|
|
235
|
+
if (options.promptFile) {
|
|
236
|
+
args.push('--prompt-file', options.promptFile);
|
|
237
|
+
} else {
|
|
238
|
+
// The prompt itself (positional, must be last). `--` stops option
|
|
239
|
+
// parsing so a prompt that starts with `-` is not read as a flag.
|
|
240
|
+
args.push('--', options.prompt);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return args;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Build argv for short analysis calls (spawnSync). Same headless surface
|
|
248
|
+
* as sessions, but without `--json`: analysis consumers parse the reply
|
|
249
|
+
* text, and `parseOutput()` extracts no text from JSONL events.
|
|
250
|
+
*
|
|
251
|
+
* @param {import('./provider-adapter.mjs').AnalysisArgs} options
|
|
252
|
+
* @returns {string[]}
|
|
253
|
+
*/
|
|
254
|
+
buildAnalysisArgs(options) {
|
|
255
|
+
return this.buildSessionArgs(options, { json: false });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Build argv for the transcript/export path: `muse export`.
|
|
260
|
+
* Offline and byte-deterministic per Meta docs; never modifies the log.
|
|
261
|
+
*
|
|
262
|
+
* @param {{ sessionId?: string, out?: string, redacted?: boolean, last?: boolean }} options
|
|
263
|
+
* @returns {string[]}
|
|
264
|
+
*/
|
|
265
|
+
buildExportArgs(options = {}) {
|
|
266
|
+
const args = ['export'];
|
|
267
|
+
if (options.last) {
|
|
268
|
+
args.push('--last');
|
|
269
|
+
} else if (options.sessionId) {
|
|
270
|
+
args.push('--session', options.sessionId);
|
|
271
|
+
}
|
|
272
|
+
if (options.redacted) args.push('--redacted');
|
|
273
|
+
if (options.out) args.push('--out', options.out);
|
|
274
|
+
return args;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* The CLI accepts `--model <id>` with a Meta provider catalog id
|
|
279
|
+
* (observed: `muse-spark-1.3`, the current model, plus
|
|
280
|
+
* `muse-spark-1.3-contributor`, `muse-spark-1.2`, and
|
|
281
|
+
* `muse-spark-1.2-contributor`). Only Muse ids pass through. Generic or
|
|
282
|
+
* other-provider names (Ralph defaults to Claude names) map to null so no
|
|
283
|
+
* `--model` flag is emitted and muse uses its configured default.
|
|
284
|
+
*
|
|
285
|
+
* @param {string} genericModel
|
|
286
|
+
* @returns {string|null}
|
|
287
|
+
*/
|
|
288
|
+
mapModel(genericModel) {
|
|
289
|
+
return /^muse-/i.test(genericModel) ? genericModel : null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
getEnvOverrides() {
|
|
293
|
+
return { CI: 'true', NO_COLOR: '1' };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** No evidenced stdin command channel for `muse exec`; no abort frame. */
|
|
297
|
+
getAbortInput() {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* ADR fail-closed path policy: no native session-log root is assumed, so
|
|
303
|
+
* there is no transcript path to return. Use `buildExportArgs()` to
|
|
304
|
+
* produce a `muse export` document instead.
|
|
305
|
+
*
|
|
306
|
+
* @returns {null}
|
|
307
|
+
*/
|
|
308
|
+
getTranscriptPath() {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Parse `muse exec --json` output. Returns the events, the final answer
|
|
314
|
+
* text, and settlement from the `run.terminal.<state>` record: `settled`
|
|
315
|
+
* is true only for `completed`, false for any other terminal state, and
|
|
316
|
+
* null when the stream ended without a terminal record. One malformed
|
|
317
|
+
* line rejects the whole stream (fail closed).
|
|
318
|
+
*
|
|
319
|
+
* @param {string} stdout
|
|
320
|
+
* @returns {{ events: object[], text: string|null, settled: boolean|null,
|
|
321
|
+
* terminal: string|null, reason: string|null } | null}
|
|
322
|
+
*/
|
|
323
|
+
parseOutput(stdout) {
|
|
324
|
+
if (typeof stdout !== 'string' || Buffer.byteLength(stdout) > 10485760) return null;
|
|
325
|
+
const events = [];
|
|
326
|
+
for (const raw of stdout.split('\n')) {
|
|
327
|
+
const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
|
|
328
|
+
if (!line) continue;
|
|
329
|
+
try {
|
|
330
|
+
events.push(JSON.parse(line));
|
|
331
|
+
} catch {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (!events.length) return null;
|
|
336
|
+
let deltas = '';
|
|
337
|
+
let terminal = null;
|
|
338
|
+
for (const event of events) {
|
|
339
|
+
const type = typeof event?.payload_type === 'string' ? event.payload_type : '';
|
|
340
|
+
const payload = event?.payload ?? {};
|
|
341
|
+
if (type === 'run.output.delta' && typeof payload.text === 'string') deltas += payload.text;
|
|
342
|
+
if (terminal) return null; // double final or output after terminal
|
|
343
|
+
if (type.startsWith('run.terminal.')) {
|
|
344
|
+
if (!['run.terminal.completed', 'run.terminal.failed'].includes(type)
|
|
345
|
+
|| payload.terminal !== type.slice('run.terminal.'.length)) return null;
|
|
346
|
+
terminal = payload;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
const state = typeof terminal?.terminal === 'string' ? terminal.terminal : null;
|
|
350
|
+
const finalText = typeof terminal?.text === 'string' && terminal.text ? terminal.text : deltas;
|
|
351
|
+
return {
|
|
352
|
+
events,
|
|
353
|
+
text: finalText || null,
|
|
354
|
+
settled: state === null ? null : state === 'completed',
|
|
355
|
+
terminal: state,
|
|
356
|
+
reason: typeof terminal?.reason === 'string' ? terminal.reason : null,
|
|
357
|
+
...(terminal && museQuotaEvidence(terminal) ? { quota: museQuotaEvidence(terminal) } : {}),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
registerProvider('muse', () => new MuseAdapter());
|
|
363
|
+
|
|
364
|
+
export default MuseAdapter;
|