@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.
Files changed (168) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +270 -0
  2. package/agentic/code/providers/model-capabilities.v1.json +3091 -24
  3. package/bin/aiwg.mjs +86 -21
  4. package/dist/src/a2a/codecs.js +9 -0
  5. package/dist/src/artifacts/corpus-tools/vision-extract.js +105 -15
  6. package/dist/src/artifacts/repair.js +9 -4
  7. package/dist/src/channel/manager.mjs +59 -10
  8. package/dist/src/cli/child-process-result.js +40 -0
  9. package/dist/src/cli/env.js +38 -16
  10. package/dist/src/cli/flag-parsing.js +236 -0
  11. package/dist/src/cli/handlers/decision.js +297 -2
  12. package/dist/src/cli/handlers/diagnose.js +9 -2
  13. package/dist/src/cli/handlers/feedback.js +14 -5
  14. package/dist/src/cli/handlers/init.js +245 -30
  15. package/dist/src/cli/handlers/mc.js +23 -0
  16. package/dist/src/cli/handlers/models.js +69 -6
  17. package/dist/src/cli/handlers/ralph.js +3 -0
  18. package/dist/src/cli/handlers/refresh.js +16 -0
  19. package/dist/src/cli/handlers/regenerate.js +14 -0
  20. package/dist/src/cli/handlers/runtime-info.js +24 -9
  21. package/dist/src/cli/handlers/script-runner.js +16 -6
  22. package/dist/src/cli/handlers/session.js +396 -62
  23. package/dist/src/cli/handlers/sessions.js +146 -40
  24. package/dist/src/cli/handlers/setup-manifest.js +439 -39
  25. package/dist/src/cli/handlers/setup.js +139 -39
  26. package/dist/src/cli/handlers/subcommands.js +3 -1
  27. package/dist/src/cli/handlers/team.js +18 -8
  28. package/dist/src/cli/handlers/use.js +13 -0
  29. package/dist/src/cli/handlers/utilities.js +106 -23
  30. package/dist/src/cli/prompt-utils.js +72 -0
  31. package/dist/src/cli/provider-resolution.js +11 -0
  32. package/dist/src/config/aiwg-config.js +268 -4
  33. package/dist/src/config/cli.js +146 -19
  34. package/dist/src/config/dispatch-thresholds-runtime.mjs +38 -0
  35. package/dist/src/config/project-artifacts-health.mjs +14 -3
  36. package/dist/src/config/user-config.js +36 -10
  37. package/dist/src/decision/adapters/jev.js +80 -28
  38. package/dist/src/decision/ask.js +352 -0
  39. package/dist/src/decision/driver.js +4 -0
  40. package/dist/src/decision/entry.js +38 -6
  41. package/dist/src/decision/heldout/collector.js +10 -1
  42. package/dist/src/decision/index.js +3 -0
  43. package/dist/src/decision/interop.js +60 -8
  44. package/dist/src/decision/jev-credentials.js +121 -0
  45. package/dist/src/decision/ledger.js +839 -0
  46. package/dist/src/decision/validate.js +6 -3
  47. package/dist/src/effects/dispatch-verifier.js +252 -0
  48. package/dist/src/effects/index.js +1 -0
  49. package/dist/src/features/installer.js +2 -2
  50. package/dist/src/flow/graph-metadata.js +5 -0
  51. package/dist/src/gates/providers/loader.js +3 -1
  52. package/dist/src/installation/manager-command.mjs +1 -1
  53. package/dist/src/installation/manager.mjs +17 -2
  54. package/dist/src/lint/runner.js +2 -2
  55. package/dist/src/mcp/adapters/codex-runtime.js +360 -56
  56. package/dist/src/mcp/cli.mjs +587 -77
  57. package/dist/src/mcp/config-file.mjs +279 -0
  58. package/dist/src/mcp/config-layers.mjs +348 -0
  59. package/dist/src/mcp/credentials.mjs +484 -0
  60. package/dist/src/mcp/grok-build-config.mjs +16 -26
  61. package/dist/src/mcp/helpers.mjs +106 -2
  62. package/dist/src/mcp/omp-config.mjs +50 -29
  63. package/dist/src/mcp/profiles.js +97 -23
  64. package/dist/src/mcp/profiles.mjs +99 -27
  65. package/dist/src/mcp/registry.js +264 -57
  66. package/dist/src/mcp/registry.mjs +198 -45
  67. package/dist/src/mcp/server.mjs +403 -20
  68. package/dist/src/mcp/toml-string.mjs +9 -0
  69. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  70. package/dist/src/mcp/tool-filters.mjs +357 -0
  71. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  72. package/dist/src/mcp/tools/decision.mjs +58 -0
  73. package/dist/src/mcp/tools/orchestration.mjs +38 -0
  74. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  75. package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
  76. package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
  77. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  78. package/dist/src/memory/project-registry.js +2 -2
  79. package/dist/src/mission-protocol/codecs.js +639 -1
  80. package/dist/src/mission-protocol/types.js +110 -0
  81. package/dist/src/mission-protocol/worker-status.js +2 -0
  82. package/dist/src/models/config-loader.js +70 -13
  83. package/dist/src/models/grok-build-models.js +93 -41
  84. package/dist/src/models/model-capabilities.v1.json +3091 -24
  85. package/dist/src/models/model-discovery.js +862 -63
  86. package/dist/src/models/provider-policy.js +625 -11
  87. package/dist/src/models/qualification-digest.mjs +17 -0
  88. package/dist/src/models/resolver.js +259 -0
  89. package/dist/src/models/worker-direct-readiness.js +529 -0
  90. package/dist/src/models/worker-dispatch-offer.js +169 -0
  91. package/dist/src/models/worker-health-readiness.js +131 -0
  92. package/dist/src/models/worker-installation.mjs +879 -0
  93. package/dist/src/models/wrapper-route.js +18 -0
  94. package/dist/src/ops/registry.js +9 -14
  95. package/dist/src/plugins/standalone-packager.js +4 -3
  96. package/dist/src/process/bounded-child-process.mjs +387 -0
  97. package/dist/src/process/cli-command.mjs +75 -0
  98. package/dist/src/providers/capability-matrix.js +8 -0
  99. package/dist/src/providers/capability-matrix.yaml +270 -0
  100. package/dist/src/providers/grok-build-headless.js +43 -21
  101. package/dist/src/providers/provider-definitions.js +9 -1
  102. package/dist/src/providers/provider-definitions.mjs +9 -2
  103. package/dist/src/serve/executor-registry.js +16 -24
  104. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  105. package/dist/src/serve/mission-conductor.js +9 -0
  106. package/dist/src/serve/shared-host-scheduler.js +2 -0
  107. package/dist/src/serve/worker-invocation.js +538 -0
  108. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  109. package/dist/src/sessions/adapter-registry.js +45 -0
  110. package/dist/src/sessions/adapters/generic.js +37 -3
  111. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  112. package/dist/src/sessions/adapters/grokbot.js +423 -24
  113. package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
  114. package/dist/src/sessions/adapters/muse.js +68 -29
  115. package/dist/src/sessions/adapters/pi.js +1 -0
  116. package/dist/src/sessions/batch-contracts.js +13 -1
  117. package/dist/src/sessions/batch-import.js +23 -42
  118. package/dist/src/sessions/candidates.js +47 -11
  119. package/dist/src/sessions/contracts.js +35 -0
  120. package/dist/src/sessions/coverage-preflight.js +134 -0
  121. package/dist/src/sessions/discovery.js +3 -0
  122. package/dist/src/sessions/grok-build-cli.js +67 -5
  123. package/dist/src/sessions/importer.js +24 -8
  124. package/dist/src/sessions/index.js +2 -0
  125. package/dist/src/sessions/repository.js +54 -13
  126. package/dist/src/sessions/workspace-discovery.js +99 -37
  127. package/dist/src/skills/adapters/clawhub.js +10 -8
  128. package/dist/src/skills/adapters/openclaw.js +10 -7
  129. package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
  130. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  131. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  132. package/dist/src/smiths/mcpsmith/example.js +1 -1
  133. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
  134. package/dist/src/storage/backends/logseq.js +68 -7
  135. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  136. package/dist/src/testing/generators/test-code-generator.js +57 -30
  137. package/dist/src/uhp/client.js +11 -1
  138. package/dist/src/uhp/mission.js +5 -0
  139. package/dist/src/update/checker.mjs +5 -5
  140. package/dist/src/update/notifier.mjs +54 -2
  141. package/dist/src/writing/fidelity.js +18 -1
  142. package/package.json +18 -1
  143. package/schemas/mission-v1.schema.json +2429 -0
  144. package/schemas/models/model-catalog.v1.schema.json +301 -0
  145. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  146. package/schemas/models/model-policy.v1.schema.json +14 -0
  147. package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
  148. package/tools/_resolve-impl.mjs +39 -0
  149. package/tools/agents/deploy-agents.mjs +15 -2
  150. package/tools/agents/providers/base.mjs +63 -43
  151. package/tools/agents/providers/grokbot.mjs +11 -12
  152. package/tools/agents/providers/hermes.mjs +6 -3
  153. package/tools/agents/providers/omp.mjs +5 -2
  154. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  155. package/tools/providers/deepseek-harness-transport.mjs +358 -0
  156. package/tools/providers/omp-transport.mjs +192 -0
  157. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  158. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  159. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  160. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  161. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  162. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  163. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  164. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  165. package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
  166. package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
  167. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  168. package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
