@aiwg/cli 2026.10.3 → 2026.10.5

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 (279) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/model-capabilities.v1.json +96 -4
  3. package/bin/aiwg.mjs +50 -27
  4. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  5. package/dist/src/artifacts/browser-export.js +6 -0
  6. package/dist/src/artifacts/checksum-manifest.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +173 -92
  8. package/dist/src/artifacts/citation-table-schema.js +59 -0
  9. package/dist/src/artifacts/cli.js +69 -18
  10. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  11. package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
  12. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  13. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  14. package/dist/src/artifacts/discovery-eval.js +6 -4
  15. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  16. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  17. package/dist/src/artifacts/framework-capabilities.js +171 -0
  18. package/dist/src/artifacts/framework-index-paths.js +73 -0
  19. package/dist/src/artifacts/framework-source-digest.js +75 -0
  20. package/dist/src/artifacts/index-builder.js +286 -131
  21. package/dist/src/artifacts/index-files.js +132 -18
  22. package/dist/src/artifacts/index-preparation.js +130 -0
  23. package/dist/src/artifacts/index-refresh.js +591 -0
  24. package/dist/src/artifacts/index-worker.mjs +11 -0
  25. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  26. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  27. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  28. package/dist/src/artifacts/query-engine.js +410 -88
  29. package/dist/src/artifacts/relations-builder.js +168 -0
  30. package/dist/src/artifacts/relations.js +148 -0
  31. package/dist/src/artifacts/stats.js +5 -2
  32. package/dist/src/artifacts/types.js +30 -10
  33. package/dist/src/artifacts/watcher.js +1 -1
  34. package/dist/src/auth/credential-store.js +2 -1
  35. package/dist/src/catalog/cli.d.mts +5 -0
  36. package/dist/src/channel/manager.d.mts +20 -0
  37. package/dist/src/channel/manager.mjs +19 -2
  38. package/dist/src/cli/command-log.js +2 -3
  39. package/dist/src/cli/handlers/artifacts.js +3 -0
  40. package/dist/src/cli/handlers/auth.js +3 -0
  41. package/dist/src/cli/handlers/decision.js +19 -12
  42. package/dist/src/cli/handlers/diagnose.js +4 -3
  43. package/dist/src/cli/handlers/effect.js +4 -2
  44. package/dist/src/cli/handlers/evidence.js +3 -0
  45. package/dist/src/cli/handlers/help.js +1 -0
  46. package/dist/src/cli/handlers/index.js +3 -1
  47. package/dist/src/cli/handlers/init.js +4 -1
  48. package/dist/src/cli/handlers/lint.js +5 -0
  49. package/dist/src/cli/handlers/models.js +104 -8
  50. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  51. package/dist/src/cli/handlers/refresh.js +12 -0
  52. package/dist/src/cli/handlers/resource-versions.js +3 -0
  53. package/dist/src/cli/handlers/rules.js +195 -0
  54. package/dist/src/cli/handlers/run.js +4 -0
  55. package/dist/src/cli/handlers/runtime-info.js +1 -2
  56. package/dist/src/cli/handlers/script-runner.js +3 -2
  57. package/dist/src/cli/handlers/session.js +19 -9
  58. package/dist/src/cli/handlers/sessions.js +137 -43
  59. package/dist/src/cli/handlers/setup-manifest.js +39 -44
  60. package/dist/src/cli/handlers/setup.js +41 -0
  61. package/dist/src/cli/handlers/steward.js +11 -0
  62. package/dist/src/cli/handlers/subcommands.js +57 -2
  63. package/dist/src/cli/handlers/uhp.js +3 -0
  64. package/dist/src/cli/handlers/use.js +294 -55
  65. package/dist/src/cli/handlers/utilities.js +26 -10
  66. package/dist/src/cli/handlers/version.js +8 -0
  67. package/dist/src/cli/router.js +15 -1
  68. package/dist/src/cli/services/deployment-verification.js +19 -9
  69. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  70. package/dist/src/cli/skill-usage.js +2 -3
  71. package/dist/src/config/aiwg-config.js +87 -37
  72. package/dist/src/config/cli.js +111 -5
  73. package/dist/src/config/decision-config.js +106 -0
  74. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  75. package/dist/src/config/gitignore.js +50 -0
  76. package/dist/src/config/index-graph-overrides.js +36 -0
  77. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  78. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  79. package/dist/src/config/user-config-dir.d.mts +9 -0
  80. package/dist/src/config/user-config.js +2 -1
  81. package/dist/src/config/user-registry.js +23 -15
  82. package/dist/src/config/user-state.js +7 -0
  83. package/dist/src/decision/ask.js +203 -43
  84. package/dist/src/decision/context-live-qualification.js +2 -2
  85. package/dist/src/decision/driver.js +13 -8
  86. package/dist/src/decision/ensemble/aggregate.js +23 -10
  87. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  88. package/dist/src/decision/entry.js +2 -15
  89. package/dist/src/decision/graph-live-qualification.js +1 -1
  90. package/dist/src/decision/graph-run.js +9 -0
  91. package/dist/src/decision/heldout/collector.js +5 -13
  92. package/dist/src/decision/heldout/contract.js +30 -7
  93. package/dist/src/decision/jev-credentials.js +15 -1
  94. package/dist/src/decision/ledger.js +131 -28
  95. package/dist/src/decision/offload-activation.js +137 -0
  96. package/dist/src/decision/offload.js +550 -0
  97. package/dist/src/decision/qualification/load.js +4 -6
  98. package/dist/src/decision/savings.js +28 -0
  99. package/dist/src/effects/types.js +5 -1
  100. package/dist/src/effects/verifiers/git.js +2 -1
  101. package/dist/src/effects/verifiers/tracker.js +26 -14
  102. package/dist/src/extensions/commands/definitions.js +19 -0
  103. package/dist/src/extensions/project-local-remove.js +36 -1
  104. package/dist/src/gates/providers/loader.js +61 -17
  105. package/dist/src/installation/manager-command.d.mts +18 -0
  106. package/dist/src/installation/manager-command.mjs +14 -2
  107. package/dist/src/installation/manager.d.mts +49 -0
  108. package/dist/src/installation/manager.mjs +78 -3
  109. package/dist/src/issues/cli.js +4 -4
  110. package/dist/src/issues/local.js +24 -5
  111. package/dist/src/jobs/runner.js +1 -1
  112. package/dist/src/lint/cli.js +27 -1
  113. package/dist/src/lint/loader.js +2 -1
  114. package/dist/src/lint/reporters.js +25 -1
  115. package/dist/src/lint/rule-activation.js +43 -0
  116. package/dist/src/lint/runner.js +137 -62
  117. package/dist/src/mcp/adapters/codex-runtime.js +7 -1
  118. package/dist/src/mcp/claude-install.d.mts +4 -0
  119. package/dist/src/mcp/claude-install.mjs +41 -0
  120. package/dist/src/mcp/cli.d.mts +1 -0
  121. package/dist/src/mcp/cli.mjs +36 -11
  122. package/dist/src/mcp/config-file.d.mts +28 -0
  123. package/dist/src/mcp/config-layers.d.mts +36 -0
  124. package/dist/src/mcp/credentials.d.mts +51 -0
  125. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  126. package/dist/src/mcp/grok-build-config.mjs +1 -1
  127. package/dist/src/mcp/helpers.mjs +3 -2
  128. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  129. package/dist/src/mcp/muse-native-config.mjs +1 -0
  130. package/dist/src/mcp/omp-config.d.mts +4 -0
  131. package/dist/src/mcp/registry.js +3 -93
  132. package/dist/src/mcp/registry.mjs +3 -87
  133. package/dist/src/mcp/server-config.d.mts +3 -0
  134. package/dist/src/mcp/server-config.mjs +89 -0
  135. package/dist/src/mcp/server.mjs +37 -375
  136. package/dist/src/mcp/test-simple.mjs +2 -1
  137. package/dist/src/mcp/toml-editor.d.mts +4 -0
  138. package/dist/src/mcp/toml-string.d.mts +1 -0
  139. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  140. package/dist/src/mcp/tool-filters.d.mts +46 -0
  141. package/dist/src/mcp/tools/decision.mjs +12 -11
  142. package/dist/src/mcp/tools/discovery.mjs +62 -55
  143. package/dist/src/mcp/tools/orchestration.mjs +12 -2
  144. package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
  145. package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
  146. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
  147. package/dist/src/mission-protocol/codecs.js +8 -0
  148. package/dist/src/models/config-loader.js +7 -2
  149. package/dist/src/models/model-capabilities.v1.json +96 -4
  150. package/dist/src/models/model-discovery.js +76 -11
  151. package/dist/src/models/provider-policy.js +101 -7
  152. package/dist/src/models/qualification-digest.d.mts +2 -0
  153. package/dist/src/models/resolver.js +12 -10
  154. package/dist/src/models/worker-direct-readiness.js +65 -44
  155. package/dist/src/models/worker-dispatch-offer.js +3 -3
  156. package/dist/src/models/worker-health-readiness.js +16 -3
  157. package/dist/src/models/worker-installation.d.mts +39 -0
  158. package/dist/src/models/worker-installation.mjs +135 -28
  159. package/dist/src/process/bounded-child-process.d.mts +84 -0
  160. package/dist/src/process/bounded-child-process.mjs +43 -0
  161. package/dist/src/process/cli-command.d.mts +16 -0
  162. package/dist/src/process/signal-child.d.mts +6 -0
  163. package/dist/src/process/signal-child.mjs +7 -0
  164. package/dist/src/providers/deployed-skill-roots.js +20 -0
  165. package/dist/src/providers/grok-build-acp.js +3 -2
  166. package/dist/src/providers/grok-build-headless.js +12 -4
  167. package/dist/src/providers/grok-build-inspect.js +1 -0
  168. package/dist/src/providers/omp-agent.d.mts +8 -0
  169. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  170. package/dist/src/providers/omp-paths.d.mts +13 -0
  171. package/dist/src/providers/skill-ownership.d.mts +9 -0
  172. package/dist/src/providers/skill-ownership.mjs +88 -0
  173. package/dist/src/rules/activation.d.mts +21 -0
  174. package/dist/src/rules/activation.mjs +62 -0
  175. package/dist/src/rules/inline-budget.d.mts +26 -0
  176. package/dist/src/rules/inline-budget.mjs +66 -0
  177. package/dist/src/rules/inventory.js +129 -0
  178. package/dist/src/rules/pin-state.js +73 -0
  179. package/dist/src/rules/pins.d.mts +14 -0
  180. package/dist/src/rules/pins.mjs +112 -0
  181. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  182. package/dist/src/serve/mission-conductor.js +38 -12
  183. package/dist/src/serve/worker-invocation.js +30 -10
  184. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  185. package/dist/src/sessions/adapter-registry.js +23 -19
  186. package/dist/src/sessions/adapters/claude.js +39 -54
  187. package/dist/src/sessions/adapters/codex.js +95 -67
  188. package/dist/src/sessions/adapters/copilot.js +4 -4
  189. package/dist/src/sessions/adapters/cursor.js +40 -57
  190. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  191. package/dist/src/sessions/adapters/factory.js +31 -27
  192. package/dist/src/sessions/adapters/generic.js +10 -10
  193. package/dist/src/sessions/adapters/grok-build.js +11 -11
  194. package/dist/src/sessions/adapters/grokbot.js +24 -13
  195. package/dist/src/sessions/adapters/hermes.js +27 -29
  196. package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
  197. package/dist/src/sessions/adapters/muse.js +150 -93
  198. package/dist/src/sessions/adapters/omp.js +28 -14
  199. package/dist/src/sessions/adapters/openclaw.js +89 -79
  200. package/dist/src/sessions/adapters/opencode.js +27 -24
  201. package/dist/src/sessions/adapters/openhuman.js +18 -60
  202. package/dist/src/sessions/adapters/pi.js +29 -14
  203. package/dist/src/sessions/adapters/warp.js +3 -3
  204. package/dist/src/sessions/adapters/windsurf.js +19 -48
  205. package/dist/src/sessions/batch-contracts.js +16 -8
  206. package/dist/src/sessions/batch-import.js +252 -12
  207. package/dist/src/sessions/contracts.js +50 -3
  208. package/dist/src/sessions/coverage-preflight.js +27 -2
  209. package/dist/src/sessions/export-plan.js +4 -3
  210. package/dist/src/sessions/import-exclusions.js +72 -0
  211. package/dist/src/sessions/import-lease.js +34 -18
  212. package/dist/src/sessions/import-limits.js +37 -0
  213. package/dist/src/sessions/importer.js +60 -38
  214. package/dist/src/sessions/lifecycle.js +45 -0
  215. package/dist/src/sessions/policy.js +145 -26
  216. package/dist/src/sessions/readers.js +106 -53
  217. package/dist/src/sessions/refresh.js +105 -0
  218. package/dist/src/sessions/repository.js +151 -54
  219. package/dist/src/sessions/workspace-discovery.js +292 -60
  220. package/dist/src/sessions/workspace-matching.js +107 -0
  221. package/dist/src/skills/importer.js +1 -0
  222. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  223. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  224. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  225. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  226. package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
  227. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  228. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  229. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
  230. package/dist/src/storage/backends/fs.js +18 -76
  231. package/dist/src/storage/backends/obsidian.js +1 -0
  232. package/dist/src/tracker/forge-http.js +2 -1
  233. package/dist/src/update/checker.d.mts +2 -0
  234. package/dist/src/update/service.d.mts +22 -0
  235. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  236. package/dist/src/utils/contained-filesystem.mjs +683 -0
  237. package/dist/src/utils/temporary-directory.js +36 -0
  238. package/dist/src/writing/example-generator.js +1 -1
  239. package/package.json +15 -2
  240. package/schemas/models/model-catalog.v1.schema.json +1 -1
  241. package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
  242. package/tools/_resolve-impl.mjs +13 -15
  243. package/tools/agents/deploy-agents.mjs +49 -21
  244. package/tools/agents/providers/base.mjs +104 -134
  245. package/tools/agents/providers/claude.mjs +8 -24
  246. package/tools/agents/providers/codex.mjs +18 -11
  247. package/tools/agents/providers/cursor.mjs +19 -18
  248. package/tools/agents/providers/grok-build.mjs +3 -1
  249. package/tools/agents/providers/hermes.mjs +57 -47
  250. package/tools/agents/providers/openclaw.mjs +9 -4
  251. package/tools/agents/providers/pi.mjs +5 -0
  252. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  253. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  254. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  255. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  256. package/tools/agents/providers/warp.mjs +20 -26
  257. package/tools/cli/decision-offload-report.mjs +40 -0
  258. package/tools/cli/workspace-status.mjs +862 -0
  259. package/tools/plugin/package-plugins.mjs +186 -8
  260. package/tools/plugin/plugin-links.mjs +114 -0
  261. package/tools/providers/antigravity-transport.mjs +3 -1
  262. package/tools/providers/deepseek-harness-transport.mjs +6 -4
  263. package/tools/providers/omp-transport.mjs +4 -2
  264. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  265. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  266. package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
  267. package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
  268. package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
  269. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  270. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  271. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  272. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  273. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  274. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  275. package/tools/ralph-external/process-monitor.mjs +1017 -0
  276. package/tools/ralph-external/session-launcher.mjs +5057 -0
  277. package/tools/scripts/resource-preflight.mjs +541 -0
  278. package/tools/skills/deploy-skills-codex.mjs +2 -1
  279. package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
