@provos/ironcurtain 0.11.0 → 0.13.0

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 (472) hide show
  1. package/README.md +23 -19
  2. package/dist/cli-help.d.ts +12 -0
  3. package/dist/cli-help.js +21 -0
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.js +14 -6
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/agent-model-guard.d.ts +14 -0
  8. package/dist/config/agent-model-guard.js +24 -0
  9. package/dist/config/agent-model-guard.js.map +1 -0
  10. package/dist/config/config-command.d.ts +33 -1
  11. package/dist/config/config-command.js +723 -11
  12. package/dist/config/config-command.js.map +1 -1
  13. package/dist/config/first-start.js +2 -8
  14. package/dist/config/first-start.js.map +1 -1
  15. package/dist/config/model-provider.d.ts +9 -0
  16. package/dist/config/model-provider.js +13 -0
  17. package/dist/config/model-provider.js.map +1 -1
  18. package/dist/config/openrouter-catalog.d.ts +46 -0
  19. package/dist/config/openrouter-catalog.js +111 -0
  20. package/dist/config/openrouter-catalog.js.map +1 -0
  21. package/dist/config/openrouter-models-fallback.d.ts +15 -0
  22. package/dist/config/openrouter-models-fallback.js +49 -0
  23. package/dist/config/openrouter-models-fallback.js.map +1 -0
  24. package/dist/config/paths.d.ts +48 -6
  25. package/dist/config/paths.js +63 -8
  26. package/dist/config/paths.js.map +1 -1
  27. package/dist/config/types.d.ts +8 -0
  28. package/dist/config/user-config.d.ts +179 -6
  29. package/dist/config/user-config.js +282 -5
  30. package/dist/config/user-config.js.map +1 -1
  31. package/dist/daemon/daemon-command.js +14 -5
  32. package/dist/daemon/daemon-command.js.map +1 -1
  33. package/dist/daemon/ironcurtain-daemon.d.ts +18 -0
  34. package/dist/daemon/ironcurtain-daemon.js +28 -1
  35. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  36. package/dist/daemon-client/daemon-client.d.ts +123 -0
  37. package/dist/daemon-client/daemon-client.js +324 -0
  38. package/dist/daemon-client/daemon-client.js.map +1 -0
  39. package/dist/docker/adapters/claude-code.js +109 -1
  40. package/dist/docker/adapters/claude-code.js.map +1 -1
  41. package/dist/docker/adapters/codex.d.ts +18 -0
  42. package/dist/docker/adapters/codex.js +376 -0
  43. package/dist/docker/adapters/codex.js.map +1 -0
  44. package/dist/docker/adapters/goose.js +38 -2
  45. package/dist/docker/adapters/goose.js.map +1 -1
  46. package/dist/docker/agent-adapter.d.ts +34 -6
  47. package/dist/docker/agent-adapter.js +2 -0
  48. package/dist/docker/agent-adapter.js.map +1 -1
  49. package/dist/docker/agent-registry.js +5 -0
  50. package/dist/docker/agent-registry.js.map +1 -1
  51. package/dist/docker/apple-container-manager.d.ts +49 -0
  52. package/dist/docker/apple-container-manager.js +486 -0
  53. package/dist/docker/apple-container-manager.js.map +1 -0
  54. package/dist/docker/ca.js +9 -2
  55. package/dist/docker/ca.js.map +1 -1
  56. package/dist/docker/code-mode-proxy.d.ts +11 -0
  57. package/dist/docker/code-mode-proxy.js +3 -1
  58. package/dist/docker/code-mode-proxy.js.map +1 -1
  59. package/dist/docker/container-lifecycle.d.ts +2 -2
  60. package/dist/docker/container-lifecycle.js +9 -0
  61. package/dist/docker/container-lifecycle.js.map +1 -1
  62. package/dist/docker/container-runtime.d.ts +29 -0
  63. package/dist/docker/container-runtime.js +76 -0
  64. package/dist/docker/container-runtime.js.map +1 -0
  65. package/dist/docker/docker-agent-session.d.ts +31 -0
  66. package/dist/docker/docker-agent-session.js +97 -7
  67. package/dist/docker/docker-agent-session.js.map +1 -1
  68. package/dist/docker/docker-infrastructure.d.ts +232 -15
  69. package/dist/docker/docker-infrastructure.js +762 -118
  70. package/dist/docker/docker-infrastructure.js.map +1 -1
  71. package/dist/docker/docker-manager.d.ts +25 -5
  72. package/dist/docker/docker-manager.js +388 -21
  73. package/dist/docker/docker-manager.js.map +1 -1
  74. package/dist/docker/docker-probe.d.ts +45 -0
  75. package/dist/docker/docker-probe.js +86 -0
  76. package/dist/docker/docker-probe.js.map +1 -0
  77. package/dist/docker/docker-progress-sink.d.ts +1 -1
  78. package/dist/docker/docker-resource-lifecycle.d.ts +71 -0
  79. package/dist/docker/docker-resource-lifecycle.js +454 -0
  80. package/dist/docker/docker-resource-lifecycle.js.map +1 -0
  81. package/dist/docker/gc-command.d.ts +2 -0
  82. package/dist/docker/gc-command.js +37 -0
  83. package/dist/docker/gc-command.js.map +1 -0
  84. package/dist/docker/mitm-proxy.d.ts +58 -5
  85. package/dist/docker/mitm-proxy.js +338 -26
  86. package/dist/docker/mitm-proxy.js.map +1 -1
  87. package/dist/docker/network-topology.d.ts +88 -0
  88. package/dist/docker/network-topology.js +167 -0
  89. package/dist/docker/network-topology.js.map +1 -0
  90. package/dist/docker/oauth-credentials.d.ts +13 -0
  91. package/dist/docker/oauth-credentials.js +125 -0
  92. package/dist/docker/oauth-credentials.js.map +1 -1
  93. package/dist/docker/openrouter.d.ts +107 -0
  94. package/dist/docker/openrouter.js +236 -0
  95. package/dist/docker/openrouter.js.map +1 -0
  96. package/dist/docker/orientation.js +1 -1
  97. package/dist/docker/orientation.js.map +1 -1
  98. package/dist/docker/package-types.d.ts +1 -1
  99. package/dist/docker/platform.d.ts +3 -1
  100. package/dist/docker/platform.js +3 -1
  101. package/dist/docker/platform.js.map +1 -1
  102. package/dist/docker/provider-config.d.ts +35 -1
  103. package/dist/docker/provider-config.js +93 -3
  104. package/dist/docker/provider-config.js.map +1 -1
  105. package/dist/docker/provision-lock.d.ts +46 -0
  106. package/dist/docker/provision-lock.js +152 -0
  107. package/dist/docker/provision-lock.js.map +1 -0
  108. package/dist/docker/pty-session.d.ts +22 -0
  109. package/dist/docker/pty-session.js +238 -84
  110. package/dist/docker/pty-session.js.map +1 -1
  111. package/dist/docker/pty-types.d.ts +15 -0
  112. package/dist/docker/pty-types.js +8 -0
  113. package/dist/docker/pty-types.js.map +1 -1
  114. package/dist/docker/registry-proxy.d.ts +46 -0
  115. package/dist/docker/registry-proxy.js +255 -1
  116. package/dist/docker/registry-proxy.js.map +1 -1
  117. package/dist/docker/resource-limits.d.ts +14 -5
  118. package/dist/docker/resource-limits.js +11 -8
  119. package/dist/docker/resource-limits.js.map +1 -1
  120. package/dist/docker/sse-extractor.js +55 -5
  121. package/dist/docker/sse-extractor.js.map +1 -1
  122. package/dist/docker/stream-delay.d.ts +86 -0
  123. package/dist/docker/stream-delay.js +218 -0
  124. package/dist/docker/stream-delay.js.map +1 -0
  125. package/dist/docker/token-stream-types.d.ts +15 -0
  126. package/dist/docker/trajectory-capture.d.ts +79 -0
  127. package/dist/docker/trajectory-capture.js +521 -0
  128. package/dist/docker/trajectory-capture.js.map +1 -0
  129. package/dist/docker/trajectory-reassembler.d.ts +286 -0
  130. package/dist/docker/trajectory-reassembler.js +1010 -0
  131. package/dist/docker/trajectory-reassembler.js.map +1 -0
  132. package/dist/docker/trajectory-tap.d.ts +100 -0
  133. package/dist/docker/trajectory-tap.js +410 -0
  134. package/dist/docker/trajectory-tap.js.map +1 -0
  135. package/dist/docker/trajectory-types.d.ts +187 -0
  136. package/dist/docker/trajectory-types.js +43 -0
  137. package/dist/docker/trajectory-types.js.map +1 -0
  138. package/dist/docker/types.d.ts +116 -12
  139. package/dist/doctor/checks.d.ts +39 -6
  140. package/dist/doctor/checks.js +129 -25
  141. package/dist/doctor/checks.js.map +1 -1
  142. package/dist/doctor/doctor-command.js +39 -9
  143. package/dist/doctor/doctor-command.js.map +1 -1
  144. package/dist/doctor/mcp-liveness.d.ts +1 -1
  145. package/dist/doctor/mcp-liveness.js +12 -4
  146. package/dist/doctor/mcp-liveness.js.map +1 -1
  147. package/dist/escalation/escalation-watcher.js +6 -0
  148. package/dist/escalation/escalation-watcher.js.map +1 -1
  149. package/dist/{mux → escalation}/trusted-input.d.ts +7 -4
  150. package/dist/{mux → escalation}/trusted-input.js +8 -5
  151. package/dist/escalation/trusted-input.js.map +1 -0
  152. package/dist/hash.js.map +1 -1
  153. package/dist/index.js +65 -16
  154. package/dist/index.js.map +1 -1
  155. package/dist/mux/mux-app.d.ts +9 -0
  156. package/dist/mux/mux-app.js +18 -16
  157. package/dist/mux/mux-app.js.map +1 -1
  158. package/dist/mux/mux-command.js +26 -8
  159. package/dist/mux/mux-command.js.map +1 -1
  160. package/dist/mux/mux-input-handler.d.ts +26 -2
  161. package/dist/mux/mux-input-handler.js +93 -11
  162. package/dist/mux/mux-input-handler.js.map +1 -1
  163. package/dist/mux/mux-renderer.d.ts +2 -1
  164. package/dist/mux/mux-renderer.js +67 -0
  165. package/dist/mux/mux-renderer.js.map +1 -1
  166. package/dist/mux/provider-profile-snapshot.d.ts +44 -0
  167. package/dist/mux/provider-profile-snapshot.js +56 -0
  168. package/dist/mux/provider-profile-snapshot.js.map +1 -0
  169. package/dist/mux/types.d.ts +3 -2
  170. package/dist/mux/types.js +6 -2
  171. package/dist/mux/types.js.map +1 -1
  172. package/dist/observe/observe-command.js +112 -125
  173. package/dist/observe/observe-command.js.map +1 -1
  174. package/dist/persona/broad-policy-validator.d.ts +95 -0
  175. package/dist/persona/broad-policy-validator.js +171 -0
  176. package/dist/persona/broad-policy-validator.js.map +1 -0
  177. package/dist/persona/compile-persona-policy.d.ts +54 -2
  178. package/dist/persona/compile-persona-policy.js +29 -4
  179. package/dist/persona/compile-persona-policy.js.map +1 -1
  180. package/dist/persona/event-bus-progress-reporter.d.ts +57 -0
  181. package/dist/persona/event-bus-progress-reporter.js +66 -0
  182. package/dist/persona/event-bus-progress-reporter.js.map +1 -0
  183. package/dist/persona/persona-command.js +46 -76
  184. package/dist/persona/persona-command.js.map +1 -1
  185. package/dist/persona/persona-compile-orchestrator.d.ts +101 -0
  186. package/dist/persona/persona-compile-orchestrator.js +504 -0
  187. package/dist/persona/persona-compile-orchestrator.js.map +1 -0
  188. package/dist/persona/persona-service.d.ts +137 -0
  189. package/dist/persona/persona-service.js +263 -0
  190. package/dist/persona/persona-service.js.map +1 -0
  191. package/dist/persona/policy-mutation-audit.d.ts +109 -0
  192. package/dist/persona/policy-mutation-audit.js +179 -0
  193. package/dist/persona/policy-mutation-audit.js.map +1 -0
  194. package/dist/persona/rule-delta.d.ts +30 -0
  195. package/dist/persona/rule-delta.js +82 -0
  196. package/dist/persona/rule-delta.js.map +1 -0
  197. package/dist/persona/types.d.ts +12 -0
  198. package/dist/pipeline/annotate.js +10 -2
  199. package/dist/pipeline/annotate.js.map +1 -1
  200. package/dist/pipeline/compile.js +14 -2
  201. package/dist/pipeline/compile.js.map +1 -1
  202. package/dist/pipeline/constitution-compiler.js.map +1 -1
  203. package/dist/pipeline/generate-with-repair.js.map +1 -1
  204. package/dist/pipeline/pipeline-runner.d.ts +63 -4
  205. package/dist/pipeline/pipeline-runner.js +159 -50
  206. package/dist/pipeline/pipeline-runner.js.map +1 -1
  207. package/dist/pipeline/pipeline-shared.d.ts +32 -3
  208. package/dist/pipeline/pipeline-shared.js +76 -8
  209. package/dist/pipeline/pipeline-shared.js.map +1 -1
  210. package/dist/pipeline/refresh-lists.js +3 -5
  211. package/dist/pipeline/refresh-lists.js.map +1 -1
  212. package/dist/pipeline/scenario-schema-validator.js.map +1 -1
  213. package/dist/{mux → pty}/pty-bridge.d.ts +41 -3
  214. package/dist/{mux → pty}/pty-bridge.js +71 -18
  215. package/dist/pty/pty-bridge.js.map +1 -0
  216. package/dist/pty/resolve-ironcurtain-bin.d.ts +17 -0
  217. package/dist/pty/resolve-ironcurtain-bin.js +22 -0
  218. package/dist/pty/resolve-ironcurtain-bin.js.map +1 -0
  219. package/dist/sandbox/index.d.ts +19 -4
  220. package/dist/sandbox/index.js +31 -18
  221. package/dist/sandbox/index.js.map +1 -1
  222. package/dist/servers/fetch-server.js +2 -0
  223. package/dist/servers/fetch-server.js.map +1 -1
  224. package/dist/session/index.d.ts +1 -1
  225. package/dist/session/index.js +20 -2
  226. package/dist/session/index.js.map +1 -1
  227. package/dist/session/preflight.d.ts +20 -35
  228. package/dist/session/preflight.js +92 -109
  229. package/dist/session/preflight.js.map +1 -1
  230. package/dist/session/resource-budget-tracker.js +8 -0
  231. package/dist/session/resource-budget-tracker.js.map +1 -1
  232. package/dist/session/session-manager.d.ts +7 -0
  233. package/dist/session/session-manager.js.map +1 -1
  234. package/dist/session/types.d.ts +23 -0
  235. package/dist/signal/bot-command.js +6 -1
  236. package/dist/signal/bot-command.js.map +1 -1
  237. package/dist/signal/setup-signal.js +1 -1
  238. package/dist/signal/setup-signal.js.map +1 -1
  239. package/dist/signal/signal-bot-daemon.d.ts +1 -0
  240. package/dist/signal/signal-bot-daemon.js +9 -2
  241. package/dist/signal/signal-bot-daemon.js.map +1 -1
  242. package/dist/signal/signal-container.d.ts +3 -3
  243. package/dist/signal/signal-container.js +2 -2
  244. package/dist/signal/signal-container.js.map +1 -1
  245. package/dist/trusted-process/call-circuit-breaker.d.ts +6 -0
  246. package/dist/trusted-process/call-circuit-breaker.js +22 -3
  247. package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
  248. package/dist/trusted-process/container-command.d.ts +35 -0
  249. package/dist/trusted-process/container-command.js +49 -0
  250. package/dist/trusted-process/container-command.js.map +1 -0
  251. package/dist/trusted-process/mcp-error-utils.js +4 -1
  252. package/dist/trusted-process/mcp-error-utils.js.map +1 -1
  253. package/dist/trusted-process/mcp-proxy-server.js +37 -3
  254. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  255. package/dist/trusted-process/policy-engine.js +13 -2
  256. package/dist/trusted-process/policy-engine.js.map +1 -1
  257. package/dist/trusted-process/tcp-server-transport.d.ts +19 -1
  258. package/dist/trusted-process/tcp-server-transport.js +56 -10
  259. package/dist/trusted-process/tcp-server-transport.js.map +1 -1
  260. package/dist/trusted-process/tool-call-coordinator.d.ts +22 -8
  261. package/dist/trusted-process/tool-call-coordinator.js +149 -65
  262. package/dist/trusted-process/tool-call-coordinator.js.map +1 -1
  263. package/dist/trusted-process/tool-call-pipeline.d.ts +8 -0
  264. package/dist/trusted-process/tool-call-pipeline.js +68 -34
  265. package/dist/trusted-process/tool-call-pipeline.js.map +1 -1
  266. package/dist/util/atomic-write.d.ts +31 -0
  267. package/dist/util/atomic-write.js +43 -0
  268. package/dist/util/atomic-write.js.map +1 -0
  269. package/dist/utils/preflight-checks.js +1 -3
  270. package/dist/utils/preflight-checks.js.map +1 -1
  271. package/dist/utils/stdio-guard.d.ts +1 -0
  272. package/dist/utils/stdio-guard.js +27 -0
  273. package/dist/utils/stdio-guard.js.map +1 -0
  274. package/dist/web-ui/__tests__/config-dispatch.test.d.ts +13 -0
  275. package/dist/web-ui/__tests__/config-dispatch.test.js +383 -0
  276. package/dist/web-ui/__tests__/config-dispatch.test.js.map +1 -0
  277. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +6 -0
  278. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -1
  279. package/dist/web-ui/conn-registry.d.ts +14 -0
  280. package/dist/web-ui/conn-registry.js +25 -0
  281. package/dist/web-ui/conn-registry.js.map +1 -0
  282. package/dist/web-ui/dispatch/config-dispatch.d.ts +15 -0
  283. package/dist/web-ui/dispatch/config-dispatch.js +277 -0
  284. package/dist/web-ui/dispatch/config-dispatch.js.map +1 -0
  285. package/dist/web-ui/dispatch/escalation-dispatch.js +11 -2
  286. package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -1
  287. package/dist/web-ui/dispatch/persona-dispatch.d.ts +46 -2
  288. package/dist/web-ui/dispatch/persona-dispatch.js +209 -56
  289. package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -1
  290. package/dist/web-ui/dispatch/pty-dispatch.d.ts +15 -0
  291. package/dist/web-ui/dispatch/pty-dispatch.js +66 -0
  292. package/dist/web-ui/dispatch/pty-dispatch.js.map +1 -0
  293. package/dist/web-ui/dispatch/session-dispatch.js +89 -6
  294. package/dist/web-ui/dispatch/session-dispatch.js.map +1 -1
  295. package/dist/web-ui/dispatch/types.d.ts +35 -0
  296. package/dist/web-ui/dispatch/types.js +42 -1
  297. package/dist/web-ui/dispatch/types.js.map +1 -1
  298. package/dist/web-ui/dispatch/workflow-dispatch.js +116 -5
  299. package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -1
  300. package/dist/web-ui/json-rpc-dispatch.js +4 -1
  301. package/dist/web-ui/json-rpc-dispatch.js.map +1 -1
  302. package/dist/web-ui/pty-session-manager.d.ts +234 -0
  303. package/dist/web-ui/pty-session-manager.js +599 -0
  304. package/dist/web-ui/pty-session-manager.js.map +1 -0
  305. package/dist/web-ui/web-event-bus.d.ts +34 -1
  306. package/dist/web-ui/web-event-bus.js.map +1 -1
  307. package/dist/web-ui/web-session-transport.js.map +1 -1
  308. package/dist/web-ui/web-ui-server.d.ts +22 -0
  309. package/dist/web-ui/web-ui-server.js +41 -0
  310. package/dist/web-ui/web-ui-server.js.map +1 -1
  311. package/dist/web-ui/web-ui-types.d.ts +212 -5
  312. package/dist/web-ui/web-ui-types.js.map +1 -1
  313. package/dist/web-ui-static/assets/index-CCVI4nEK.css +1 -0
  314. package/dist/web-ui-static/assets/index-Dm012gvY.js +114 -0
  315. package/dist/web-ui-static/index.html +2 -2
  316. package/dist/workflow/cli-shared.d.ts +62 -0
  317. package/dist/workflow/cli-shared.js +117 -0
  318. package/dist/workflow/cli-shared.js.map +1 -0
  319. package/dist/workflow/cli-support.js +0 -2
  320. package/dist/workflow/cli-support.js.map +1 -1
  321. package/dist/workflow/container-snapshots.d.ts +29 -0
  322. package/dist/workflow/container-snapshots.js +137 -0
  323. package/dist/workflow/container-snapshots.js.map +1 -0
  324. package/dist/workflow/daemon-gate-commands.d.ts +21 -0
  325. package/dist/workflow/daemon-gate-commands.js +595 -0
  326. package/dist/workflow/daemon-gate-commands.js.map +1 -0
  327. package/dist/workflow/discovery.d.ts +29 -0
  328. package/dist/workflow/discovery.js +67 -7
  329. package/dist/workflow/discovery.js.map +1 -1
  330. package/dist/workflow/lane-template.d.ts +89 -0
  331. package/dist/workflow/lane-template.js +141 -0
  332. package/dist/workflow/lane-template.js.map +1 -0
  333. package/dist/workflow/lint.d.ts +7 -1
  334. package/dist/workflow/lint.js +186 -3
  335. package/dist/workflow/lint.js.map +1 -1
  336. package/dist/workflow/machine-builder.d.ts +90 -2
  337. package/dist/workflow/machine-builder.js +222 -29
  338. package/dist/workflow/machine-builder.js.map +1 -1
  339. package/dist/workflow/message-log.d.ts +15 -1
  340. package/dist/workflow/message-log.js.map +1 -1
  341. package/dist/workflow/orchestrator.d.ts +505 -1
  342. package/dist/workflow/orchestrator.js +1725 -69
  343. package/dist/workflow/orchestrator.js.map +1 -1
  344. package/dist/workflow/prompt-builder.js +2 -1
  345. package/dist/workflow/prompt-builder.js.map +1 -1
  346. package/dist/workflow/run-state-command.js +1 -1
  347. package/dist/workflow/run-state-command.js.map +1 -1
  348. package/dist/workflow/shutdown-signals.d.ts +7 -0
  349. package/dist/workflow/shutdown-signals.js +40 -0
  350. package/dist/workflow/shutdown-signals.js.map +1 -0
  351. package/dist/workflow/status-parser.js +19 -4
  352. package/dist/workflow/status-parser.js.map +1 -1
  353. package/dist/workflow/types.d.ts +123 -23
  354. package/dist/workflow/types.js +2 -0
  355. package/dist/workflow/types.js.map +1 -1
  356. package/dist/workflow/validate.d.ts +23 -1
  357. package/dist/workflow/validate.js +311 -22
  358. package/dist/workflow/validate.js.map +1 -1
  359. package/dist/workflow/workflow-command.d.ts +0 -17
  360. package/dist/workflow/workflow-command.js +118 -94
  361. package/dist/workflow/workflow-command.js.map +1 -1
  362. package/dist/workflow/workflow-discovery.d.ts +2 -0
  363. package/dist/workflow/workflow-discovery.js +22 -1
  364. package/dist/workflow/workflow-discovery.js.map +1 -1
  365. package/dist/workflow/workflow-manager.d.ts +34 -0
  366. package/dist/workflow/workflow-manager.js +52 -1
  367. package/dist/workflow/workflow-manager.js.map +1 -1
  368. package/dist/workflow/workflows/design-and-code/README.md +50 -0
  369. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  370. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  371. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  372. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  373. package/dist/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  374. package/dist/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  375. package/dist/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  376. package/dist/workflow/workflows/evolve/README.md +66 -0
  377. package/dist/workflow/workflows/evolve/scripts/LICENSE +201 -0
  378. package/dist/workflow/workflows/evolve/scripts/README.md +314 -0
  379. package/dist/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  380. package/dist/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  381. package/dist/workflow/workflows/evolve/scripts/evolve-db +16 -0
  382. package/dist/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  383. package/dist/workflow/workflows/evolve/scripts/evolve-files +16 -0
  384. package/dist/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  385. package/dist/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  386. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  387. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  388. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  389. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  390. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  391. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  392. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  393. package/dist/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  394. package/dist/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  395. package/dist/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  396. package/dist/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  397. package/dist/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  398. package/dist/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  399. package/dist/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  400. package/dist/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  401. package/dist/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  402. package/dist/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  403. package/dist/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  404. package/dist/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  405. package/dist/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  406. package/dist/workflow/workflows/evolve/workflow.yaml +646 -0
  407. package/dist/workflow/workflows/test-email-summary/workflow.yaml +1 -0
  408. package/dist/workflow/workflows/vuln-discovery/README.md +54 -0
  409. package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +6 -1
  410. package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +1 -0
  411. package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +4 -0
  412. package/dist/workflow/workflows/vuln-discovery/workflow.yaml +13 -8
  413. package/docker/Dockerfile.base +22 -0
  414. package/docker/Dockerfile.base.arm64 +18 -0
  415. package/docker/Dockerfile.claude-code +3 -1
  416. package/docker/Dockerfile.codex +17 -0
  417. package/docker/Dockerfile.goose +3 -1
  418. package/docker/entrypoint-claude-code.sh +28 -45
  419. package/docker/entrypoint-codex.sh +42 -0
  420. package/docker/entrypoint-goose.sh +5 -24
  421. package/docker/entrypoint-uid-remap.sh +93 -0
  422. package/package.json +39 -30
  423. package/src/workflow/workflows/design-and-code/README.md +50 -0
  424. package/src/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  425. package/src/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  426. package/src/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  427. package/src/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  428. package/src/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  429. package/src/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  430. package/src/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  431. package/src/workflow/workflows/evolve/README.md +66 -0
  432. package/src/workflow/workflows/evolve/scripts/LICENSE +201 -0
  433. package/src/workflow/workflows/evolve/scripts/README.md +314 -0
  434. package/src/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  435. package/src/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  436. package/src/workflow/workflows/evolve/scripts/evolve-db +16 -0
  437. package/src/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  438. package/src/workflow/workflows/evolve/scripts/evolve-files +16 -0
  439. package/src/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  440. package/src/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  441. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  442. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  443. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  444. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  445. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  446. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  447. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  448. package/src/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  449. package/src/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  450. package/src/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  451. package/src/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  452. package/src/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  453. package/src/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  454. package/src/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  455. package/src/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  456. package/src/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  457. package/src/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  458. package/src/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  459. package/src/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  460. package/src/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  461. package/src/workflow/workflows/evolve/workflow.yaml +646 -0
  462. package/src/workflow/workflows/test-email-summary/workflow.yaml +1 -0
  463. package/src/workflow/workflows/vuln-discovery/README.md +54 -0
  464. package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +6 -1
  465. package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +1 -0
  466. package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +4 -0
  467. package/src/workflow/workflows/vuln-discovery/workflow.yaml +13 -8
  468. package/dist/mux/pty-bridge.js.map +0 -1
  469. package/dist/mux/trusted-input.js.map +0 -1
  470. package/dist/web-ui-static/assets/index-BFO5z1js.js +0 -76
  471. package/dist/web-ui-static/assets/index-CCUwrkIL.css +0 -1
  472. package/packages/web-ui/README.md +0 -113