@@ -0,0 +1,315 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { ProviderAdapter, registerProvider } from './provider-adapter.mjs';
3
+ import { OmpFrameDecoder, summarizeOmpEvents, OmpRpcClient, MAX_FRAME, MAX_MESSAGE } from '../../providers/omp-transport.mjs';
4
+ import { createWorkerHealthObserver, healthEventMatchesBinding } from './worker-health.mjs';
5
+
6
+ /**
7
+ * Version-bound direct-worker surface (roctinam/aiwg#2910, B-OG-45).
8
+ *
9
+ * Qualified against `omp/18.8.7` `--help` (help/version syntax only,
10
+ * 2026-10-09, captured on noodle; never live qualification):
11
+ *
12
+ * omp --mode json --print --no-extensions --no-skills --no-rules
13
+ * --model M --thinking E --cwd W --approval-mode write < briefPath
14
+ *
15
+ * `--print`/`-p` ("Non-interactive mode: process prompt and exit") is kept
16
+ * explicit alongside `--mode json` because OMP's own `--help` lists it as a
17
+ * separate flag (unlike the evidenced Pi fork behavior where `--mode json`
18
+ * alone forces non-interactive): the direct profile never depends on an
19
+ * unverified inheritance assumption for this one invariant.
20
+ *
21
+ * `--approval-mode` is ALWAYS rendered explicitly and is NEVER omitted: a
22
+ * static read of the installed omp binary (`~/.local/bin/omp` on noodle,
23
+ * 18.8.7, noodle) shows the compiled-in default is `"yolo"`
24
+ * (`approvalMode: Uxa(s) ? s : "yolo"`) — a full approval/sandbox bypass —
25
+ * so an omitted flag would silently inherit the most dangerous mode, not a
26
+ * safe one. The same binary defines the mode->tier gate explicitly:
27
+ * `Kxa = { "always-ask": "read", write: "write", yolo: "exec" }`
28
+ * `PS = { read: 0, write: 1, exec: 2 }`
29
+ * `Xxa(mode, toolTier) { return PS[toolTier] <= PS[Kxa[mode]]; }`
30
+ * i.e. a tool call is auto-allowed only when its own tier rank is at or
31
+ * below the mode's threshold tier rank; anything above resolves to a
32
+ * `"prompt"`-required decision. `write`'s threshold is `write` (rank 1), so
33
+ * it auto-approves `read`/`write`-tier tools but an `exec`-tier tool
34
+ * (`bash`, the dominant tool for a coding worker) still resolves to
35
+ * `"prompt"` required. `write` is therefore NOT a complete non-prompting
36
+ * guarantee for this profile — it is only the least-prompting of the two
37
+ * non-bypass values (`always-ask`'s threshold is `read`, rank 0, so it
38
+ * prompts for `write` AND `exec`). `yolo` is never used (its threshold is
39
+ * `exec`, rank 2: every tier auto-allowed, i.e. a full bypass). No headless
40
+ * auto-resolution for a `"prompt"`-required decision was found in the
41
+ * binary (the one "Cannot prompt for confirmation (no TTY)" string in the
42
+ * binary belongs to Bun's own bundled package-install security-advisory
43
+ * prompt, not OMP's tool-approval gate); the changelog text compiled into
44
+ * the same binary separately describes ACP approval requests getting stuck
45
+ * `"pending"` when the channel "cannot answer" them. Exec-tier tool calls
46
+ * under `write` in print mode are therefore an OPEN GAP, not a qualified
47
+ * non-prompting path: the `approval` control stays `unqualified` and this
48
+ * gap is the reason, not a documentation placeholder. OMP has no
49
+ * `--approve/--no-approve` trust flag like Pi; the closest documented
50
+ * surface for unvetted project-local resources is discovery of
51
+ * extensions/skills/rules, so the direct profile always disables all three
52
+ * (`--no-extensions --no-skills --no-rules`) rather than trusting arbitrary
53
+ * workspace files. `--cwd` is explicit and documented (`--help`), unlike
54
+ * Pi, which has no such flag. There is no documented sandbox,
55
+ * output-file, output-schema, step-cap or child-denial flag (`omp --help`,
56
+ * 2026-10-09): those controls stay unavailable rather than invented, and
57
+ * the final answer is extracted from the stdout event stream
58
+ * (`parseOutput()`/`summarizeOmpEvents`), never a `-o`-style file.
59
+ *
60
+ * The brief travels on stdin, never argv — CONFIRMED by a static read of
61
+ * the installed omp binary (18.8.7, noodle), not merely inherited from
62
+ * Pi's lineage: `async function wUt()` (OMP's own `readPipedInput`) returns
63
+ * early when `process.stdin.isTTY === true`, otherwise awaits
64
+ * `Bun.stdin.text()` to EOF and returns the text unless it is
65
+ * all-whitespace; it runs whenever `mode` is not `rpc`/`rpc-ui`/`acp`
66
+ * (`--mode json` included: `const p = r === "rpc" || r === "rpc-ui" || r
67
+ * === "acp"; const c = p ? undefined : await readPipedInput();`), and its
68
+ * result is passed as `stdinContent` into OMP's own `buildInitialMessage`
69
+ * (`function aso({parsed, fileText, stdinContent}) { ... }`), which
70
+ * prefixes it onto the initial message exactly like Pi's equivalent
71
+ * function. Unlike Pi's `readPipedStdin`, OMP's `readPipedInput` does not
72
+ * trim the returned text (only the emptiness check is trimmed), so a brief
73
+ * with leading/trailing whitespace is NOT altered by OMP (a difference
74
+ * from Pi, noted since the shared fixture briefs intentionally carry
75
+ * leading/trailing whitespace).
76
+ */
77
+ export const OMP_DIRECT_VERSION = '18.8.7';
78
+ /** Mirrors providers.omp.effortValues in model-capabilities.v1.json (hardcoded,
79
+ * not read via a repo-relative path: the installed-module loader copies this
80
+ * file to an arbitrary package root, so import.meta.url-relative reads to the
81
+ * repository are not safe here). */
82
+ export const OMP_DIRECT_EFFORT_VALUES = Object.freeze(['minimal', 'low', 'medium', 'high', 'xhigh']);
83
+ /** Explicit, non-bypassing approval only (`yolo` refused; OMP's unset default is yolo). `write`
84
+ * auto-approves read/write tools; exec-tier (bash) approvals have no confirmed headless
85
+ * resolution, so the approval control stays unqualified. */
86
+ export const OMP_DIRECT_APPROVAL_MODES = Object.freeze(['write']);
87
+ /** Brief transport for the direct profile: piped stdin, never argv. */
88
+ export const OMP_DIRECT_STDIN = 'brief-text';
89
+
90
+ /** Oh My Pi's native flags/events intentionally differ from Pi. */
91
+ export class OmpAdapter extends ProviderAdapter {
92
+ createHealthObserver(options = {}) {
93
+ // Reuse OMP's owned v2 chunk grammar. Neither another CLI's framing nor
94
+ // the strict worker-final parser is a substitute for native reassembly.
95
+ const messageLimit = Math.min(options.maxMessageBytes ?? 4 * MAX_FRAME, MAX_MESSAGE);
96
+ let decoder = new OmpFrameDecoder({ messageLimit });
97
+ let lastSource = null;
98
+ const observer = createWorkerHealthObserver({ ...options, provider: this.getName(),
99
+ maxFrameBytes: options.maxFrameBytes ?? MAX_FRAME,
100
+ normalizeEvent: (frame, source, binding) => {
101
+ if (source.kind !== 'stdout') return null;
102
+ const identity = `${source.id}:${source.generation}`;
103
+ if (lastSource !== null && lastSource !== identity) decoder = new OmpFrameDecoder({ messageLimit });
104
+ lastSource = identity;
105
+ try {
106
+ const event = decoder.decode(frame);
107
+ return event ? this.observeHealthEvent(event, source, binding) : null;
108
+ } catch (error) {
109
+ decoder = new OmpFrameDecoder({ messageLimit });
110
+ throw error;
111
+ }
112
+ },
113
+ });
114
+ const finish = observer.finish;
115
+ observer.finish = observation => {
116
+ try { decoder.end(); } catch { observer.gap('MALFORMED_FRAME'); }
117
+ return finish(observation);
118
+ };
119
+ return observer;
120
+ }
121
+ observeHealthEvent(event, source, binding) {
122
+ if (source.kind !== 'stdout' || !event || typeof event !== 'object') return null;
123
+ if (!healthEventMatchesBinding({ sessionId: event.sessionId }, binding)) return null;
124
+ if (event.type === 'agent_start') return { kind: 'accepted' };
125
+ if (event.type === 'tool_execution_end') {
126
+ if (event.isError === true) return { kind: 'error', reasonCode: 'tool-failed' };
127
+ if (typeof event.toolCallId === 'string') return { kind: 'progress', key: event.toolCallId };
128
+ return { kind: 'activity' };
129
+ }
130
+ if (event.type === 'agent_end') {
131
+ if (Array.isArray(event.messages) && event.messages.some(message => message.stopReason === 'error' || message.stopReason === 'aborted')) return { kind: 'error', reasonCode: 'provider-failed' };
132
+ return event.willContinue === true ? { kind: 'activity' } : { kind: 'terminal', success: true, key: 'agent_end' };
133
+ }
134
+ if (event.type === 'rpc_frame_error' || event.type === 'error') return { kind: 'error', reasonCode: 'provider-failed' };
135
+ return null;
136
+ }
137
+ async getVersion() { const result = spawnSync(this.getBinary(), ['--version'], { encoding: 'utf8', timeout: 5000, maxBuffer: 65536 }); return result.status === 0 ? result.stdout.trim() : null; }
138
+ async isAvailable() { return Boolean(await this.getVersion()); }
139
+ getName() { return 'omp'; }
140
+ getBinary() { return process.env.AIWG_OMP_BIN || 'omp'; }
141
+ getCapabilities() { return { streamJson: true, sessionResume: true, budgetControl: false, systemPrompt: true, agentMode: false, mcpConfig: false, maxTurns: false, rpcAbort: true }; }
142
+ buildSessionArgs(options) {
143
+ const args = ['--mode', 'json'];
144
+ if (options.profile) args.push('--profile', options.profile);
145
+ for (const config of options.config ?? []) args.push('--config', config);
146
+ if (options.model) args.push('--model', this.mapModel(options.model));
147
+ if (options.thinking) args.push('--thinking', options.thinking);
148
+ if (options.sessionId) args.push('--session', options.sessionId);
149
+ if (options.sessionDir) args.push('--session-dir', options.sessionDir);
150
+ if (options.noSession) args.push('--no-session');
151
+ if (options.noExtensions) args.push('--no-extensions');
152
+ if (options.tools) { if (options.tools.length) args.push('--tools', options.tools.join(',')); else args.push('--no-tools'); }
153
+ if (options.systemPrompt) args.push('--append-system-prompt', options.systemPrompt);
154
+ for (const [option, capability] of [['budget', 'budgetControl'], ['maxTurns', 'maxTurns'], ['mcpConfig', 'mcpConfig']]) if (options[option]) this.warnUnsupported(capability, option);
155
+ args.push('--', options.prompt);
156
+ return args;
157
+ }
158
+ buildAnalysisArgs(options) { return this.buildSessionArgs(options); }
159
+
160
+ /**
161
+ * Driver-owned argv after the binary for the version-bound direct-shell
162
+ * profile. Model, thinking level and workdir are always explicit (never
163
+ * a CLI default or alias); approval is pinned explicitly to `write` (never
164
+ * left unset, which would be yolo; exec-tier approvals stay unresolved); extension/skill/rule discovery is
165
+ * always disabled (no `--approve`-style trust flag exists on OMP); the
166
+ * brief is validated here but never placed in argv — it travels on
167
+ * stdin (`stdin: 'brief-text'`).
168
+ *
169
+ * @param {{ selected?: { model?: unknown, effort?: { value?: unknown } }, cwd?: unknown, brief?: unknown }} invocation
170
+ * @param {unknown} _profile
171
+ * @param {{ approval?: unknown }} [permissions]
172
+ * @returns {string[]}
173
+ */
174
+ buildWorkerArgs(invocation, _profile, permissions) {
175
+ const model = invocation?.selected?.model;
176
+ if (typeof model !== 'string' || !model || model.includes('\0')) {
177
+ throw new Error('OMP worker requires an explicit model');
178
+ }
179
+ const effort = invocation?.selected?.effort?.value ?? null;
180
+ if (effort === null || !OMP_DIRECT_EFFORT_VALUES.includes(effort)) {
181
+ throw new Error('OMP worker requires an explicit qualified thinking level (never a CLI default)');
182
+ }
183
+ const cwd = invocation?.cwd;
184
+ if (typeof cwd !== 'string' || !cwd.startsWith('/') || cwd.includes('\0')) {
185
+ throw new Error('OMP worker requires an absolute workspace');
186
+ }
187
+ // The shared resolved-permission vocabulary's 'never' ("never require
188
+ // approval") is the only input that maps to OMP's explicit, non-bypassing
189
+ // `write` mode (exec-tier approvals unresolved headless; see module doc); 'always'/'on-request' have no equivalent
190
+ // without prompting or a full sandbox/approval bypass and are refused.
191
+ const requestedApproval = permissions?.approval ?? 'never';
192
+ if (requestedApproval !== 'never') {
193
+ throw new Error(`OMP worker approval is not a qualified direct mode: ${String(requestedApproval)}`);
194
+ }
195
+ const approval = OMP_DIRECT_APPROVAL_MODES[0];
196
+ const brief = invocation?.brief;
197
+ if (typeof brief !== 'string' || !brief.trim() || brief.includes('\0')) {
198
+ throw new Error('OMP worker requires a non-empty brief');
199
+ }
200
+ return ['--mode', 'json', '--print', '--no-extensions', '--no-skills', '--no-rules',
201
+ '--model', model, '--thinking', effort, '--cwd', cwd, '--approval-mode', approval];
202
+ }
203
+
204
+ mapModel(model) { return model; }
205
+ getEnvOverrides() { return { CI: 'true', NO_COLOR: '1' }; }
206
+ // JSON print mode has no stdin abort protocol; runner terminates its process.
207
+ getAbortInput() { return null; }
208
+ createRpcClient(options = {}) { return new OmpRpcClient({ binary: this.getBinary(), ...options }); }
209
+ getTranscriptPath(sessionId) { return sessionId?.endsWith('.jsonl') ? sessionId : null; }
210
+ parseOutput(stdout, options = {}) {
211
+ if (Buffer.byteLength(stdout) > MAX_MESSAGE) return null;
212
+ try { const decoder = new OmpFrameDecoder({ frameLimit: MAX_MESSAGE }); const events = decoder.push(Buffer.from(stdout)); decoder.end(); return events.length ? summarizeOmpEvents(events, options) : null; } catch { return null; }
213
+ }
214
+
215
+ /**
216
+ * #2995 round 3: strict direct-worker-profile final, separate from the
217
+ * legacy `parseOutput`/`summarizeOmpEvents` candidate above (that SHARED
218
+ * helper is never changed here — module doc). `summarizeOmpEvents`
219
+ * tracks the LAST assistant message across every event with a
220
+ * `messages`/`message` field, including `tool_execution_end.message`,
221
+ * so it can retain a stale or tool-carried answer when the real terminal
222
+ * `agent_end` is answerless. This rule instead: (1) only reads the
223
+ * assistant answer from the LAST terminal `agent_end` (`willContinue !==
224
+ * true`) itself, never from any other event type; (2) a non-terminal
225
+ * `agent_end` (`willContinue: true`) clears any earlier candidate text,
226
+ * since it is not the run's actual final answer; (3) ANY event observed
227
+ * after the terminal `agent_end` fails the parse, mirroring Factory/
228
+ * Cursor's structural "nothing follows the terminal" rule.
229
+ * @param {string|Uint8Array} stdout
230
+ * @returns {{ text: string, success: true } | null}
231
+ */
232
+ parseDirectFinal(stdout) {
233
+ if (Buffer.byteLength(stdout) > MAX_MESSAGE) return null;
234
+ let events;
235
+ try {
236
+ const decoder = new OmpFrameDecoder({ frameLimit: MAX_MESSAGE });
237
+ events = decoder.push(Buffer.from(stdout));
238
+ decoder.end();
239
+ } catch { return null; }
240
+ if (!events.length) return null;
241
+ let terminal = null, text = null, failed = false, aborted = false, elided = false, afterTerminal = false;
242
+ for (const event of events) {
243
+ if (/elided for RPC frame|rpcFrameElidedKeys/.test(JSON.stringify(event))) elided = true;
244
+ if (terminal) { afterTerminal = true; continue; }
245
+ if (event.type === 'rpc_frame_error' || event.type === 'error') { failed = true; continue; }
246
+ if (event.type === 'tool_execution_end' && event.isError) { failed = true; continue; }
247
+ if (event.type === 'agent_end' && event.willContinue === true) { text = null; continue; }
248
+ if (event.type === 'agent_end' && event.willContinue !== true) {
249
+ terminal = event;
250
+ const assistant = Array.isArray(event.messages) ? event.messages.filter(message => message.role === 'assistant').at(-1) : undefined;
251
+ if (!assistant) { text = null; continue; }
252
+ if (assistant.stopReason === 'error') failed = true;
253
+ if (assistant.stopReason === 'aborted') aborted = true;
254
+ text = Array.isArray(assistant.content) ? assistant.content.filter(part => part.type === 'text').map(part => part.text).join('\n') : null;
255
+ continue;
256
+ }
257
+ }
258
+ if (!terminal || afterTerminal || failed || aborted || elided) return null;
259
+ if (!text || !text.trim()) return null;
260
+ return { text, success: true };
261
+ }
262
+ }
263
+ // Self-register on import for the legacy Ralph loop only. The direct worker
264
+ // profile loads through the #2885 installed loader instead (see below).
265
+ registerProvider('omp', () => new OmpAdapter());
266
+
267
+ /**
268
+ * #2995 round 3: `OmpDirectAdapter` is the Claude-precedent `-direct`
269
+ * subclass, registered under its OWN id (`omp-direct`, never the bare
270
+ * `omp` legacy id above) so loading this installed module cannot overwrite
271
+ * the legacy registration in-process (src/models/worker-installation.mjs
272
+ * ~l.550). `parseOutput` here is the strict `parseDirectFinal` rule (never
273
+ * the loose, generic-accepting legacy candidate), and `parseWorkerFinal`
274
+ * is strict with no generic-envelope fallback: this class is never
275
+ * reachable from the legacy bare id, so the legacy group-mode contract
276
+ * (test/unit/ralph-external/worker-finals.test.ts) never sees it.
277
+ */
278
+ export class OmpDirectAdapter extends OmpAdapter {
279
+ // #2995 round 4 (item 3): production resolves the installed adapter by
280
+ // `adapter.id` (now `omp-direct`, item B) and src/models/
281
+ // worker-installation.mjs's `inspectWorkerInstallation` requires
282
+ // `adapter.id === instance.getName()`. `getBinary()` is untouched; the
283
+ // inherited `createHealthObserver` override's `provider: this.getName()`
284
+ // label changes too, but it only feeds the shared observer's stderr
285
+ // classifier/report label -- `observeHealthEvent` below (the actual
286
+ // event dispatch) is unaffected.
287
+ getName() { return 'omp-direct'; }
288
+
289
+ parseOutput(stdout) {
290
+ return this.parseDirectFinal(stdout);
291
+ }
292
+
293
+ /**
294
+ * @param {string|Uint8Array} output
295
+ * @param {Object} [options]
296
+ * @returns {Promise<import('../../../src/mission-protocol/types.js').WorkerClaims>}
297
+ */
298
+ async parseWorkerFinal(output, options = {}) {
299
+ if (options.format === 'mcp') return super.parseWorkerFinal(output, options);
300
+ const text = typeof output === 'string' ? output : Buffer.from(output).toString('utf8');
301
+ const final = this.parseDirectFinal(text);
302
+ if (!final) throw new Error('OMP worker stream did not produce a successful final answer');
303
+ return super.parseWorkerFinal(final.text, { ...options, format: 'json' });
304
+ }
305
+ }
306
+
307
+ /** Installed-module interface version (mirrors #2897 codex-adapter.mjs). */
308
+ export const interfaceVersion = 1;
309
+
310
+ /** Installed-module registration: exactly the one approved adapter ID. */
311
+ export function register(factory) {
312
+ factory('omp-direct', () => new OmpDirectAdapter());
313
+ }
314
+
315
+ export default OmpAdapter;