@aiwg/cli 2026.10.1 → 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 (166) 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 +210 -4
  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 +75 -27
  38. package/dist/src/decision/driver.js +3 -1
  39. package/dist/src/decision/entry.js +38 -6
  40. package/dist/src/decision/heldout/collector.js +10 -1
  41. package/dist/src/decision/index.js +1 -0
  42. package/dist/src/decision/interop.js +60 -8
  43. package/dist/src/decision/ledger.js +839 -0
  44. package/dist/src/decision/validate.js +6 -3
  45. package/dist/src/effects/dispatch-verifier.js +252 -0
  46. package/dist/src/effects/index.js +1 -0
  47. package/dist/src/features/installer.js +2 -2
  48. package/dist/src/flow/graph-metadata.js +5 -0
  49. package/dist/src/gates/providers/loader.js +3 -1
  50. package/dist/src/installation/manager-command.mjs +1 -1
  51. package/dist/src/installation/manager.mjs +17 -2
  52. package/dist/src/lint/runner.js +2 -2
  53. package/dist/src/mcp/adapters/codex-runtime.js +360 -56
  54. package/dist/src/mcp/cli.mjs +587 -77
  55. package/dist/src/mcp/config-file.mjs +279 -0
  56. package/dist/src/mcp/config-layers.mjs +348 -0
  57. package/dist/src/mcp/credentials.mjs +484 -0
  58. package/dist/src/mcp/grok-build-config.mjs +16 -26
  59. package/dist/src/mcp/helpers.mjs +106 -2
  60. package/dist/src/mcp/omp-config.mjs +50 -29
  61. package/dist/src/mcp/profiles.js +97 -23
  62. package/dist/src/mcp/profiles.mjs +99 -27
  63. package/dist/src/mcp/registry.js +264 -57
  64. package/dist/src/mcp/registry.mjs +198 -45
  65. package/dist/src/mcp/server.mjs +403 -20
  66. package/dist/src/mcp/toml-string.mjs +9 -0
  67. package/dist/src/mcp/toml-strip-mcp.mjs +135 -0
  68. package/dist/src/mcp/tool-filters.mjs +357 -0
  69. package/dist/src/mcp/tools/agentic-sandbox.mjs +3 -1
  70. package/dist/src/mcp/tools/decision.mjs +21 -3
  71. package/dist/src/mcp/tools/orchestration.mjs +38 -0
  72. package/dist/src/mcp/tools/subsystems.mjs +39 -8
  73. package/dist/src/mcp/tools/worker-invoke.mjs +1539 -0
  74. package/dist/src/mcp/tools/worker-shell-control.mjs +258 -0
  75. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +79 -0
  76. package/dist/src/memory/project-registry.js +2 -2
  77. package/dist/src/mission-protocol/codecs.js +639 -1
  78. package/dist/src/mission-protocol/types.js +110 -0
  79. package/dist/src/mission-protocol/worker-status.js +2 -0
  80. package/dist/src/models/config-loader.js +70 -13
  81. package/dist/src/models/grok-build-models.js +93 -41
  82. package/dist/src/models/model-capabilities.v1.json +3091 -24
  83. package/dist/src/models/model-discovery.js +862 -63
  84. package/dist/src/models/provider-policy.js +625 -11
  85. package/dist/src/models/qualification-digest.mjs +17 -0
  86. package/dist/src/models/resolver.js +259 -0
  87. package/dist/src/models/worker-direct-readiness.js +529 -0
  88. package/dist/src/models/worker-dispatch-offer.js +169 -0
  89. package/dist/src/models/worker-health-readiness.js +131 -0
  90. package/dist/src/models/worker-installation.mjs +879 -0
  91. package/dist/src/models/wrapper-route.js +18 -0
  92. package/dist/src/ops/registry.js +9 -14
  93. package/dist/src/plugins/standalone-packager.js +4 -3
  94. package/dist/src/process/bounded-child-process.mjs +387 -0
  95. package/dist/src/process/cli-command.mjs +75 -0
  96. package/dist/src/providers/capability-matrix.js +8 -0
  97. package/dist/src/providers/capability-matrix.yaml +270 -0
  98. package/dist/src/providers/grok-build-headless.js +43 -21
  99. package/dist/src/providers/provider-definitions.js +9 -1
  100. package/dist/src/providers/provider-definitions.mjs +9 -2
  101. package/dist/src/serve/executor-registry.js +16 -24
  102. package/dist/src/serve/fleet-mission-conductor.js +9 -0
  103. package/dist/src/serve/mission-conductor.js +9 -0
  104. package/dist/src/serve/shared-host-scheduler.js +2 -0
  105. package/dist/src/serve/worker-invocation.js +538 -0
  106. package/dist/src/serve/worker-stdin-frame.mjs +107 -0
  107. package/dist/src/sessions/adapter-registry.js +45 -0
  108. package/dist/src/sessions/adapters/generic.js +37 -3
  109. package/dist/src/sessions/adapters/grokbot-locator.js +111 -0
  110. package/dist/src/sessions/adapters/grokbot.js +423 -24
  111. package/dist/src/sessions/adapters/muse-workspace-scan.js +108 -0
  112. package/dist/src/sessions/adapters/muse.js +68 -29
  113. package/dist/src/sessions/adapters/pi.js +1 -0
  114. package/dist/src/sessions/batch-contracts.js +13 -1
  115. package/dist/src/sessions/batch-import.js +23 -42
  116. package/dist/src/sessions/candidates.js +47 -11
  117. package/dist/src/sessions/contracts.js +35 -0
  118. package/dist/src/sessions/coverage-preflight.js +134 -0
  119. package/dist/src/sessions/discovery.js +3 -0
  120. package/dist/src/sessions/grok-build-cli.js +67 -5
  121. package/dist/src/sessions/importer.js +24 -8
  122. package/dist/src/sessions/index.js +2 -0
  123. package/dist/src/sessions/repository.js +54 -13
  124. package/dist/src/sessions/workspace-discovery.js +99 -37
  125. package/dist/src/skills/adapters/clawhub.js +10 -8
  126. package/dist/src/skills/adapters/openclaw.js +10 -7
  127. package/dist/src/smiths/context-pipeline/parallelism-section.js +192 -3
  128. package/dist/src/smiths/context-pipeline/workspace-context.js +116 -9
  129. package/dist/src/smiths/hook-bridge/codex-translator.js +6 -5
  130. package/dist/src/smiths/mcpsmith/example.js +1 -1
  131. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +10 -14
  132. package/dist/src/storage/backends/logseq.js +68 -7
  133. package/dist/src/storage/fortemi-qualification-receipt.js +2 -2
  134. package/dist/src/testing/generators/test-code-generator.js +57 -30
  135. package/dist/src/uhp/client.js +11 -1
  136. package/dist/src/uhp/mission.js +5 -0
  137. package/dist/src/update/checker.mjs +5 -5
  138. package/dist/src/update/notifier.mjs +54 -2
  139. package/dist/src/writing/fidelity.js +18 -1
  140. package/package.json +18 -1
  141. package/schemas/mission-v1.schema.json +2429 -0
  142. package/schemas/models/model-catalog.v1.schema.json +301 -0
  143. package/schemas/models/model-config-compatibility.v1.schema.json +281 -0
  144. package/schemas/models/model-policy.v1.schema.json +14 -0
  145. package/schemas/models/provider-model-capabilities.v1.schema.json +1066 -0
  146. package/tools/_resolve-impl.mjs +39 -0
  147. package/tools/agents/deploy-agents.mjs +15 -2
  148. package/tools/agents/providers/base.mjs +49 -32
  149. package/tools/agents/providers/grokbot.mjs +11 -12
  150. package/tools/agents/providers/hermes.mjs +6 -3
  151. package/tools/agents/providers/omp.mjs +5 -2
  152. package/tools/agents/providers/openclaw-translator.mjs +19 -5
  153. package/tools/providers/deepseek-harness-transport.mjs +358 -0
  154. package/tools/providers/omp-transport.mjs +192 -0
  155. package/tools/ralph-external/lib/claude-adapter.mjs +389 -0
  156. package/tools/ralph-external/lib/codex-adapter.mjs +432 -0
  157. package/tools/ralph-external/lib/deepseek-harness-adapter.mjs +397 -0
  158. package/tools/ralph-external/lib/factory-adapter.mjs +503 -0
  159. package/tools/ralph-external/lib/muse-adapter.mjs +364 -0
  160. package/tools/ralph-external/lib/omp-adapter.mjs +315 -0
  161. package/tools/ralph-external/lib/opencode-adapter.mjs +575 -0
  162. package/tools/ralph-external/lib/openhuman-adapter.mjs +75 -0
  163. package/tools/ralph-external/lib/pi-adapter.mjs +333 -0
  164. package/tools/ralph-external/lib/provider-adapter.mjs +690 -0
  165. package/tools/ralph-external/lib/worker-health.mjs +439 -0
  166. package/vscode-extension/schemas/aiwg.config.v1.json +1953 -0
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url';
5
5
  import Ajv2020 from 'ajv/dist/2020.js';