@@ -13,6 +13,7 @@
13
13
  import type { LanguageModelV3 } from '@ai-sdk/provider';
14
14
  import type { LanguageModel } from 'ai';
15
15
  import type { MCPServerConfig } from '../config/types.js';
16
+ import { type ServerProgressReporter } from './pipeline-shared.js';
16
17
  import type { PromptCacheStrategy } from '../session/prompt-cache.js';
17
18
  import type { CompiledPolicyFile, CompiledRule, DynamicListsFile, ListDefinition, StoredToolAnnotationsFile, TestScenario, ToolAnnotation } from './types.js';
18
19
  /**
@@ -67,6 +68,41 @@ export interface PipelineRunConfig {
67
68
  * When undefined, the pre-filter is skipped.
68
69
  */
69
70
  readonly prefilterText?: string;
71
+ /**
72
+ * Factory for per-server progress reporters. When provided, both the
73
+ * sequential and parallel paths use this instead of the built-in ora/spinner
74
+ * (sequential) or multi-line (parallel) reporters. Non-TTY callers (e.g. the
75
+ * daemon driving a compile over the WS surface) inject an event-bus reporter
76
+ * here. Absent => the built-in reporters are used (CLI behavior).
77
+ */
78
+ readonly reporterFactory?: (serverName: string) => ServerProgressReporter;
79
+ /**
80
+ * Cooperative cancellation. Threaded into every LLM call (`abortSignal`) and
81
+ * checked at each per-phase boundary so a long compile aborts promptly.
82
+ */
83
+ readonly signal?: AbortSignal;
84
+ /**
85
+ * Suppress narrative stderr output. The daemon path sets this so progress is
86
+ * routed through `onProgress`/`reporterFactory` instead of polluting stderr.
87
+ * When `quiet` is true, a `reporterFactory` MUST be supplied — there is no
88
+ * silent fallback to the ora/spinner reporter (it would write to stderr).
89
+ */
90
+ readonly quiet?: boolean;
91
+ /**
92
+ * Whether the list resolver may connect to live MCP servers to resolve
93
+ * data-backed (`requiresMcp: true`) lists. Defaults to true (CLI). The WS
94
+ * compile path sets this to false: there is no live MCP available, so any
95
+ * list definition that requires MCP fails fast with a typed error rather than
96
+ * silently resolving to empty.
97
+ */
98
+ readonly allowMcpLists?: boolean;
99
+ /**
100
+ * Optional validation hook invoked on the in-memory merged policy BEFORE any
101
+ * artifact is written. Throwing rejects the compile and leaves all prior
102
+ * artifacts intact (no partial write). Used by the WS surface to enforce
103
+ * additional invariants (e.g. server scoping) atomically.
104
+ */
105
+ readonly validateCompiled?: (policy: CompiledPolicyFile, dynamicLists?: DynamicListsFile) => void;
70
106
  }