@@ -202,8 +202,7 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
202
202
  const { RuntimeDiscovery } = await import('../../smiths/toolsmith/runtime-discovery.mjs');
203
203
  const discovery = new RuntimeDiscovery();
204
204
  if (hasDiscover) {
205
- // Full discovery
206
- const catalog = await discovery.discover();
205
+ const catalog = await discovery.discover({ force: args.includes('--force') });
207
206
  if (hasJson) {
208
207
  console.log(JSON.stringify(catalog, null, 2));
209
208
  }
@@ -8,6 +8,7 @@
8
8
  * @source @src/cli/router.ts
9
9
  * @issue #33
10
10
  */
11
+ import { signalChild } from '../../process/signal-child.mjs';
11
12
  import { spawn } from 'child_process';
12
13
  import path from 'path';
13
14
  /**
@@ -73,12 +74,12 @@ export class DefaultScriptRunner {
73
74
  // does not exit within the deadline. Both kill() calls are wrapped
74
75
  // because they can throw ESRCH on an already-exited child.
75
76
  try {
76
- child.kill('SIGTERM');
77
+ signalChild(child, 'SIGTERM');
77
78
  }
78
79
  catch { /* already exited */ }
79
80
  killEscalationId = setTimeout(() => {
80
81
  try {
81
- child.kill('SIGKILL');
82
+ signalChild(child, 'SIGKILL');
82
83
  }
83
84
  catch { /* already exited */ }
84
85
  }, killDeadlineMs);