6
6
  import addFormats from 'ajv-formats';
7
7
  import { canonicalJson } from '../security/artifact-trust.js';
8
- import { admitEntry, EntryAdmissionError } from './entry.js';
8
+ import { admissionClockStart, admitEntry, elapsedAdmissionMs, EntryAdmissionError } from './entry.js';
9
9
  import { DECISION_API_VERSION, DECISION_API_VERSION_STRUCTURED } from './types.js';
10
10
  const schemaFiles = {
11
11
  DecisionBinding: 'DecisionBinding.schema.json',
@@ -83,10 +83,13 @@ export function preprocessingLineageSchemaErrors(value) {
83
83
  return validate(value) ? null : ajvMessage(validate.errors);
84
84
  }
85
85
  export function artifactDigest(value) {
86
- const started = performance.now();
86
+ // Per-thread CPU time, not wall-clock: see entry.ts `admissionClockStart`
87
+ // for why a `performance.now()` budget here would be tripped by host
88
+ // scheduling contention rather than actual digesting cost.
89
+ const started = admissionClockStart();
87
90
  admitEntry(value);
88
91
  const canonical = canonicalJson(value);
89
- if (performance.now() - started > 1000) {
92
+ if (elapsedAdmissionMs(started) > 1000) {
90
93
  throw new EntryAdmissionError('time-budget', { bytes: Buffer.byteLength(canonical), entries: 0 });
91
94
  }
92
95
  return `sha256:${createHash('sha256').update(canonical).digest('hex')}`;
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Worker-dispatch effect verification (#2891, S1 direct/shared checkpoint).
3
+ *
4
+ * Modelled on the ledger lock recovery (`src/effects/lock-recovery.ts`): an
5
+ * extension kind whose verifier is registered only when the evidence it
6
+ * checks actually exists. Here the evidence is Tier 0 supervisor evidence
7
+ * held by the conductor — the dispatch record digest for one invocation,
8
+ * plus optional `file.digest` artifacts. Without supervisor evidence the
9
+ * factory returns null and the kind stays unregistered, so reconciliation
10
+ * through the existing ledger answers `unknown` / `verifier-missing` instead
11
+ * of manufacturing proof.
12
+ *
13
+ * Artifact checks reuse the built-in `file.digest` verifier by delegation;
14
+ * this module owns no second ledger and no file hashing of its own.
15
+ *
16
+ * Managed (S2+) durable dispatch proof and Tier 1 registration are out of
17
+ * scope; this verifier covers direct/shared supervisor evidence only.
18
+ *
19
+ * @see docs/contracts/effect-ledger.v1.md "Verifier interface"
20
+ */
21
+ import { createHash } from 'node:crypto';
22
+ import { constants } from 'node:fs';
23
+ import { open } from 'node:fs/promises';
24
+ import { resolve } from 'node:path';
25
+ import { EffectLedgerError } from './errors.js';
26
+ import { fileDigestVerifier } from './verifiers/file.js';
27
+ import { EffectVerifierError } from './verifiers/types.js';
28
+ export const DISPATCH_KIND = 'x.aiwg.worker-dispatch';
29
+ export const DISPATCH_VERIFIER_VERSION = '1.0.0';
30
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/;
31
+ /**
32
+ * Canonical invocation-ID shape for dispatch evidence (#2891). This is the
33
+ * SINGLE definition: `tools/ralph-external/lib/dispatch-evidence.mjs`
34
+ * imports this exact binding (through `tools/_resolve-impl.mjs`'s
35
+ * `importImpl`, since `tools/` cannot import `src/*.js` directly) rather
36
+ * than duplicating the literal, so the two can never drift.
37
+ */
38
+ export const INVOCATION_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/;
39
+ /** Conductor-owned evidence directories and files are owner-only. */
40
+ export const DISPATCH_EVIDENCE_DIR_MODE = 0o700;
41
+ export const DISPATCH_EVIDENCE_FILE_MODE = 0o600;
42
+ export function dispatchEffectTarget(invocationId) {
43
+ return `x-aiwg:dispatch/${invocationId}`;
44
+ }
45
+ /**
46
+ * Test-only race-injection seam (#2891 R1/R7), mirroring the equivalent hook
47
+ * in `tools/ralph-external/lib/dispatch-evidence.mjs`. No-op in production
48
+ * (`afterOpen` defaults to `null`); this package's own conformance suite
49
+ * sets it to a one-shot callback that performs a deterministic TOCTOU swap
50
+ * between `loadSupervisorEvidence`'s two `open()` calls, then clears
51
+ * itself. Never read outside `openTraced` below.
52
+ */
53
+ export const __raceTestHooks = { afterOpen: null };
54
+ async function openTraced(path, flags) {
55
+ const handle = await open(path, flags);
56
+ if (__raceTestHooks.afterOpen)
57
+ await __raceTestHooks.afterOpen(path);
58
+ return handle;
59
+ }
60
+ /**
61
+ * Read supervisor evidence from the conductor-owned evidence location. The
62
+ * evidence directory is opened `O_NOFOLLOW` ONCE and kept open for the
63
+ * whole read: the file `<invocationId>.json` is then opened through that
64
+ * SAME still-open directory descriptor, via `/proc/self/fd/<dirfd>/<name>`,
65
+ * never by re-resolving `<evidenceDir>/<invocationId>.json` as a fresh path
66
+ * string. This means a swap of `evidenceDir`'s path between validating the
67
+ * directory and reading the file cannot redirect the read onto an attacker-
68
+ * controlled location — the descriptor still refers to the original,
69
+ * validated directory regardless of what the path now names.
70
+ *
71
+ * Every check runs against an open fd: the evidence directory must be a
72
+ * real directory owned by us with mode exactly 0700, and the evidence file
73
+ * must be a regular file owned by us with mode exactly 0600 and exactly one
74
+ * link (no surprise hardlink). Supervisor evidence is never read from a
75
+ * worker-writable path: any symlink, wrong mode, wrong owner, extra link or
76
+ * unbound invocationId throws instead of verifying.
77
+ *
78
+ * When `expectedRecordDigest` is supplied (the sha256 of the evidence
79
+ * file's raw bytes, recorded by the conductor at capture time — for
80
+ * example from the effect ledger intent this evidence backs), the freshly
81
+ * read bytes must hash to it or the load throws. Shape and permission
82
+ * checks alone cannot detect in-place mutation of an already-captured
83
+ * evidence file; this closes that gap for any caller that has an earlier
84
+ * recorded digest to check against.
85
+ */
86
+ export async function loadSupervisorEvidence(options) {
87
+ const invocationId = options?.invocationId;
88
+ if (typeof invocationId !== 'string' || !INVOCATION_PATTERN.test(invocationId)) {
89
+ throw new EffectVerifierError('malformed-response', 'Supervisor evidence needs a valid invocation ID');
90
+ }
91
+ if (!options || typeof options.evidenceDir !== 'string' || !options.evidenceDir) {
92
+ throw new EffectVerifierError('container-unreadable', 'Supervisor evidence needs a conductor-owned evidence directory');
93
+ }
94
+ const dirResolved = resolve(options.evidenceDir);
95
+ let dirHandle;
96
+ try {
97
+ dirHandle = await openTraced(dirResolved, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | constants.O_NOFOLLOW);
98
+ }
99
+ catch {
100
+ throw new EffectVerifierError('container-unreadable', `Supervisor evidence directory is not a real directory: ${dirResolved}`);
101
+ }
102
+ try {
103
+ const dirStat = await dirHandle.stat();
104
+ const uid = typeof process.getuid === 'function' ? process.getuid() : null;
105
+ if (!dirStat.isDirectory() || (dirStat.mode & 0o777) !== DISPATCH_EVIDENCE_DIR_MODE
106
+ || (uid !== null && dirStat.uid !== uid)) {
107
+ throw new EffectVerifierError('container-unreadable', `Supervisor evidence directory is not owner-only 0700: ${dirResolved}`);
108
+ }
109
+ const name = `${invocationId}.json`;
110
+ const ref = `/proc/self/fd/${dirHandle.fd}/${name}`;
111
+ let handle;
112
+ try {
113
+ handle = await openTraced(ref, constants.O_RDONLY | constants.O_NOFOLLOW);
114
+ }
115
+ catch {
116
+ throw new EffectVerifierError('container-unreadable', `Supervisor evidence is unreadable: ${dirResolved}/${name}`);
117
+ }
118
+ try {
119
+ const stat = await handle.stat();
120
+ const fileUid = typeof process.getuid === 'function' ? process.getuid() : null;
121
+ if (!stat.isFile() || (stat.mode & 0o777) !== DISPATCH_EVIDENCE_FILE_MODE
122
+ || (fileUid !== null && stat.uid !== fileUid) || stat.nlink !== 1) {
123
+ throw new EffectVerifierError('container-unreadable', `Supervisor evidence is not an owner-only 0600 regular file with a single link: ${dirResolved}/${name}`);
124
+ }
125
+ const raw = await handle.readFile('utf8');
126
+ if (typeof options.expectedRecordDigest === 'string' && options.expectedRecordDigest) {
127
+ const actual = `sha256:${createHash('sha256').update(raw, 'utf8').digest('hex')}`;
128
+ if (actual !== options.expectedRecordDigest) {
129
+ throw new EffectVerifierError('malformed-response', 'Supervisor evidence does not match its recorded digest; mutation after capture is not trusted');
130
+ }
131
+ }
132
+ let parsed;
133
+ try {
134
+ parsed = JSON.parse(raw);
135
+ }
136
+ catch {
137
+ throw new EffectVerifierError('malformed-response', 'Supervisor evidence is not valid JSON');
138
+ }
139
+ if (!hasSupervisorEvidence(parsed) || parsed.invocationId !== invocationId) {
140
+ throw new EffectVerifierError('malformed-response', 'Supervisor evidence is not bound to this invocation');
141
+ }
142
+ return parsed;
143
+ }
144
+ finally {
145
+ await handle.close();
146
+ }
147
+ }
148
+ finally {
149
+ await dirHandle.close();
150
+ }
151
+ }
152
+ /** True only when actual supervisor evidence exists: a bound invocation and digest. */
153
+ export function hasSupervisorEvidence(evidence) {
154
+ if (!evidence || typeof evidence !== 'object')
155
+ return false;
156
+ const record = evidence;
157
+ if (typeof record.invocationId !== 'string' || !INVOCATION_PATTERN.test(record.invocationId))
158
+ return false;
159
+ if (typeof record.evidenceDigest !== 'string' || !DIGEST_PATTERN.test(record.evidenceDigest))
160
+ return false;
161
+ if (record.artifactPath !== undefined || record.artifactDigest !== undefined) {
162
+ if (typeof record.artifactPath !== 'string' || !record.artifactPath)
163
+ return false;
164
+ if (typeof record.artifactDigest !== 'string' || !DIGEST_PATTERN.test(record.artifactDigest))
165
+ return false;
166
+ }
167
+ return true;
168
+ }
169
+ function assertContextShape(context, target) {
170
+ const invocationId = context.invocationId;
171
+ const evidenceDigest = context.evidenceDigest;
172
+ if (typeof invocationId !== 'string' || !INVOCATION_PATTERN.test(invocationId)
173
+ || target !== dispatchEffectTarget(invocationId)) {
174
+ throw new EffectVerifierError('malformed-response', 'Dispatch effect has an unexpected target or invocation');
175
+ }
176
+ if (typeof evidenceDigest !== 'string' || !DIGEST_PATTERN.test(evidenceDigest)) {
177
+ throw new EffectVerifierError('malformed-response', 'Dispatch effect has no evidence digest');
178
+ }
179
+ return {
180
+ invocationId,
181
+ evidenceDigest,
182
+ ...(typeof context.artifactPath === 'string' ? { artifactPath: context.artifactPath } : {}),
183
+ ...(typeof context.artifactDigest === 'string' ? { artifactDigest: context.artifactDigest } : {}),
184
+ };
185
+ }
186
+ /**
187
+ * Build the dispatch verifier bound to one project's supervisor evidence, or
188
+ * null when no actual supervisor evidence exists. Callers register the
189
+ * returned verifier (for example through the `extensions` of
190
+ * `buildCliVerifierRegistry`) only when it is non-null; a null result means
191
+ * the kind stays unregistered and reconciliation reports unknown.
192
+ */
193
+ export function createDispatchVerifierIfSupervised(options) {
194
+ if (!options || typeof options.projectDir !== 'string' || !options.projectDir) {
195
+ throw new EffectLedgerError('usage', 'Dispatch verification needs a project directory', 'invalid-project');
196
+ }
197
+ if (!hasSupervisorEvidence(options.evidence))
198
+ return null;
199
+ const supervised = options.evidence;
200
+ // The record never picks its own strictness: artifacts root ONLY at the
201
+ // conductor-owned evidence directory, and the branch below is driven by
202
+ // what the SUPERVISOR evidence names, not by what the ledger context
203
+ // offers. No evidenceDir means no delegation target: `verify()` below
204
+ // reports `evidence-conflict` for an artifact check rather than silently
205
+ // widening the trusted root to `projectDir`.
206
+ const files = options.evidenceDir ? fileDigestVerifier({ root: options.evidenceDir }) : null;
207
+ return {
208
+ kind: DISPATCH_KIND,
209
+ version: DISPATCH_VERIFIER_VERSION,
210
+ canReportAbsent: true,
211
+ async verify(request) {
212
+ const context = assertContextShape((request.context ?? {}), request.target);
213
+ if (context.invocationId !== supervised.invocationId) {
214
+ throw new EffectVerifierError('malformed-response', 'Dispatch evidence is bound to a different invocation');
215
+ }
216
+ const expected = request.expected.digest ?? request.payloadDigest;
217
+ if (expected !== supervised.evidenceDigest || context.evidenceDigest !== supervised.evidenceDigest) {
218
+ return { result: 'unknown', reason: 'evidence-conflict', complete: false };
219
+ }
220
+ // Supervisor-driven: when supervised evidence names an artifact the
221
+ // verifier ALWAYS delegates to file.digest. A context that omits the
222
+ // artifact (or mismatches it), or a missing evidenceDir to delegate
223
+ // into, is evidence-conflict, never a present/complete downgrade and
224
+ // never a projectDir fallback.
225
+ if (supervised.artifactPath !== undefined) {
226
+ if (!files) {
227
+ return { result: 'unknown', reason: 'evidence-conflict', complete: false };
228
+ }
229
+ if (context.artifactPath !== supervised.artifactPath || context.artifactDigest !== supervised.artifactDigest) {
230
+ return { result: 'unknown', reason: 'evidence-conflict', complete: false };
231
+ }
232
+ const observation = await files.verify({
233
+ ...request,
234
+ kind: 'file.digest',
235
+ target: `file:${supervised.artifactPath}@${supervised.artifactDigest}`,
236
+ expected: { digest: supervised.artifactDigest },
237
+ });
238
+ if (observation.result !== 'present')
239
+ return observation;
240
+ return {
241
+ result: 'present', reason: 'state-match', complete: true,
242
+ evidence: { invocationId: context.invocationId, evidenceDigest: supervised.evidenceDigest, artifact: supervised.artifactPath },
243
+ };
244
+ }
245
+ return {
246
+ result: 'present', reason: 'state-match', complete: true,
247
+ evidence: { invocationId: context.invocationId, evidenceDigest: supervised.evidenceDigest },
248
+ };
249
+ },
250
+ };
251
+ }
252
+ //# sourceMappingURL=dispatch-verifier.js.map
@@ -19,6 +19,7 @@ export { writeCheckpoint } from './checkpoint.js';
19
19
  export { checkpointDigest, verifyLedger, } from './verify.js';
20
20
  export { EFFECT_RETENTION_POLICY, purgeEffect } from './lifecycle.js';
21
21
  export { LEDGER_LOCK_NAME_PATTERN, LOCK_RECOVERY_KIND, LOCK_RECOVERY_VERIFIER_VERSION, inspectLedgerLock, inspectLedgerLocks, ledgerLockRecoveryVerifier, lockRecoveryTarget, recoverStaleLedgerLock, } from './lock-recovery.js';
22
+ export { DISPATCH_KIND, DISPATCH_VERIFIER_VERSION, INVOCATION_PATTERN, createDispatchVerifierIfSupervised, dispatchEffectTarget, hasSupervisorEvidence, loadSupervisorEvidence, } from './dispatch-verifier.js';
22
23
  export { assertDigestOnly, containsRestrictedMaterial } from './redaction.js';
23
24
  export { EFFECT_SCHEMA_IDS, effectSchemaErrors, isEffectSchemaValid } from './schema.js';
24
25
  export { decisionLinkOf, verifyDecisionLinks, } from './decision-links.js';
@@ -1,4 +1,4 @@
1
- import { spawnSync } from 'node:child_process';
1
+ import { boundedSpawnSync } from '../process/bounded-child-process.mjs';
2
2
  import { promises as fs } from 'node:fs';
3
3
  import path from 'node:path';
4
4
  import { FEATURE_CATALOG, getFeature } from './catalog.js';
@@ -46,7 +46,7 @@ export async function installFeature(name, options = {}) {
46
46
  const root = options.root ?? getFeaturesRoot();
47
47
  await prepareFeatureManifest(feature, root);
48
48
  const npmCommand = options.npmCommand ?? (process.platform === 'win32' ? 'npm.cmd' : 'npm');
49
- const result = spawnSync(npmCommand, ['install', '--prefix', root, '--no-audit', '--no-fund'], { stdio: 'inherit', env: process.env });
49
+ const result = boundedSpawnSync(npmCommand, ['install', '--prefix', root, '--no-audit', '--no-fund'], 'long', { stdio: 'inherit', env: process.env });
50
50
  if (result.error)
51
51
  throw result.error;
52
52
  if (result.status !== 0) {
@@ -1,3 +1,4 @@
1
+ import { assertMissionWorkerHandoff } from '../mission-protocol/codecs.js';
1
2
  /** A2A/telemetry metadata key reserved for the optional Flow graph profile. */
2
3
  export const AIWG_GRAPH_METADATA_KEY = 'aiwg.flow.graph';
3
4
  function nonEmpty(value) {
@@ -15,9 +16,12 @@ export function isGraphExecutionMetadata(value) {
15
16
  && nonEmpty(item.nodeRunId);
16
17
  }
17
18
  export function graphMetadataRecord(value) {
19
+ assertMissionWorkerHandoff(value, 'graph');
18
20
  return { [AIWG_GRAPH_METADATA_KEY]: value };
19
21
  }
20
22
  export function extractGraphMetadata(metadata) {
23
+ if (metadata)
24
+ assertMissionWorkerHandoff({ metadata }, 'graph');
21
25
  const value = metadata?.[AIWG_GRAPH_METADATA_KEY];
22
26
  return isGraphExecutionMetadata(value) ? value : undefined;
23
27
  }
@@ -28,6 +32,7 @@ export function extractGraphMetadata(metadata) {
28
32
  * member of this contract and is therefore dropped by construction.
29
33
  */
30
34
  export function projectGraphMetadata(value, audience) {
35
+ assertMissionWorkerHandoff(value, 'graph');
31
36
  if (!isGraphExecutionMetadata(value))
32
37
  throw new Error('Invalid graph execution metadata.');
33
38
  if (audience === 'internal')
@@ -7,6 +7,7 @@ import { GateProviderEntrySchema } from '../../extensions/manifest.js';
7
7
  import { artifactDigest } from '../../decision/validate.js';
8
8
  import { providerSourceDigest } from './types.js';
9
9
  import { ISOLATED_CHILD_SOURCE } from './isolated-child-source.js';
10
+ import { positiveTimeoutMs } from '../../process/bounded-child-process.mjs';
10
11
  /**
11
12
  * Addon/extension metric-provider loading, isolated rework (P1-P5).
12
13
  *
@@ -684,7 +685,8 @@ function runIsolatedRecordSync(record, args, limits) {
684
685
  const completed = spawnSync(plan.command, plan.args, {
685
686
  env: plan.env,
686
687
  input,
687
- timeout: limits.timeoutMs,
688
+ // Validated upstream; clamped here so the bound holds at the call site.
689
+ timeout: positiveTimeoutMs(limits.timeoutMs, GATE_PROVIDER_DEFAULT_TIMEOUT_MS),
688
690
  killSignal: 'SIGKILL',
689
691
  maxBuffer: limits.maxOutputBytes,
690
692
  encoding: 'utf8',
@@ -20,7 +20,7 @@ export function resolveManagerCommand(file, args, options = {}) {
20
20
  const command = `"${[file, ...args].map(quoteCmdArgument).join(' ')}"`;
21
21
  return {
22
22
  file: commandInterpreter,
23
- args: ['/d', '/s', '/c', command],
23
+ args: ['/d', '/v:off', '/s', '/c', command],
24
24
  // The payload above already contains cmd.exe-native quoting. Instruct
25
25
  // Node not to apply MSVCRT escaping to those embedded quotes (#173).
26
26
  windowsVerbatimArguments: true,
@@ -1,4 +1,4 @@
1
- import { execFileSync } from 'node:child_process';
1
+ import { boundedExecFileSync } from '../process/bounded-child-process.mjs';
2
2
  import {
3
3
  accessSync,
4
4
  constants,
@@ -50,7 +50,22 @@ function resolveExecutable(name, options = {}) {
50
50
  if (existsSync(besideNode)) return canonicalPath(besideNode);
51
51
  try {
52
52
  const finder = process.platform === 'win32' ? 'where.exe' : 'which';
53
- const found = execFileSync(finder, [name], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] })
53
+ // Go/no-go review, blocker 1B: this runs unconditionally on first
54
+ // launch (creating the installation identity) for *every* command,
55
+ // including a credential-handling one — before `bin/aiwg.mjs` even
56
+ // knows which command that is. `which`/`where.exe` only need `PATH`
57
+ // (and, on Windows, `PATHEXT`/`SystemRoot` for its own resolution), so
58
+ // this never inherits the whole parent environment the way an absent
59
+ // `env` option otherwise would.
60
+ const lookupEnv = {
61
+ PATH: env.PATH ?? '',
62
+ ...(process.platform === 'win32' ? { PATHEXT: env.PATHEXT ?? '', SystemRoot: env.SystemRoot ?? '' } : {}),
63
+ };
64
+ const found = boundedExecFileSync(finder, [name], 'quick', {
65
+ encoding: 'utf8',
66
+ stdio: ['ignore', 'pipe', 'ignore'],
67
+ env: lookupEnv,
68
+ })
54
69
  .split(/\r?\n/, 1)[0]?.trim();
55
70
  return found ? canonicalPath(found) : null;
56
71
  } catch {
@@ -7,7 +7,7 @@
7
7
  * @issue #810
8
8
  */
9
9
  import fs from 'fs';
10
- import { spawnSync } from 'child_process';
10
+ import { boundedSpawnSync } from '../process/bounded-child-process.mjs';
11
11
  import fsp from 'fs/promises';
12
12
  import path from 'path';
13
13
  import { glob } from 'glob';
@@ -560,7 +560,7 @@ function dropGitignored(targetDir, files) {
560
560
  if (files.length === 0)
561
561
  return files;
562
562
  try {
563
- const result = spawnSync('git', ['check-ignore', '--stdin'], {
563
+ const result = boundedSpawnSync('git', ['check-ignore', '--stdin'], 'quick', {
564
564
  cwd: targetDir,
565
565
  input: `${files.join('\n')}\n`,
566
566
  encoding: 'utf8',