71
107
  /**
72
108
  * LLM model references shared across pipeline runs.
@@ -80,8 +116,14 @@ export interface PipelineModels {
80
116
  /** Cheap model for pre-filter classification (Haiku). */
81
117
  readonly prefilterModel: LanguageModel;
82
118
  }
83
- /** Creates PipelineModels from user config. Delegates to shared createPipelineLlm. */
84
- export declare function createPipelineModels(logDir?: string): Promise<PipelineModels>;
119
+ /**
120
+ * Creates PipelineModels from user config. Delegates to shared createPipelineLlm.
121
+ *
122
+ * `logFileName` overrides the default `llm-interactions.jsonl` filename so a
123
+ * caller can scope the log to a single operation (e.g. the persona compile
124
+ * orchestrator passes `<operationId>.jsonl`).
125
+ */
126
+ export declare function createPipelineModels(logDir?: string, logFileName?: string): Promise<PipelineModels>;
85
127
  /** Output from a single server's compilation cycle. */
86
128
  interface ServerCompilationResult {
87
129
  readonly serverName: string;
@@ -92,6 +134,23 @@ interface ServerCompilationResult {
92
134
  readonly constitutionHash: string;
93
135
  readonly resolvedLists?: DynamicListsFile;
94
136
  }
137
+ /**
138
+ * A narrative logger for non-progress output (summaries, warnings, gaps).
139
+ * In quiet mode the message is routed to `onProgress` (so the daemon can
140
+ * surface it) instead of stderr; otherwise it goes to stderr as before.
141
+ */
142
+ type NarrativeLogger = (message: string) => void;
143
+ /**
144
+ * Thrown when a list definition requires live MCP access (`requiresMcp: true`)
145
+ * but the run was configured with `allowMcpLists: false` (e.g. the WS compile
146
+ * path, which has no live MCP). Carries a discriminant `code` so callers can
147
+ * detect it without importing this module (`error.code === 'MCP_LISTS_DISALLOWED'`).
148
+ */
149
+ export declare class McpListsDisallowedError extends Error {
150
+ readonly listNames: string[];
151
+ readonly code = "MCP_LISTS_DISALLOWED";
152
+ constructor(listNames: string[]);
153
+ }
95
154
  /**
96
155
  * Computes a content hash for a single server's policy compilation inputs.
97
156
  * The hash includes the server name, constitution, annotations, and prompt
@@ -114,7 +173,7 @@ export declare function validateServerScoping(serverName: string, rules: Compile
114
173
  * generationPrompt values for the same named list, uses first-wins
115
174
  * (alphabetical server order) and logs a warning.
116
175
  */
117
- export declare function deduplicateListDefinitions(defs: ListDefinition[]): ListDefinition[];
176
+ export declare function deduplicateListDefinitions(defs: ListDefinition[], log?: NarrativeLogger): ListDefinition[];
118
177
  /**
119
178
  * Merges per-server compilation results into a single CompiledPolicyFile.
120
179
  * Rules are concatenated in alphabetical server order for determinism.
@@ -123,7 +182,7 @@ export declare function deduplicateListDefinitions(defs: ListDefinition[]): List
123
182
  export declare function mergeServerResults(results: ServerCompilationResult[], constitutionHash: string, skippedServers?: Array<{
124
183
  serverName: string;
125
184
  reason: string;
126
- }>): CompiledPolicyFile;
185
+ }>, log?: NarrativeLogger): CompiledPolicyFile;
127
186
  /**
128
187
  * Encapsulates the full policy compilation pipeline:
129
188
  * compile rules -> generate scenarios -> verify -> repair loop.
@@ -20,17 +20,23 @@ import { PolicyEngine } from '../trusted-process/policy-engine.js';
20
20
  import { buildCompilerSystemPrompt, ConstitutionCompilerSession, formatAnnotationsSummary, validateCompiledRules, } from './constitution-compiler.js';
21
21
  import { getHandwrittenScenarios } from './handwritten-scenarios.js';
22
22
  import { resolveAllLists } from './list-resolver.js';
23
- import { computeHash, loadExistingArtifact, loadStoredToolAnnotationsFile, resolveRulePaths, writeArtifact, createPipelineLlm, createPerServerModel, createThrottledModel, SpinnerProgressReporter, } from './pipeline-shared.js';
23
+ import { computeHash, loadExistingArtifact, loadStoredToolAnnotationsFile, resolveRulePaths, writeArtifact, removeArtifactIfExists, createPipelineLlm, createPerServerModel, createThrottledModel, SpinnerProgressReporter, } from './pipeline-shared.js';
24
24
  import { resolveStoredAnnotationsFile } from '../types/argument-roles.js';
25
25
  import { applyScenarioCorrections, buildJudgeSystemPrompt, detectAllDefaultRoleFallbacks, extractScenarioCorrections, filterStructuralConflicts, PolicyVerifierSession, verifyPolicy, } from './policy-verifier.js';
26
26
  import { filterInvalidSchemaScenarios } from './scenario-schema-validator.js';
27
27
  import { buildGeneratorSystemPrompt, generateScenarios, repairScenarios } from './scenario-generator.js';
28
28
  import { prefilterServers } from './server-prefilter.js';
29
29
  import { connectViaProxy } from './proxy-mcp-connections.js';
30
- /** Creates PipelineModels from user config. Delegates to shared createPipelineLlm. */
31
- export async function createPipelineModels(logDir) {
30
+ /**
31
+ * Creates PipelineModels from user config. Delegates to shared createPipelineLlm.
32
+ *
33
+ * `logFileName` overrides the default `llm-interactions.jsonl` filename so a
34
+ * caller can scope the log to a single operation (e.g. the persona compile
35
+ * orchestrator passes `<operationId>.jsonl`).
36
+ */
37
+ export async function createPipelineModels(logDir, logFileName) {
32
38
  const effectiveLogDir = logDir ?? resolve(process.cwd(), 'generated');
33
- const llm = await createPipelineLlm(effectiveLogDir, 'unknown');
39
+ const llm = await createPipelineLlm(effectiveLogDir, 'unknown', logFileName);
34
40
  const userConfig = loadUserConfig();
35
41
  const haikuBaseLlm = await createLanguageModel(userConfig.prefilterModelId, userConfig);
36
42
  const { model: prefilterModel } = createPerServerModel(haikuBaseLlm, llm.logPath, 'prefilter');
@@ -57,18 +63,20 @@ function computeScenariosHash(systemPrompt, handwrittenScenarios) {
57
63
  * `mcpServers` may be undefined when compile-policy is run with `--no-mcp`,
58
64
  * in which case the check is skipped.
59
65
  */
60
- function warnOnCompileAnnotationGaps(mcpServers, storedAnnotationsFile) {
66
+ function warnOnCompileAnnotationGaps(mcpServers, storedAnnotationsFile, log = (msg) => {
67
+ console.error(msg);
68
+ }) {
61
69
  if (!mcpServers)
62
70
  return;
63
71
  const { missing } = findAnnotationServerDrift(storedAnnotationsFile, mcpServers);
64
72
  if (missing.length === 0)
65
73
  return;
66
- console.error(chalk.yellow(`Warning: ${missing.length} configured MCP server(s) have no tool annotations: ${missing.map(safeForDisplay).join(', ')}.`));
67
- console.error(chalk.yellow(' The compiled policy will have no rules for these servers and their tools will be denied.'));
74
+ log(chalk.yellow(`Warning: ${missing.length} configured MCP server(s) have no tool annotations: ${missing.map(safeForDisplay).join(', ')}.`));
75
+ log(chalk.yellow(' The compiled policy will have no rules for these servers and their tools will be denied.'));
68
76
  for (const name of missing) {
69
- console.error(chalk.yellow(` Run \`ironcurtain annotate-tools --server ${safeForDisplay(name)}\` to annotate this server.`));
77
+ log(chalk.yellow(` Run \`ironcurtain annotate-tools --server ${safeForDisplay(name)}\` to annotate this server.`));
70
78
  }
71
- console.error('');
79
+ log('');
72
80
  }
73
81
  function extractPermittedDirectories(rules) {
74
82
  const dirs = new Set();
@@ -131,6 +139,31 @@ function filterAndLogStructuralConflicts(engine, scenarios, label = 'Discarded s
131
139
  function toError(err) {
132
140
  return err instanceof Error ? err : new Error(String(err));
133
141
  }
142
+ function makeNarrativeLogger(config) {
143
+ if (config.quiet) {
144
+ const onProgress = config.onProgress;
145
+ return (msg) => {
146
+ onProgress?.(msg);
147
+ };
148
+ }
149
+ return (msg) => {
150
+ console.error(msg);
151
+ };
152
+ }
153
+ /**
154
+ * Resolves the reporter for a server. A caller-supplied `reporterFactory`
155
+ * always wins. When absent: quiet mode has NO fallback (the built-in reporters
156
+ * write to stderr), so it is a configuration error; non-quiet mode uses the
157
+ * built-in concrete reporter via `makeDefault`.
158
+ */
159
+ function resolveReporter(config, serverName, makeDefault) {
160
+ if (config.reporterFactory)
161
+ return config.reporterFactory(serverName);
162
+ if (config.quiet) {
163
+ throw new Error(`quiet:true requires a reporterFactory; no stderr-free reporter is available for server "${serverName}".`);
164
+ }
165
+ return makeDefault();
166
+ }
134
167
  class RuleValidationError extends Error {
135
168
  errors;
136
169
  constructor(errors) {
@@ -139,6 +172,23 @@ class RuleValidationError extends Error {
139
172
  this.name = 'RuleValidationError';
140
173
  }
141
174
  }
175
+ /**
176
+ * Thrown when a list definition requires live MCP access (`requiresMcp: true`)
177
+ * but the run was configured with `allowMcpLists: false` (e.g. the WS compile
178
+ * path, which has no live MCP). Carries a discriminant `code` so callers can
179
+ * detect it without importing this module (`error.code === 'MCP_LISTS_DISALLOWED'`).
180
+ */
181
+ export class McpListsDisallowedError extends Error {
182
+ listNames;
183
+ code = 'MCP_LISTS_DISALLOWED';
184
+ constructor(listNames) {
185
+ super(`Dynamic list(s) require live MCP access but it is disabled (allowMcpLists: false): ` +
186
+ `${listNames.join(', ')}. Resolve these lists in an environment with MCP available, ` +
187
+ `or remove the MCP-backed list definitions.`);
188
+ this.listNames = listNames;
189
+ this.name = 'McpListsDisallowedError';
190
+ }
191
+ }
142
192
  function validateRulesOrThrow(rules, listDefinitions = [], reporter) {
143
193
  const warn = (msg) => (reporter ? reporter.warn(msg) : console.error(msg));
144
194
  const ruleValidation = validateCompiledRules(rules, listDefinitions);
@@ -279,7 +329,9 @@ export function validateServerScoping(serverName, rules) {
279
329
  * generationPrompt values for the same named list, uses first-wins
280
330
  * (alphabetical server order) and logs a warning.
281
331
  */
282
- export function deduplicateListDefinitions(defs) {
332
+ export function deduplicateListDefinitions(defs, log = (msg) => {
333
+ console.error(msg);
334
+ }) {
283
335
  const seen = new Map();
284
336
  for (const def of defs) {
285
337
  const existing = seen.get(def.name);
@@ -287,7 +339,7 @@ export function deduplicateListDefinitions(defs) {
287
339
  seen.set(def.name, def);
288
340
  }
289
341
  else if (existing.generationPrompt !== def.generationPrompt) {
290
- console.error(` Warning: list "${def.name}" has divergent generation prompts ` +
342
+ log(` Warning: list "${def.name}" has divergent generation prompts ` +
291
343
  `across servers. Using first definition (alphabetical server order).`);
292
344
  }
293
345
  }
@@ -298,11 +350,11 @@ export function deduplicateListDefinitions(defs) {
298
350
  * Rules are concatenated in alphabetical server order for determinism.
299
351
  * List definitions are deduplicated by name.
300
352
  */
301
- export function mergeServerResults(results, constitutionHash, skippedServers) {
353
+ export function mergeServerResults(results, constitutionHash, skippedServers, log) {
302
354
  const sortedResults = [...results].sort((a, b) => a.serverName.localeCompare(b.serverName));
303
355
  const allRules = sortedResults.flatMap((r) => r.rules);
304
356
  const allListDefs = sortedResults.flatMap((r) => r.listDefinitions);
305
- const uniqueListDefs = deduplicateListDefinitions(allListDefs);
357
+ const uniqueListDefs = deduplicateListDefinitions(allListDefs, log);
306
358
  const mergedInputHash = computeHash(...sortedResults.map((r) => r.inputHash));
307
359
  const artifact = {
308
360
  generatedAt: new Date().toISOString(),
@@ -366,21 +418,22 @@ export class PipelineRunner {
366
418
  * to select the appropriate compiler prompt.
367
419
  */
368
420
  async runPerServer(config) {
421
+ const log = makeNarrativeLogger(config);
422
+ config.signal?.throwIfAborted();
369
423
  const storedAnnotationsFile = config.preloadedStoredAnnotations ??
370
424
  loadStoredToolAnnotationsFile(config.toolAnnotationsDir, config.toolAnnotationsFallbackDir);
371
425
  if (!storedAnnotationsFile) {
372
426
  throw new Error("tool-annotations.json not found. Run 'ironcurtain annotate-tools' first.");
373
427
  }
374
- warnOnCompileAnnotationGaps(config.mcpServers, storedAnnotationsFile);
428
+ warnOnCompileAnnotationGaps(config.mcpServers, storedAnnotationsFile, log);
375
429
  // Resolve conditional role specs to flat annotations for compiler prompts
376
430
  const toolAnnotationsFile = resolveStoredAnnotationsFile(storedAnnotationsFile);
377
431
  const constitutionHash = computeHash(config.constitutionInput);
378
432
  // Phase 1: Per-server compilation (with optional Haiku pre-filter)
379
433
  const { results: serverResults, skippedServers } = await this.compileAllServers(config, toolAnnotationsFile, constitutionHash, storedAnnotationsFile);
380
- // Phase 2: Merge
381
- const mergedPolicy = mergeServerResults(serverResults, constitutionHash, skippedServers);
382
- writeArtifact(config.outputDir, 'compiled-policy.json', mergedPolicy);
383
- // Merge scenarios for the global artifact
434
+ config.signal?.throwIfAborted();
435
+ // Phase 2: Merge (all three artifacts assembled in memory before any write)
436
+ const mergedPolicy = mergeServerResults(serverResults, constitutionHash, skippedServers, log);
384
437
  const allScenarios = serverResults.flatMap((r) => r.scenarios);
385
438
  const mergedScenariosFile = {
386
439
  generatedAt: new Date().toISOString(),
@@ -388,10 +441,10 @@ export class PipelineRunner {
388
441
  inputHash: mergedPolicy.inputHash,
389
442
  scenarios: allScenarios,
390
443
  };
391
- writeArtifact(config.outputDir, 'test-scenarios.json', mergedScenariosFile);
392
- // Phase 3: Merge per-server resolved lists into global dynamic-lists.json
444
+ // Phase 3: Merge per-server resolved lists into the global dynamic-lists.json.
393
445
  // Each server resolved its own lists during compileServer(); merge them here.
394
446
  const listDefinitions = mergedPolicy.listDefinitions ?? [];
447
+ let mergedDynamicLists;
395
448
  if (listDefinitions.length > 0) {
396
449
  const keptListNames = new Set(listDefinitions.map((d) => d.name));
397
450
  const mergedListEntries = {};
@@ -406,12 +459,34 @@ export class PipelineRunner {
406
459
  }
407
460
  }
408
461
  }
409
- const mergedDynamicLists = {
462
+ mergedDynamicLists = {
410
463
  generatedAt: new Date().toISOString(),
411
464
  lists: mergedListEntries,
412
465
  };
466
+ }
467
+ // Optional caller validation runs on the in-memory policy BEFORE any artifact
468
+ // is written, so a rejection leaves the prior generation's files intact.
469
+ config.validateCompiled?.(mergedPolicy, mergedDynamicLists);
470
+ // Write order is the REVERSE of the runtime read order in
471
+ // loadPersonaPolicyArtifacts (compiled-policy first, then dynamic-lists):
472
+ // 1. dynamic-lists.json (or atomically REMOVE the stale file when there
473
+ // are no list definitions — fixing the previous skip-leaves-stale bug)
474
+ // 2. test-scenarios.json
475
+ // 3. compiled-policy.json LAST
476
+ // Each write is per-file atomic (tmp + rename). The dependency ordering
477
+ // guarantees a concurrent reader that observes the NEW compiled-policy.json
478
+ // already sees the matching dynamic-lists.json; the reverse interleaving
479
+ // (new lists + old compiled) is fail-safe because an unknown @list-id
480
+ // resolves to the empty allowlist => deny (PolicyEngine.getEffectiveListValues
481
+ // returns [] for a missing list rather than throwing).
482
+ if (mergedDynamicLists) {
413
483
  writeArtifact(config.outputDir, 'dynamic-lists.json', mergedDynamicLists);
414
484
  }
485
+ else {
486
+ removeArtifactIfExists(config.outputDir, 'dynamic-lists.json');
487
+ }
488
+ writeArtifact(config.outputDir, 'test-scenarios.json', mergedScenariosFile);
489
+ writeArtifact(config.outputDir, 'compiled-policy.json', mergedPolicy);
415
490
  // Cross-server verification is intentionally omitted. Per-server rules compose
416
491
  // correctly by construction: every rule has server: [serverName] (enforced by
417
492
  // Zod schema), the engine checks cond.server.includes(request.serverName) so
@@ -423,16 +498,16 @@ export class PipelineRunner {
423
498
  const totalRules = mergedPolicy.rules.length;
424
499
  const totalScenarios = allScenarios.length;
425
500
  const serverCount = serverResults.length;
426
- console.error('');
427
- console.error(` Servers compiled: ${serverCount}`);
428
- console.error(` Rules: ${totalRules}`);
429
- console.error(` Scenarios tested: ${totalScenarios}`);
430
- console.error(` Artifacts written to: ${chalk.dim(config.outputDir + '/')}`);
501
+ log('');
502
+ log(` Servers compiled: ${serverCount}`);
503
+ log(` Rules: ${totalRules}`);
504
+ log(` Scenarios tested: ${totalScenarios}`);
505
+ log(` Artifacts written to: ${chalk.dim(config.outputDir + '/')}`);
431
506
  if (config.llmLogPath) {
432
- console.error(` LLM interaction log: ${chalk.dim(config.llmLogPath)}`);
507
+ log(` LLM interaction log: ${chalk.dim(config.llmLogPath)}`);
433
508
  }
434
- console.error('');
435
- console.error(chalk.green.bold('Policy compilation successful!'));
509
+ log('');
510
+ log(chalk.green.bold('Policy compilation successful!'));
436
511
  return mergedPolicy;
437
512
  }
438
513
  /**
@@ -441,6 +516,7 @@ export class PipelineRunner {
441
516
  * Multi-server compilation uses parallel execution with a multi-line progress display.
442
517
  */
443
518
  async compileAllServers(config, toolAnnotationsFile, constitutionHash, storedAnnotationsFile) {
519
+ const log = makeNarrativeLogger(config);
444
520
  const serverEntries = Object.entries(toolAnnotationsFile.servers);
445
521
  // Apply server filter if provided
446
522
  const { serverFilter } = config;
@@ -457,7 +533,7 @@ export class PipelineRunner {
457
533
  if (config.prefilterText !== undefined && !config.serverFilter) {
458
534
  const prefilterText = config.prefilterText;
459
535
  if (prefilterText.trim() === '') {
460
- console.error(chalk.yellow('Pre-filter input is empty. All servers skipped (default-deny applies).'));
536
+ log(chalk.yellow('Pre-filter input is empty. All servers skipped (default-deny applies).'));
461
537
  const allSkipped = filteredEntries.map(([name]) => ({
462
538
  serverName: name,
463
539
  reason: 'No input text provided — all servers skipped by default',
@@ -470,7 +546,7 @@ export class PipelineRunner {
470
546
  entriesToCompile = filteredEntries.filter(([name]) => {
471
547
  const decision = decisionMap.get(name);
472
548
  if (decision?.skip) {
473
- console.error(` ${chalk.dim(name)}: ${chalk.yellow('skipped')} — ${decision.reason}`);
549
+ log(` ${chalk.dim(name)}: ${chalk.yellow('skipped')} — ${decision.reason}`);
474
550
  skippedServers.push({ serverName: name, reason: decision.reason });
475
551
  return false;
476
552
  }
@@ -478,11 +554,11 @@ export class PipelineRunner {
478
554
  });
479
555
  const skippedCount = filteredEntries.length - entriesToCompile.length;
480
556
  if (skippedCount > 0) {
481
- console.error(` Pre-filter: ${skippedCount} server(s) skipped, ${entriesToCompile.length} proceeding`);
557
+ log(` Pre-filter: ${skippedCount} server(s) skipped, ${entriesToCompile.length} proceeding`);
482
558
  }
483
559
  }
484
560
  if (entriesToCompile.length === 0) {
485
- console.error(chalk.yellow('All servers skipped by pre-filter. Policy will use default-deny only.'));
561
+ log(chalk.yellow('All servers skipped by pre-filter. Policy will use default-deny only.'));
486
562
  return { results: [], skippedServers };
487
563
  }
488
564
  // Load handwritten scenarios once, then filter per server (avoids N redundant loads)
@@ -513,20 +589,22 @@ export class PipelineRunner {
513
589
  return { results, skippedServers };
514
590
  }
515
591
  /** Creates a per-server model wrapped with LLM call throttling. */
516
- createServerModel(serverName, llmSemaphore) {
592
+ createServerModel(serverName, llmSemaphore, signal) {
517
593
  const { model, logContext } = createPerServerModel(this.baseLlm, this.logPath, serverName);
518
- return { model: createThrottledModel(model, llmSemaphore), logContext };
594
+ return { model: createThrottledModel(model, llmSemaphore, signal), logContext };
519
595
  }
520
596
  async compileServersSequential(filteredEntries, config, constitutionHash, buildUnit, llmSemaphore) {
597
+ const log = makeNarrativeLogger(config);
521
598
  const results = [];
522
599
  const totalServers = filteredEntries.length;
523
600
  const failedServers = [];
524
601
  for (let i = 0; i < filteredEntries.length; i++) {
602
+ config.signal?.throwIfAborted();
525
603
  const [serverName] = filteredEntries[i];
526
- console.error('');
527
- console.error(chalk.bold(`[${i + 1}/${totalServers}] Compiling server: ${serverName}`));
528
- const { model, logContext } = this.createServerModel(serverName, llmSemaphore);
529
- const reporter = new SpinnerProgressReporter(serverName);
604
+ log('');
605
+ log(chalk.bold(`[${i + 1}/${totalServers}] Compiling server: ${serverName}`));
606
+ const { model, logContext } = this.createServerModel(serverName, llmSemaphore, config.signal);
607
+ const reporter = resolveReporter(config, serverName, () => new SpinnerProgressReporter(serverName));
530
608
  try {
531
609
  const result = await this.compileServer(buildUnit(serverName), config, constitutionHash, model, logContext, reporter);
532
610
  results.push(result);
@@ -534,20 +612,36 @@ export class PipelineRunner {
534
612
  catch (err) {
535
613
  const coerced = toError(err);
536
614
  reporter.fail('compiling', coerced);
537
- console.error(` ${chalk.red(`Server "${serverName}" failed:`)} ${coerced.message}`);
615
+ // A disabled-MCP-list error is a run-level configuration failure, not a
616
+ // per-server verification failure. Surface it past the failedServers
617
+ // aggregation so the orchestrator can map it to LIST_REQUIRES_MCP
618
+ // instead of the generic "all servers failed compilation" error.
619
+ if (err instanceof McpListsDisallowedError)
620
+ throw err;
621
+ log(` ${chalk.red(`Server "${serverName}" failed:`)} ${coerced.message}`);
538
622
  failedServers.push(serverName);
539
623
  }
540
624
  }
541
- return this.handleCompilationResults(results, failedServers);
625
+ return this.handleCompilationResults(results, failedServers, log);
542
626
  }
543
627
  async compileServersParallel(filteredEntries, config, constitutionHash, buildUnit, llmSemaphore, serverLimit) {
628
+ const log = makeNarrativeLogger(config);
544
629
  // Lazy import to avoid loading parallel-progress.ts when not needed
545
630
  const { ParallelProgressDisplay, ParallelProgressReporter } = await import('./parallel-progress.js');
546
631
  const serverNames = filteredEntries.map(([name]) => name);
547
- const display = new ParallelProgressDisplay(serverNames);
632
+ // When a reporterFactory is injected the caller drives its own display; the
633
+ // built-in multi-line ParallelProgressDisplay would write to stderr, so it
634
+ // is only constructed for the built-in reporter path.
635
+ const display = config.reporterFactory ? undefined : new ParallelProgressDisplay(serverNames);
548
636
  const settled = await Promise.allSettled(filteredEntries.map(([serverName]) => serverLimit(async () => {
549
- const { model, logContext } = this.createServerModel(serverName, llmSemaphore);
550
- const reporter = new ParallelProgressReporter(display, serverName);
637
+ config.signal?.throwIfAborted();
638
+ const { model, logContext } = this.createServerModel(serverName, llmSemaphore, config.signal);
639
+ const reporter = resolveReporter(config, serverName, () => {
640
+ // `display` is defined whenever reporterFactory is absent (built-in path).
641
+ if (!display)
642
+ throw new Error('internal: built-in parallel display missing');
643
+ return new ParallelProgressReporter(display, serverName);
644
+ });
551
645
  try {
552
646
  return await this.compileServer(buildUnit(serverName), config, constitutionHash, model, logContext, reporter);
553
647
  }
@@ -556,7 +650,7 @@ export class PipelineRunner {
556
650
  throw err;
557
651
  }
558
652
  })));
559
- display.finish();
653
+ display?.finish();
560
654
  const results = [];
561
655
  const failedServers = [];
562
656
  for (const [i, outcome] of settled.entries()) {
@@ -564,16 +658,21 @@ export class PipelineRunner {
564
658
  results.push(outcome.value);
565
659
  }
566
660
  else {
661
+ // A disabled-MCP-list error is a run-level configuration failure, not a
662
+ // per-server verification failure. Surface it past the failedServers
663
+ // aggregation so the orchestrator can map it to LIST_REQUIRES_MCP.
664
+ if (outcome.reason instanceof McpListsDisallowedError)
665
+ throw outcome.reason;
567
666
  const serverName = filteredEntries[i][0];
568
667
  failedServers.push(serverName);
569
668
  }
570
669
  }
571
- return this.handleCompilationResults(results, failedServers);
670
+ return this.handleCompilationResults(results, failedServers, log);
572
671
  }
573
- handleCompilationResults(results, failedServers) {
672
+ handleCompilationResults(results, failedServers, log) {
574
673
  if (failedServers.length > 0) {
575
- console.error(`\n${chalk.yellow(`Warning: ${failedServers.length} server(s) failed verification: ${failedServers.join(', ')}`)}`);
576
- console.error(chalk.yellow('Continuing with successfully compiled servers.'));
674
+ log(`\n${chalk.yellow(`Warning: ${failedServers.length} server(s) failed verification: ${failedServers.join(', ')}`)}`);
675
+ log(chalk.yellow('Continuing with successfully compiled servers.'));
577
676
  }
578
677
  if (results.length === 0) {
579
678
  throw new Error(`All ${failedServers.length} server(s) failed compilation: ${failedServers.join(', ')}. ` +
@@ -619,6 +718,7 @@ export class PipelineRunner {
619
718
  }
620
719
  const compilerSystem = this.cacheStrategy.wrapSystemPrompt(compilerPrompt);
621
720
  // Step 1: Compile rules for this server
721
+ config.signal?.throwIfAborted();
622
722
  logContext.stepName = `compile-${unit.serverName}`;
623
723
  const compileResult = await this.compileServerPolicyRules(unit, compilerSystem, inputHash, model, reporter);
624
724
  let { rules } = compileResult;
@@ -626,6 +726,7 @@ export class PipelineRunner {
626
726
  // Validate server scoping (debug assertion)
627
727
  validateServerScoping(unit.serverName, rules);
628
728
  // Resolve dynamic lists for this server (before scenario generation so lists are available)
729
+ config.signal?.throwIfAborted();
629
730
  let dynamicLists;
630
731
  if (listDefinitions.length > 0) {
631
732
  dynamicLists = await this.resolveServerLists(listDefinitions, serverOutputDir, config, ` ${unit.serverName}`, model, logContext, reporter);
@@ -652,12 +753,14 @@ export class PipelineRunner {
652
753
  ? extractServerDomainAllowlists({ [unit.serverName]: unit.mcpServerConfig })
653
754
  : undefined;
654
755
  // Step 2: Generate test scenarios for this server
756
+ config.signal?.throwIfAborted();
655
757
  logContext.stepName = `scenarios-${unit.serverName}`;
656
758
  const permittedDirectories = extractPermittedDirectories(rules);
657
759
  const scenarioPrompt = buildGeneratorSystemPrompt(unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, permittedDirectories, dynamicLists);
658
760
  const scenarioHash = computeScenariosHash(scenarioPrompt, unit.handwrittenScenarios);
659
761
  const scenarioResult = await this.generateTestScenarios(unit.constitutionText, unit.storedAnnotations, unit.allowedDirectory, unit.handwrittenScenarios, scenarioHash, existingServerScenarios, ` ${unit.serverName}`, permittedDirectories, dynamicLists, model, reporter);
660
762
  // Step 3: Verify and repair
763
+ config.signal?.throwIfAborted();
661
764
  const state = await this.verifyAndRepairServer(unit, config, constitutionHash, inputHash, serverPolicyFile, serverAnnotationsFile, serverDomainAllowlists, dynamicLists, rules, listDefinitions, compileResult.session, scenarioResult, permittedDirectories, compilerSystem, model, logContext, reporter, serverOutputDir, writeServerPolicy);
662
765
  // Sync repaired state back so the writeServerPolicy closure sees updated values
663
766
  rules = state.rules;
@@ -943,7 +1046,13 @@ export class PipelineRunner {
943
1046
  async resolveServerLists(listDefinitions, serverOutputDir, config, labelPrefix, model, logContext, reporter) {
944
1047
  logContext.stepName = `resolve-lists-${labelPrefix.trim()}`;
945
1048
  const existingLists = loadExistingArtifact(serverOutputDir, 'dynamic-lists.json');
946
- const needsMcp = listDefinitions.some((d) => d.requiresMcp);
1049
+ const mcpListNames = listDefinitions.filter((d) => d.requiresMcp).map((d) => d.name);
1050
+ const needsMcp = mcpListNames.length > 0;
1051
+ // The WS compile path disables live MCP. Fail fast with a typed error rather
1052
+ // than silently resolving MCP-backed lists to empty (which would deny).
1053
+ if (needsMcp && config.allowMcpLists === false) {
1054
+ throw new McpListsDisallowedError(mcpListNames);
1055
+ }
947
1056
  let proxy;
948
1057
  if (needsMcp && config.mcpServers) {
949
1058
  proxy = await connectViaProxy(listDefinitions, config.mcpServers, config.toolAnnotationsDir);