@@ -32,10 +32,9 @@
32
32
  */
33
33
  import { spawnSync } from 'child_process';
34
34
  import { readFile } from 'node:fs/promises';
35
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
36
- import { tmpdir } from 'node:os';
35
+ import { mkdtempSync, rmSync, writeFileSync, realpathSync, statSync } from 'node:fs';
37
36
  import { join } from 'node:path';
38
- import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
37
+ import { boundedSpawn, boundedSpawnSync } from '../../process/bounded-child-process.mjs';
39
38
  import { existsSync } from 'node:fs';
40
39
  import { ensureRuntimeHome, writeProfileConfig, launchWithProfile } from '../../mcp/adapters/codex-runtime.js';
41
40
  import { getFrameworkRoot } from '../../channel/manager.mjs';
@@ -314,8 +313,8 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
314
313
  }
315
314
  // File transport (opencode): the prompt never touches argv or stdin — it
316
315
  // is written to a private temp file only this process can read, passed
317
- // by path. `mkdtemp()` under the system temp directory (never a custom
318
- // location — #3062 second review, finding 10) already gives the
316
+ // by path. `mkdtemp()` under the independently verified system temp
317
+ // directory gives the
319
318
  // directory itself a private, unpredictable name owned by this process;
320
319
  // the file within it additionally gets an explicit `0600` mode via an
321
320
  // exclusive create (`flag: 'wx'` — fails rather than silently
@@ -328,7 +327,15 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
328
327
  // `launchProvider()`, keyed off `plan.tempDir`) handles the ordinary
329
328
  // "launch ran, now clean up" path; this one exists only for the write
330
329
  // itself failing before a `plan` is ever returned to that caller.
331
- const tempDir = mkdtempSync(join(tmpdir(), 'aiwg-session-prompt-'));
330
+ // os.tmpdir() trusts TMPDIR/TEMP/TMP, which can point into the project.
331
+ // Select and validate the OS location independently of those overrides.
332
+ const systemTemp = realpathSync(process.platform === 'win32' ? join(process.env.SystemRoot ?? 'C:/Windows', 'Temp') : '/tmp');
333
+ const info = statSync(systemTemp);
334
+ if (!info.isDirectory() || (process.platform !== 'win32'
335
+ && (info.uid !== 0 && info.uid !== process.getuid?.() || (info.mode & 0o022) !== 0 && (info.mode & 0o1000) === 0))) {
336
+ throw new Error('Unsafe system temporary directory for session prompt');
337
+ }
338
+ const tempDir = mkdtempSync(join(systemTemp, 'aiwg-session-prompt-'));
332
339
  try {
333
340
  const promptPath = join(tempDir, 'prompt.txt');
334
341
  writeFileSync(promptPath, prompt, { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
@@ -355,7 +362,7 @@ function buildHeadlessLaunch(provider, binaryArgs, prompt) {
355
362
  * and without a terminal, this refuses before spawning anything that would
356
363
  * hang.
357
364
  */
358
- function launchProvider(provider, mcpInjected, mcpConfigPath, profile, settingsPath, prompt, interactive) {
365
+ async function launchProvider(provider, mcpInjected, mcpConfigPath, profile, settingsPath, prompt, interactive) {
359
366
  const cfg = getProviderConfig(provider);
360
367
  if (!isSpawnableProvider(provider)) {
361
368
  // Not spawnable — a prompt can never be delivered to a binary that does
@@ -424,11 +431,14 @@ ${cfg.guidanceMessage ?? `Open ${cfg.name} in your project directory to begin.`}
424
431
  // 8); stdin-transport providers get it via `input` (never inherited
425
432
  // from the parent), file-transport providers get it via a private
426
433
  // temp file path already baked into `plan.args`.
427
- const result = boundedSpawnSync(cfg.binary, plan.args, 'long', {
434
+ const launchOptions = {
428
435
  stdio: plan.input !== undefined ? ['pipe', 'inherit', 'inherit'] : ['ignore', 'inherit', 'inherit'],
429
436
  env: process.env,
430
437
  ...(plan.input !== undefined ? { input: plan.input } : {}),
431
- });
438
+ };
439
+ const result = plan.tempDir
440
+ ? await boundedSpawn(cfg.binary, plan.args, 'long', { ...launchOptions, stdio: ['ignore', 'inherit', 'inherit'] }, () => rmSync(plan.tempDir, { recursive: true, force: true }))
441
+ : boundedSpawnSync(cfg.binary, plan.args, 'long', launchOptions);
432
442
  const { exitCode, message } = exitCodeForSpawnResult(result, cfg.binary);
433
443
  return message ? { exitCode, message } : { exitCode };
434
444
  }
@@ -2,8 +2,12 @@ import { existsSync, linkSync, mkdirSync, readFileSync, realpathSync, renameSync
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { basename, dirname, isAbsolute, join, resolve, } from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
+ import { providerScopeWarnings } from '../../sessions/coverage-preflight.js';
5
6
  import { CLAUDE_ADAPTER_VERSION, ClaudeSessionAdapter, CODEX_ADAPTER_VERSION, CodexSessionAdapter, COPILOT_ADAPTER_VERSION, CopilotSessionAdapter, CURSOR_ADAPTER_VERSION, CursorSessionAdapter, FACTORY_ADAPTER_VERSION, FactorySessionAdapter, HERMES_ADAPTER_VERSION, HermesSessionAdapter, OPENCODE_ADAPTER_VERSION, OpenCodeSessionAdapter, OPENCLAW_ADAPTER_VERSION, OpenClawSessionAdapter, OPENHUMAN_ADAPTER_VERSION, OpenHumanSessionAdapter, GROKBOT_ADAPTER_VERSION, GROKBOT_NATIVE_REPLICA_LOCATOR_CLASS, GrokbotSessionAdapter, GROK_BUILD_ADAPTER_VERSION, GROK_BUILD_CLI_EXPORT_LOCATOR_CLASS, GrokBuildSessionAdapter, grokBuildList, grokBuildSearch, grokBuildExport, grokHeadlessSessionIdFromFile, MUSE_ADAPTER_VERSION, MuseSessionAdapter, PI_ADAPTER_VERSION, PiSessionAdapter, DEEPSEEK_HARNESS_ADAPTER_VERSION, DeepSeekHarnessSessionAdapter, OmpSessionAdapter, OMP_ADAPTER_VERSION, WARP_ADAPTER_VERSION, WarpSessionAdapter, DEVIN_DESKTOP_ADAPTER_VERSION, DevinDesktopSessionAdapter, CandidateExtractionService, GENERIC_ADAPTER_VERSION, GenericSessionInterchangeAdapter, IncrementalSessionImporter, ImportLeaseContentionError, FilesystemMemoryDestination, FilesystemPromotionDispositionCoordinator, MemoryPromotionGateway, SESSION_CONTRACT_VERSION, SessionContractError, SessionRepository, SessionSourceSchema, StructuralCandidateExtractor, resolveMemoryConsumerManifest, assertSessionProviderId, acquireImportLease, defaultDiscoveryManifestPath, discoverWorkspaceHistories, createSessionAdapterRegistry, coveragePreflight, expectedSessionProviders, deriveSessionTimeline, importDiscoveryManifest, previewDiscoveryImport, publicDiscoveryManifest, readDiscoveryManifest, redactSourceLocator, sha256, parseTimelineGap, writeDiscoveryManifest, buildSessionExportPlan, reverifySessionExportPlan, buildSessionAiwgFortemiIndexExport, recoverAiwgFortemiIndexFromFullV1Shard, embedAttachmentsInFullV1Shard, recoverEmbeddedAttachments, SESSION_EXPORT_PLAN_SCHEMA_VERSION, } from '../../sessions/index.js';
7
+ import { resolveImportExclusions } from '../../sessions/import-exclusions.js';
8
+ import { resolveImportLimits } from '../../sessions/import-limits.js';
6
9
  import { classifyGrokbotBlobName } from '../../sessions/adapters/grokbot-locator.js';
10
+ import { readAiwgConfig } from '../../config/aiwg-config.js';
7
11
  const JSON_CONTRACT_VERSION = '1.0.0';
8
12
  async function createLineMemoryPromotionDestination(projectRoot, manifestPath) {
9
13
  const modulePath = resolve(dirname(manifestPath), 'commands', 'line-memory.mjs');
@@ -48,28 +52,30 @@ Commands:
48
52
  promote <id> <version> Preview promotion; use --confirm to write
49
53
  tag <session-id> <tag> Add a catalog tag
50
54
  relocate <source-id> <file> Update AIWG source-location metadata
51
- reindex Rebuild catalog indexes
55
+ reindex --workspace <path-or-id> | --all-workspaces Rebuild catalog indexes
52
56
  delete <session-id> Preview deletion; use --confirm to tombstone
53
57
  restore <session-id> Restore a reversible catalog tombstone
54
58
  purge <session-id> Preview terminal AIWG-copy purge
55
59
  audit --workspace <id> Read content-free mutation events
56
60
  analytics <view> --workspace <id> Summary, tool-calls, escalations, or HITL
57
61
  forensics <view> --workspace <id> Authorized timeline, indicators, or evidence
58
- doctor Check catalog availability and integrity
62
+ doctor Check catalog integrity and configured provider scope
59
63
 
60
64
  Options:
61
65
  --json Emit the versioned JSON contract
62
66
  --dry-run Preview a mutation without changing state
67
+ --all-workspaces Reindex every workspace in the selected catalog (reindex only)
63
68
  --db <path> Select a catalog; relative paths resolve from cwd
64
69
  --workspace <path-or-id> Select the workspace within that catalog for reads
65
70
  (default: current project, matching cwd, or sole catalog workspace)
66
71
  Catalog defaults to <project-or-cwd>/.aiwg/sessions/catalog.sqlite
67
72
  Imports for other workspaces stay in the selected catalog;
68
73
  use --db to read that catalog from another directory
69
- --expected-providers <ids> Comma-separated authorized scope, independent of runtime inventory
74
+ --expected-providers <ids> Explicit scope for this invocation; refresh re-reads workspace config
75
+ --carry-forward <catalog> Rebuild from sources plus re-redacted stored history (import-discovered)
70
76
  --manifest <path> Override the discovery manifest path
71
77
  --provider-home <path> Override the provider home root (testing/portable homes)
72
- --codex-root <path> Explicitly authorize a shared Codex sessions/export root
78
+ --codex-root <path> Authorize a shared Codex sessions/export root (repeatable)
73
79
  --omp-root <path> Explicitly authorize an OMP profile sessions root
74
80
  --muse-root <path> Explicitly authorize a Muse Code sessions root
75
81
  --dsh-root <path> Explicitly authorize a DeepSeek Harness sessions root
@@ -78,6 +84,10 @@ Options:
78
84
  (default: grok on PATH, then $GROK_HOME/bin/grok)
79
85
  --confirm, --yes Confirm a persistent discovered batch import
80
86
  --lock-wait-ms <n> Maximum import-lease wait (default 5000)
87
+ --exclude <id-or-glob> Skip a session ID or absolute path glob (repeatable); adds to sessions.import.exclude and AIWG_SESSIONS_IMPORT_EXCLUDE
88
+ --max-record-bytes <n> Import record limit (default 67108864); overrides AIWG_SESSIONS_IMPORT_MAX_RECORD_BYTES and sessions.import.max_record_bytes
89
+ --max-source-bytes <n> Import source limit (default 1073741824, 1 GiB; 0 unlimited); overrides AIWG_SESSIONS_IMPORT_MAX_SOURCE_BYTES and sessions.import.max_source_bytes
90
+ Import limit precedence: CLI flag > env var > workspace config > default.
81
91
  --inactivity-threshold <duration> Historical inactivity threshold (default 24h)
82
92
  --min-coverage <0..1> Fail list/report commands below a coverage ratio
83
93
  --provider <id> Filter list or select an import adapter
@@ -110,9 +120,9 @@ Analytics / forensics:
110
120
  --group-by tool|session|provider, --limit <1..5000>
111
121
  --authorize-forensics Required for each authorized forensic invocation
112
122
  --markdown Render a sanitized forensic timeline table`;
113
- function printHelp(ctx, exitCode = EXIT.ok) {
123
+ async function printHelp(ctx, exitCode = EXIT.ok) {
114
124
  if (ctx.args.includes('--json'))
115
- emit(envelope('sessions.help', 'ok', { usage: HELP }, null));
125
+ await emit(envelope('sessions.help', 'ok', { usage: HELP }, null));
116
126
  else
117
127
  console.log(HELP);
118
128
  return { exitCode };
@@ -136,7 +146,7 @@ export const sessionsHandler = {
136
146
  const normalized = normalizeError(error);
137
147
  const output = envelope('sessions', 'error', null, normalized.error);
138
148
  if (json)
139
- emit(output);
149
+ await emit(output);
140
150
  else
141
151
  console.error(`${normalized.error.code}: ${normalized.error.message}`);
142
152
  return { exitCode: normalized.exitCode, message: normalized.error.message };
@@ -148,7 +158,7 @@ export const sessionsHandler = {
148
158
  try {
149
159
  const result = await executeCommand(ctx, parsed);
150
160
  if (json)
151
- emit(result.envelope);
161
+ await emit(result.envelope);
152
162
  else
153
163
  printHuman(result.envelope);
154
164
  return { exitCode: result.exitCode };
@@ -157,7 +167,7 @@ export const sessionsHandler = {
157
167
  const normalized = normalizeError(error);
158
168
  const output = envelope(`sessions.${parsed.command}`, 'error', null, normalized.error);
159
169
  if (json)
160
- emit(output);
170
+ await emit(output);
161
171
  else
162
172
  console.error(`${normalized.error.code}: ${normalized.error.message}`);
163
173
  return { exitCode: normalized.exitCode, message: normalized.error.message };
@@ -590,11 +600,22 @@ async function executeCommand(ctx, args) {
590
600
  return ok(command, { sourceId, redactedLocator });
591
601
  }
592
602
  case 'reindex': {
593
- const { workspaceId } = authorizationContext(ctx, args, command);
603
+ const allWorkspaces = args.flags.has('--all-workspaces');
604
+ if (allWorkspaces && args.values.has('--workspace')) {
605
+ throw new CliError('INVALID_ARGUMENT', '--all-workspaces cannot be combined with --workspace', EXIT.usage);
606
+ }
607
+ const workspaceId = allWorkspaces
608
+ ? undefined
609
+ : authorizationContext(ctx, args, command).workspaceId;
610
+ const data = {
611
+ operation: 'reindex',
612
+ allWorkspaces,
613
+ workspaceIds: allWorkspaces ? repository.listWorkspaceIds() : [workspaceId],
614
+ };
594
615
  if (isDryRun(ctx, args))
595
- return preview(command, { operation: 'reindex' });
616
+ return preview(command, data);
596
617
  repository.reindex(workspaceId);
597
- return ok(command, { operation: 'reindex' });
618
+ return ok(command, data);
598
619
  }
599
620
  case 'audit': {
600
621
  const { workspaceId } = readAuthorizationContext(ctx, args, command, repository);
@@ -706,12 +727,22 @@ async function executeCommand(ctx, args) {
706
727
  providerLogsModified: false,
707
728
  });
708
729
  }
709
- case 'doctor':
730
+ case 'doctor': {
731
+ const workspacePath = resolve(ctx.cwd, args.values.get('--workspace') ?? ctx.cwd);
732
+ const workspace = existsSync(workspacePath) ? realpathSync(workspacePath) : workspacePath;
733
+ const config = await readAiwgConfig(workspace);
734
+ const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(workspace));
735
+ const preflight = existsSync(manifestPath)
736
+ ? (await readDiscoveryManifest(manifestPath)).preflight
737
+ : repository.getCoverage(workspace).preflight;
738
+ const warnings = providerScopeWarnings(config?.providers ?? [], preflight?.expectedProviders ?? [], preflight?.scopeWarnings);
710
739
  return ok(command, {
711
740
  database: redactSourceLocator(databasePath(ctx, args)),
712
741
  health: repository.doctor(),
742
+ warnings,
713
743
  jsonContractVersion: JSON_CONTRACT_VERSION,
714
744
  });
745
+ }
715
746
  default:
716
747
  throw new CliError('UNKNOWN_COMMAND', `unknown sessions command: ${command}`, EXIT.usage);
717
748
  }
@@ -721,6 +752,7 @@ async function executeCommand(ctx, args) {
721
752
  }
722
753
  }
723
754
  async function importSource(ctx, args) {
755
+ const limits = await configuredImportLimits(ctx, args);
724
756
  const input = resolve(ctx.cwd, requiredPositional(args, 0, 'file'));
725
757
  const providerInput = args.values.get('--provider') ?? 'generic';
726
758
  const provider = assertSessionProviderId(providerInput);
@@ -753,37 +785,37 @@ async function importSource(ctx, args) {
753
785
  const isDsh = provider === 'deepseek-harness';
754
786
  const isWarp = provider === 'warp';
755
787
  const isDevinDesktop = provider === 'devin-desktop';
756
- const adapter = isDsh ? new DeepSeekHarnessSessionAdapter() : isOmp ? new OmpSessionAdapter() : isClaude
757
- ? new ClaudeSessionAdapter()
788
+ const adapter = isDsh ? new DeepSeekHarnessSessionAdapter(limits) : isOmp ? new OmpSessionAdapter(limits) : isClaude
789
+ ? new ClaudeSessionAdapter(limits)
758
790
  : isCodex
759
- ? new CodexSessionAdapter()
791
+ ? new CodexSessionAdapter(limits)
760
792
  : isCopilot
761
- ? new CopilotSessionAdapter()
793
+ ? new CopilotSessionAdapter(limits)
762
794
  : isCursor
763
- ? new CursorSessionAdapter()
795
+ ? new CursorSessionAdapter(limits)
764
796
  : isFactory
765
- ? new FactorySessionAdapter()
797
+ ? new FactorySessionAdapter(limits)
766
798
  : isHermes
767
- ? new HermesSessionAdapter()
799
+ ? new HermesSessionAdapter(limits)
768
800
  : isOpenCode
769
- ? new OpenCodeSessionAdapter()
801
+ ? new OpenCodeSessionAdapter(limits)
770
802
  : isOpenClaw
771
- ? new OpenClawSessionAdapter()
803
+ ? new OpenClawSessionAdapter(limits)
772
804
  : isOpenHuman
773
- ? new OpenHumanSessionAdapter()
805
+ ? new OpenHumanSessionAdapter(limits)
774
806
  : isGrokbot
775
- ? new GrokbotSessionAdapter()
807
+ ? new GrokbotSessionAdapter(limits)
776
808
  : isGrokBuild
777
- ? new GrokBuildSessionAdapter()
809
+ ? new GrokBuildSessionAdapter(limits)
778
810
  : isMuse
779
- ? new MuseSessionAdapter()
811
+ ? new MuseSessionAdapter(limits)
780
812
  : isPi
781
- ? new PiSessionAdapter()
813
+ ? new PiSessionAdapter(limits)
782
814
  : isWarp
783
- ? new WarpSessionAdapter()
815
+ ? new WarpSessionAdapter(limits)
784
816
  : isDevinDesktop
785
- ? new DevinDesktopSessionAdapter()
786
- : new GenericSessionInterchangeAdapter();
817
+ ? new DevinDesktopSessionAdapter(limits)
818
+ : new GenericSessionInterchangeAdapter(limits);
787
819
  const locatorClass = isDsh ? 'deepseek-harness-session-v2-jsonl' : isOmp ? 'omp-session-v3-jsonl' : isClaude
788
820
  ? claudeLocatorClass(input)
789
821
  : isCodex
@@ -932,8 +964,9 @@ async function importSource(ctx, args) {
932
964
  try {
933
965
  const repository = openRepository(ctx, args);
934
966
  try {
967
+ const interruptedRuns = repository.interruptRunningBatchImports();
935
968
  const receipts = await new IncrementalSessionImporter(repository).import({
936
- source, selectedSource, adapter, workspaceId, policyVersion: '1.0.0',
969
+ source, selectedSource, adapter, workspaceId, policyVersion: '1.0.0', limits,
937
970
  inactivityThresholdMs: inactivityThreshold(args.values.get('--inactivity-threshold')),
938
971
  });
939
972
  for (const warning of workspaceReport.warnings)
@@ -942,6 +975,7 @@ async function importSource(ctx, args) {
942
975
  sourceId,
943
976
  ...workspaceReport,
944
977
  receipts,
978
+ interruptedRuns,
945
979
  totals: {
946
980
  sessionsInserted: receipts.reduce((sum, item) => sum + item.sessionsInserted, 0),
947
981
  eventsInserted: receipts.reduce((sum, item) => sum + item.eventsInserted, 0),
@@ -1234,8 +1268,15 @@ async function exportUnpack(ctx, args, command) {
1234
1268
  }
1235
1269
  async function discoverWorkspace(ctx, args) {
1236
1270
  const workspace = resolve(ctx.cwd, requiredValue(args, '--workspace'));
1271
+ const config = await readAiwgConfig(workspace);
1272
+ const canonicalWorkspace = realpathSync(workspace);
1273
+ const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(canonicalWorkspace));
1274
+ const exclude = resolveImportExclusions(config?.sessions?.import, args.exclude);
1275
+ const previous = exclude.length > 0 && existsSync(manifestPath) ? await readDiscoveryManifest(manifestPath) : undefined;
1237
1276
  const manifest = await discoverWorkspaceHistories({
1238
1277
  workspace,
1278
+ knownSources: previous?.workspaceId === canonicalWorkspace ? previous.sources : undefined,
1279
+ exclude,
1239
1280
  expectedProviders: requestedProviders(args),
1240
1281
  providerHome: args.values.has('--provider-home')
1241
1282
  ? resolve(ctx.cwd, args.values.get('--provider-home'))
@@ -1248,12 +1289,13 @@ async function discoverWorkspace(ctx, args) {
1248
1289
  ? resolve(ctx.cwd, args.values.get('--dsh-root')) : undefined,
1249
1290
  grokbotRoot: args.values.has('--grokbot-root')
1250
1291
  ? resolve(ctx.cwd, args.values.get('--grokbot-root')) : undefined,
1251
- codexRoot: args.values.has('--codex-root')
1252
- ? resolve(ctx.cwd, args.values.get('--codex-root'))
1253
- : undefined,
1292
+ codexRoots: [
1293
+ ...(config?.sessions?.codex_roots ?? []).map((root) => resolve(workspace, root)),
1294
+ ...args.codexRoots.map((root) => resolve(ctx.cwd, root)),
1295
+ ],
1254
1296
  });
1255
- const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(manifest.workspacePath));
1256
1297
  const data = {
1298
+ warnings: manifest.preflight?.scopeWarnings ?? [],
1257
1299
  manifest: publicDiscoveryManifest(manifest),
1258
1300
  manifestPath: redactSourceLocator(manifestPath),
1259
1301
  exactManifestRequiredForImport: true,
@@ -1267,12 +1309,28 @@ async function discoverWorkspace(ctx, args) {
1267
1309
  async function importDiscovered(ctx, args) {
1268
1310
  const workspace = resolve(ctx.cwd, requiredValue(args, '--workspace'));
1269
1311
  const canonicalWorkspace = realpathSync(workspace);
1312
+ const limits = await configuredImportLimits(ctx, args, canonicalWorkspace);
1313
+ const config = await readAiwgConfig(canonicalWorkspace);
1314
+ const exclude = resolveImportExclusions(config?.sessions?.import, args.exclude);
1270
1315
  const manifestPath = resolve(ctx.cwd, args.values.get('--manifest') ?? defaultDiscoveryManifestPath(canonicalWorkspace));
1271
1316
  const manifest = await readDiscoveryManifest(manifestPath);
1272
1317
  if (manifest.workspaceId !== canonicalWorkspace) {
1273
1318
  throw new CliError('MANIFEST_WORKSPACE_MISMATCH', 'discovery manifest does not belong to the explicitly authorized workspace', EXIT.contract);
1274
1319
  }
1320
+ const carryPath = args.values.has('--carry-forward')
1321
+ ? resolve(ctx.cwd, args.values.get('--carry-forward')) : undefined;
1322
+ if (carryPath) {
1323
+ const sourceStat = statSync(carryPath);
1324
+ const target = databasePath(ctx, args);
1325
+ if (existsSync(target)) {
1326
+ const targetStat = statSync(target);
1327
+ if (sourceStat.dev === targetStat.dev && sourceStat.ino === targetStat.ino) {
1328
+ throw new CliError('INVALID_ARGUMENT', 'carry-forward requires a separate destination catalog', EXIT.usage);
1329
+ }
1330
+ }
1331
+ }
1275
1332
  const expected = requestedProviders(args);
1333
+ const warnings = providerScopeWarnings(config?.providers ?? [], manifest.preflight?.expectedProviders ?? [], manifest.preflight?.scopeWarnings);
1276
1334
  if (expected && JSON.stringify(expected) !== JSON.stringify(manifest.preflight?.expectedProviders)) {
1277
1335
  throw new SessionContractError('INVALID_ARGUMENT', 'expected provider scope differs from the saved manifest; rediscover with --expected-providers');
1278
1336
  }
@@ -1288,25 +1346,35 @@ async function importDiscovered(ctx, args) {
1288
1346
  // A preview can still describe the exact manifest before a catalog exists.
1289
1347
  }
1290
1348
  return preview('import-discovered', {
1349
+ warnings,
1291
1350
  manifest: publicDiscoveryManifest(manifest),
1292
- receipt: previewDiscoveryImport(manifest, existing),
1351
+ receipt: previewDiscoveryImport(manifest, existing, new Date(), exclude),
1293
1352
  confirmationRequired: !isDryRun(ctx, args),
1294
1353
  wouldPersist: false,
1354
+ ...(carryPath ? { carryForward: redactSourceLocator(carryPath) } : {}),
1295
1355
  });
1296
1356
  }
1297
1357
  const lease = await acquireImportLease(databasePath(ctx, args), sha256(['discovered-import', manifest.manifestId, manifest.workspaceId].join('\0')), {
1298
1358
  waitMs: boundedInteger(args.values.get('--lock-wait-ms'), 5_000, 0, 300_000, '--lock-wait-ms'),
1299
1359
  });
1300
1360
  try {
1301
- const repository = openRepository(ctx, args);
1361
+ const previous = carryPath ? new SessionRepository(carryPath, { readonly: true }) : undefined;
1362
+ let repository;
1302
1363
  try {
1364
+ repository = openRepository(ctx, args);
1365
+ const interruptedRuns = repository.interruptRunningBatchImports();
1303
1366
  const receipt = await importDiscoveryManifest({
1304
1367
  manifest,
1305
1368
  repository,
1369
+ carryForward: previous,
1370
+ limits,
1371
+ exclude,
1306
1372
  signal: ctx.signal,
1307
1373
  inactivityThresholdMs: inactivityThreshold(args.values.get('--inactivity-threshold')),
1308
1374
  });
1375
+ receipt.interruptedRuns = interruptedRuns;
1309
1376
  return ok('import-discovered', {
1377
+ warnings,
1310
1378
  manifestId: manifest.manifestId,
1311
1379
  receipt,
1312
1380
  resumable: receipt.run.status !== 'complete',
@@ -1318,7 +1386,8 @@ async function importDiscovered(ctx, args) {
1318
1386
  });
1319
1387
  }
1320
1388
  finally {
1321
- repository.close();
1389
+ repository?.close();
1390
+ previous?.close();
1322
1391
  }
1323
1392
  }
1324
1393
  finally {
@@ -1648,9 +1717,25 @@ function catalogScope(ctx, args, repository, workspaceId) {
1648
1717
  : null,
1649
1718
  };
1650
1719
  }
1720
+ async function configuredImportLimits(ctx, args, workspace) {
1721
+ const selectedWorkspace = workspace ?? (args.values.has('--workspace')
1722
+ ? resolve(ctx.cwd, args.values.get('--workspace')) : ctx.cwd);
1723
+ try {
1724
+ const config = await readAiwgConfig(selectedWorkspace);
1725
+ return resolveImportLimits(config?.sessions?.import, {
1726
+ maxRecordBytes: args.values.get('--max-record-bytes'),
1727
+ maxSourceBytes: args.values.get('--max-source-bytes'),
1728
+ });
1729
+ }
1730
+ catch (error) {
1731
+ throw new CliError('INVALID_ARGUMENT', error instanceof Error ? error.message : String(error), EXIT.usage);
1732
+ }
1733
+ }
1651
1734
  function parseArgs(argv) {
1652
1735
  const flags = new Set();
1653
1736
  const values = new Map();
1737
+ const codexRoots = [];
1738
+ const exclude = [];
1654
1739
  const positionals = [];
1655
1740
  const valueFlags = new Set([
1656
1741
  '--db', '--provider', '--workspace', '--tag', '--limit', '--cursor', '--source-id',
@@ -1658,8 +1743,8 @@ function parseArgs(argv) {
1658
1743
  '--entity', '--sensitivity', '--extraction-state', '--page-size', '--max-documents',
1659
1744
  '--state', '--reviewer', '--reason', '--policy-version', '--min-confidence',
1660
1745
  '--consumer', '--actor-class', '--reason-code', '--dependent-action', '--basis',
1661
- '--manifest', '--provider-home', '--codex-root', '--omp-root', '--muse-root', '--dsh-root', '--grokbot-root', '--lock-wait-ms', '--min-coverage', '--gap',
1662
- '--inactivity-threshold', '--expected-providers',
1746
+ '--carry-forward', '--manifest', '--provider-home', '--codex-root', '--omp-root', '--muse-root', '--dsh-root', '--grokbot-root', '--lock-wait-ms', '--min-coverage', '--gap',
1747
+ '--inactivity-threshold', '--expected-providers', '--exclude', '--max-record-bytes', '--max-source-bytes',
1663
1748
  '--control-events',
1664
1749
  '--session', '--status', '--actor', '--group-by',
1665
1750
  '--out', '--plan', '--input', '--shard-name', '--max-attachment-bytes', '--format', '--grok-bin',
@@ -1673,6 +1758,10 @@ function parseArgs(argv) {
1673
1758
  throw new CliError('MISSING_OPTION_VALUE', `missing value for ${arg}`, EXIT.usage);
1674
1759
  }
1675
1760
  values.set(arg, value);
1761
+ if (arg === '--codex-root')
1762
+ codexRoots.push(value);
1763
+ if (arg === '--exclude')
1764
+ exclude.push(value);
1676
1765
  index += 1;
1677
1766
  }
1678
1767
  else if (arg.startsWith('-')) {
@@ -1685,7 +1774,7 @@ function parseArgs(argv) {
1685
1774
  positionals.push(arg);
1686
1775
  }
1687
1776
  }
1688
- return { command, positionals, flags, values };
1777
+ return { command, positionals, flags, values, codexRoots, exclude };
1689
1778
  }
1690
1779
  function requestedProviders(args) {
1691
1780
  const value = args.values.get('--expected-providers');
@@ -1961,8 +2050,13 @@ function preview(command, data) {
1961
2050
  function envelope(command, status, data, error) {
1962
2051
  return { contractVersion: JSON_CONTRACT_VERSION, command, status, data, error };
1963
2052
  }
1964
- function emit(value) {
2053
+ async function emit(value) {
1965
2054
  console.log(JSON.stringify(value, null, 2));
2055
+ // The launcher and router may force exit after the handler returns. A write
2056
+ // callback waits for the JSON already queued on stdout, including on pipes.
2057
+ await new Promise((resolve, reject) => {
2058
+ process.stdout.write('', (error) => error ? reject(error) : resolve());
2059
+ });
1966
2060
  }
1967
2061
  function printHuman(value) {
1968
2062
  if (value.status === 'preview')
@@ -2015,7 +2109,7 @@ function normalizeError(error) {
2015
2109
  }
2016
2110
  if (error instanceof SessionContractError) {
2017
2111
  const exitCode = error.code === 'UNSUPPORTED_OPERATION' ? EXIT.unsupported : EXIT.contract;
2018
- return { error: { code: error.code, message: error.message }, exitCode };
2112
+ return { error: { code: error.code, message: error.message, ...(error.detail ? { details: error.detail } : {}) }, exitCode };
2019
2113
  }
2020
2114
  if (error instanceof ImportLeaseContentionError) {
2021
2115
  return {
@@ -8,7 +8,7 @@ import { isInteractive } from '../env.js';
8
8
  import { boundedSpawnSync } from '../../process/bounded-child-process.mjs';
9
9
  import { sha256 } from '../../security/artifact-trust.js';
10
10
  import { artifactVerifyHandler } from './artifact-verify.js';
11
- import { requireFlagValue, MissingFlagValueError } from '../flag-parsing.js';
11
+ import { requireFlagValue, MissingFlagValueError, collectFlagValues, hasPositionalOrSwitch, argsTerminatorIndex, valueTokenIndexes } from '../flag-parsing.js';
12
12
  import { exitCodeForSpawnResult } from '../child-process-result.js';
13
13
  import { askString, createPromptInterface } from '../prompt-utils.js';
14
14
  const SETUP_SCHEMA_REL = 'agentic/code/addons/agentic-installer/schemas/v1/setup-manifest.schema.json';
@@ -76,9 +76,8 @@ Safety:
76
76
  Headless input steps (#3062):
77
77
  A 'type: ask' step resolves its declared 'params' the same way a 'script'
78
78
  step's env vars do (--param/--params-file/env/default); if every declared
79
- param is already resolved it runs headless, otherwise it is refused,
80
- naming the missing param names — this CLI has no interactive-capture
81
- mechanism for 'ask' steps, with or without a terminal. A step marked
79
+ param is already resolved it runs headless; otherwise a terminal prompts
80
+ for missing values. Without a terminal it refuses and names the missing params. A step marked
82
81
  'interactive: true' runs an arbitrary script whose questions cannot be
83
82
  enumerated; without a terminal it is refused outright. Every selected
84
83
  step (including nested 'platform-route' steps) is preflighted for both
@@ -96,40 +95,17 @@ Headless input steps (#3062):
96
95
  function flagValue(args, name) {
97
96
  return requireFlagValue(args, {}, name);
98
97
  }
98
+ const VALUE_FLAGS = [
99
+ '--manifest', '--schema', '--platform', '--distro', '--params-file', '--param', '--set',
100
+ '--step', '--skip', '--type', '--output', '--name', '--attestation', '--policy', '--state',
101
+ '--asset-type', '--namespace', '--channel',
102
+ ];
99
103
  function hasFlag(args, ...names) {
100
- return args.some((arg) => names.includes(arg));
104
+ return names.some(name => hasPositionalOrSwitch(args, name, VALUE_FLAGS));
101
105
  }
102
106
  function positionalManifest(args) {
103
- const valueFlags = new Set([
104
- '--manifest',
105
- '--schema',
106
- '--platform',
107
- '--distro',
108
- '--params-file',
109
- '--param',
110
- '--set',
111
- '--step',
112
- '--skip',
113
- '--type',
114
- '--output',
115
- '--name',
116
- '--attestation',
117
- '--policy',
118
- '--state',
119
- '--asset-type',
120
- '--namespace',
121
- '--channel',
122
- ]);
123
- for (let i = 0; i < args.length; i += 1) {
124
- const arg = args[i];
125
- if (valueFlags.has(arg)) {
126
- i += 1;
127
- continue;
128
- }
129
- if (!arg.startsWith('-'))
130
- return arg;
131
- }
132
- return undefined;
107
+ const consumed = valueTokenIndexes(args, VALUE_FLAGS);
108
+ return args.slice(0, argsTerminatorIndex(args)).find((arg, index) => !consumed.has(index) && !arg.startsWith('-'));
133
109
  }
134
110
  function resolveSchemaPath(frameworkRoot, cwd, override) {
135
111
  if (override)
@@ -472,15 +448,12 @@ function renderValidationText(result) {
472
448
  }
473
449
  function parseParamValues(args) {
474
450
  const values = {};
475
- for (let i = 0; i < args.length; i += 1) {
476
- if (args[i] !== '--param' && args[i] !== '--set')
477
- continue;
478
- const raw = args[i + 1] ?? '';
451
+ for (const raw of collectFlagValues(args, {}, '--param', '--set')) {
479
452
  const eq = raw.indexOf('=');
480
- if (eq <= 0)
481
- throw new Error(`${args[i]} requires KEY=VALUE`);
453
+ if (eq <= 0 || !raw.slice(0, eq).trim())
454
+ throw new MissingFlagValueError('--param KEY=VALUE');
455
+ // An empty parameter value is supported (e.g. explicitly clearing a default).
482
456
  values[raw.slice(0, eq)] = raw.slice(eq + 1);
483
- i += 1;
484
457
  }
485
458
  return values;
486
459
  }
@@ -1068,10 +1041,21 @@ export async function runSetupManifest(options) {
1068
1041
  return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };
1069
1042
  }
1070
1043
  const params = resolveParams(manifest, options);
1071
- if (params.missing.length) {
1044
+ const steps = selectedSteps(manifest, options);
1045
+ const askSteps = [];
1046
+ const collectAsks = (selected) => {
1047
+ for (const step of selected) {
1048
+ if (step.type === 'ask')
1049
+ askSteps.push(step);
1050
+ if (step.type === 'platform-route')
1051
+ collectAsks(step.routes?.find(route => route.platform === target.os)?.steps ?? []);
1052
+ }
1053
+ };
1054
+ collectAsks(steps);
1055
+ const promptable = new Set(isInteractive() ? askSteps.flatMap(askStepParams) : []);
1056
+ if (params.missing.some(name => !promptable.has(name))) {
1072
1057
  return { exitCode: 1, message: `setup-run: required params missing before execution: ${params.missing.join(', ')}` };
1073
1058
  }
1074
- const steps = selectedSteps(manifest, options);
1075
1059
  if (options.step && steps.length === 0)
1076
1060
  return { exitCode: 1, message: `setup-run: step '${options.step}' not found` };
1077
1061
  if (options.dryRun) {
@@ -1095,6 +1079,17 @@ export async function runSetupManifest(options) {
1095
1079
  message: `setup-run: refusing before executing any step:\n${blockers.map((b) => ` - ${b}`).join('\n')}`,
1096
1080
  };
1097
1081
  }
1082
+ // Resolve required ask inputs before any scripts can consume them.
1083
+ for (const step of askSteps) {
1084
+ const missing = missingAskParams(step, params.values).filter(name => params.missing.includes(name));
1085
+ if (!missing.length)
1086
+ continue;
1087
+ Object.assign(params.values, await promptForAskStep(step, missing));
1088
+ }
1089
+ const checked = resolveParams(manifest, { ...options, paramValues: { ...options.paramValues, ...params.values } });
1090
+ if (checked.missing.length)
1091
+ return { exitCode: 1, message: `setup-run: required params missing before execution: ${checked.missing.join(', ')}` };
1092
+ Object.assign(params.values, checked.values);
1098
1093
  for (const step of steps) {
1099
1094
  const result = await runStep(step, manifest, validation.manifestDir, params.values, target.os, options.yes);
1100
1095
  if (!result.ok) {