@provos/ironcurtain 0.9.1 → 0.11.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 (502) hide show
  1. package/README.md +69 -10
  2. package/dist/auth/oauth-provider.js +1 -4
  3. package/dist/auth/oauth-provider.js.map +1 -1
  4. package/dist/cli.js +40 -2
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config/config-command.js +174 -10
  7. package/dist/config/config-command.js.map +1 -1
  8. package/dist/config/first-start.js +61 -0
  9. package/dist/config/first-start.js.map +1 -1
  10. package/dist/config/generated/compiled-policy.json +528 -161
  11. package/dist/config/generated/test-scenarios.json +4810 -170
  12. package/dist/config/generated/tool-annotations.json +34 -47
  13. package/dist/config/index.d.ts +58 -0
  14. package/dist/config/index.js +97 -15
  15. package/dist/config/index.js.map +1 -1
  16. package/dist/config/mcp-servers.json +2 -2
  17. package/dist/config/model-provider.d.ts +8 -2
  18. package/dist/config/model-provider.js +52 -9
  19. package/dist/config/model-provider.js.map +1 -1
  20. package/dist/config/paths.d.ts +181 -0
  21. package/dist/config/paths.js +326 -23
  22. package/dist/config/paths.js.map +1 -1
  23. package/dist/config/types.d.ts +7 -1
  24. package/dist/config/user-config.d.ts +61 -4
  25. package/dist/config/user-config.js +108 -21
  26. package/dist/config/user-config.js.map +1 -1
  27. package/dist/config/validate-policy-dir.d.ts +10 -0
  28. package/dist/config/validate-policy-dir.js +59 -0
  29. package/dist/config/validate-policy-dir.js.map +1 -0
  30. package/dist/cron/compile-task-policy.js +1 -0
  31. package/dist/cron/compile-task-policy.js.map +1 -1
  32. package/dist/cron/job-commands.js +22 -2
  33. package/dist/cron/job-commands.js.map +1 -1
  34. package/dist/cron/types.d.ts +16 -2
  35. package/dist/cron/types.js +1 -3
  36. package/dist/cron/types.js.map +1 -1
  37. package/dist/daemon/daemon-command.js +16 -2
  38. package/dist/daemon/daemon-command.js.map +1 -1
  39. package/dist/daemon/ironcurtain-daemon.d.ts +44 -1
  40. package/dist/daemon/ironcurtain-daemon.js +210 -8
  41. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  42. package/dist/docker/adapters/claude-code.d.ts +2 -1
  43. package/dist/docker/adapters/claude-code.js +320 -131
  44. package/dist/docker/adapters/claude-code.js.map +1 -1
  45. package/dist/docker/adapters/goose.d.ts +12 -11
  46. package/dist/docker/adapters/goose.js +80 -27
  47. package/dist/docker/adapters/goose.js.map +1 -1
  48. package/dist/docker/agent-adapter.d.ts +178 -4
  49. package/dist/docker/agent-adapter.js +14 -0
  50. package/dist/docker/agent-adapter.js.map +1 -1
  51. package/dist/docker/agent-registry.js +3 -2
  52. package/dist/docker/agent-registry.js.map +1 -1
  53. package/dist/docker/code-mode-proxy.d.ts +27 -0
  54. package/dist/docker/code-mode-proxy.js +3 -0
  55. package/dist/docker/code-mode-proxy.js.map +1 -1
  56. package/dist/docker/container-lifecycle.d.ts +15 -0
  57. package/dist/docker/container-lifecycle.js +32 -0
  58. package/dist/docker/container-lifecycle.js.map +1 -0
  59. package/dist/docker/docker-agent-session.d.ts +113 -62
  60. package/dist/docker/docker-agent-session.js +241 -300
  61. package/dist/docker/docker-agent-session.js.map +1 -1
  62. package/dist/docker/docker-infrastructure.d.ts +256 -21
  63. package/dist/docker/docker-infrastructure.js +585 -30
  64. package/dist/docker/docker-infrastructure.js.map +1 -1
  65. package/dist/docker/docker-manager.d.ts +36 -1
  66. package/dist/docker/docker-manager.js +168 -26
  67. package/dist/docker/docker-manager.js.map +1 -1
  68. package/dist/docker/docker-progress-sink.d.ts +43 -0
  69. package/dist/docker/docker-progress-sink.js +299 -0
  70. package/dist/docker/docker-progress-sink.js.map +1 -0
  71. package/dist/docker/mitm-proxy.d.ts +90 -1
  72. package/dist/docker/mitm-proxy.js +720 -40
  73. package/dist/docker/mitm-proxy.js.map +1 -1
  74. package/dist/docker/oauth-credentials.d.ts +46 -3
  75. package/dist/docker/oauth-credentials.js +56 -14
  76. package/dist/docker/oauth-credentials.js.map +1 -1
  77. package/dist/docker/oauth-token-manager.js +2 -2
  78. package/dist/docker/oauth-token-manager.js.map +1 -1
  79. package/dist/docker/orientation.d.ts +1 -1
  80. package/dist/docker/provider-config.d.ts +81 -2
  81. package/dist/docker/provider-config.js +222 -28
  82. package/dist/docker/provider-config.js.map +1 -1
  83. package/dist/docker/proxy-tools.d.ts +0 -2
  84. package/dist/docker/proxy-tools.js +2 -7
  85. package/dist/docker/proxy-tools.js.map +1 -1
  86. package/dist/docker/pty-session.d.ts +49 -1
  87. package/dist/docker/pty-session.js +212 -90
  88. package/dist/docker/pty-session.js.map +1 -1
  89. package/dist/docker/pty-types.d.ts +0 -2
  90. package/dist/docker/pty-types.js +0 -2
  91. package/dist/docker/pty-types.js.map +1 -1
  92. package/dist/docker/resource-limits.d.ts +125 -0
  93. package/dist/docker/resource-limits.js +200 -0
  94. package/dist/docker/resource-limits.js.map +1 -0
  95. package/dist/docker/spawn-with-idle-timeout.d.ts +39 -0
  96. package/dist/docker/spawn-with-idle-timeout.js +152 -0
  97. package/dist/docker/spawn-with-idle-timeout.js.map +1 -0
  98. package/dist/docker/sse-extractor.d.ts +51 -0
  99. package/dist/docker/sse-extractor.js +303 -0
  100. package/dist/docker/sse-extractor.js.map +1 -0
  101. package/dist/docker/token-stream-bus.d.ts +83 -0
  102. package/dist/docker/token-stream-bus.js +98 -0
  103. package/dist/docker/token-stream-bus.js.map +1 -0
  104. package/dist/docker/token-stream-types.d.ts +71 -0
  105. package/dist/docker/token-stream-types.js +8 -0
  106. package/dist/docker/token-stream-types.js.map +1 -0
  107. package/dist/docker/types.d.ts +61 -6
  108. package/dist/doctor/checks.d.ts +138 -0
  109. package/dist/doctor/checks.js +511 -0
  110. package/dist/doctor/checks.js.map +1 -0
  111. package/dist/doctor/doctor-command.d.ts +29 -0
  112. package/dist/doctor/doctor-command.js +160 -0
  113. package/dist/doctor/doctor-command.js.map +1 -0
  114. package/dist/doctor/mcp-liveness.d.ts +28 -0
  115. package/dist/doctor/mcp-liveness.js +84 -0
  116. package/dist/doctor/mcp-liveness.js.map +1 -0
  117. package/dist/doctor/oauth-checks.d.ts +23 -0
  118. package/dist/doctor/oauth-checks.js +157 -0
  119. package/dist/doctor/oauth-checks.js.map +1 -0
  120. package/dist/doctor/output.d.ts +26 -0
  121. package/dist/doctor/output.js +82 -0
  122. package/dist/doctor/output.js.map +1 -0
  123. package/dist/escalation/listener-command.js +0 -3
  124. package/dist/escalation/listener-command.js.map +1 -1
  125. package/dist/event-bus/typed-event-bus.d.ts +26 -0
  126. package/dist/event-bus/typed-event-bus.js +35 -0
  127. package/dist/event-bus/typed-event-bus.js.map +1 -0
  128. package/dist/index.js +32 -9
  129. package/dist/index.js.map +1 -1
  130. package/dist/logger.js +31 -2
  131. package/dist/logger.js.map +1 -1
  132. package/dist/memory/auto-save.d.ts +18 -4
  133. package/dist/memory/auto-save.js +46 -6
  134. package/dist/memory/auto-save.js.map +1 -1
  135. package/dist/memory/memory-policy.d.ts +41 -0
  136. package/dist/memory/memory-policy.js +36 -0
  137. package/dist/memory/memory-policy.js.map +1 -0
  138. package/dist/mux/mux-app.d.ts +2 -0
  139. package/dist/mux/mux-app.js +30 -34
  140. package/dist/mux/mux-app.js.map +1 -1
  141. package/dist/mux/mux-command.d.ts +10 -1
  142. package/dist/mux/mux-command.js +62 -35
  143. package/dist/mux/mux-command.js.map +1 -1
  144. package/dist/mux/mux-renderer.js +22 -17
  145. package/dist/mux/mux-renderer.js.map +1 -1
  146. package/dist/mux/pty-bridge.d.ts +2 -0
  147. package/dist/mux/pty-bridge.js +3 -0
  148. package/dist/mux/pty-bridge.js.map +1 -1
  149. package/dist/mux/session-scanner.d.ts +7 -0
  150. package/dist/mux/session-scanner.js +21 -2
  151. package/dist/mux/session-scanner.js.map +1 -1
  152. package/dist/mux/types.d.ts +0 -4
  153. package/dist/mux/types.js.map +1 -1
  154. package/dist/{pipeline → observability}/llm-logger.d.ts +9 -3
  155. package/dist/{pipeline → observability}/llm-logger.js +16 -4
  156. package/dist/observability/llm-logger.js.map +1 -0
  157. package/dist/observe/observe-command.d.ts +12 -0
  158. package/dist/observe/observe-command.js +298 -0
  159. package/dist/observe/observe-command.js.map +1 -0
  160. package/dist/observe/observe-renderer.d.ts +24 -0
  161. package/dist/observe/observe-renderer.js +87 -0
  162. package/dist/observe/observe-renderer.js.map +1 -0
  163. package/dist/observe/observe-tui-rain.d.ts +42 -0
  164. package/dist/observe/observe-tui-rain.js +531 -0
  165. package/dist/observe/observe-tui-rain.js.map +1 -0
  166. package/dist/observe/observe-tui-text-panel.d.ts +46 -0
  167. package/dist/observe/observe-tui-text-panel.js +525 -0
  168. package/dist/observe/observe-tui-text-panel.js.map +1 -0
  169. package/dist/observe/observe-tui-types.d.ts +270 -0
  170. package/dist/observe/observe-tui-types.js +200 -0
  171. package/dist/observe/observe-tui-types.js.map +1 -0
  172. package/dist/observe/observe-tui-word-scorer.d.ts +143 -0
  173. package/dist/observe/observe-tui-word-scorer.js +572 -0
  174. package/dist/observe/observe-tui-word-scorer.js.map +1 -0
  175. package/dist/observe/observe-tui.d.ts +59 -0
  176. package/dist/observe/observe-tui.js +592 -0
  177. package/dist/observe/observe-tui.js.map +1 -0
  178. package/dist/persona/compile-persona-policy.js +1 -0
  179. package/dist/persona/compile-persona-policy.js.map +1 -1
  180. package/dist/persona/memory-gate.d.ts +19 -0
  181. package/dist/persona/memory-gate.js +33 -0
  182. package/dist/persona/memory-gate.js.map +1 -0
  183. package/dist/persona/persona-command.js +41 -6
  184. package/dist/persona/persona-command.js.map +1 -1
  185. package/dist/persona/resolve.d.ts +22 -0
  186. package/dist/persona/resolve.js +45 -2
  187. package/dist/persona/resolve.js.map +1 -1
  188. package/dist/persona/types.d.ts +16 -0
  189. package/dist/pipeline/annotate.d.ts +7 -1
  190. package/dist/pipeline/annotate.js +92 -2
  191. package/dist/pipeline/annotate.js.map +1 -1
  192. package/dist/pipeline/compile.js +2 -0
  193. package/dist/pipeline/compile.js.map +1 -1
  194. package/dist/pipeline/constitution-compiler.d.ts +25 -14
  195. package/dist/pipeline/constitution-compiler.js +107 -38
  196. package/dist/pipeline/constitution-compiler.js.map +1 -1
  197. package/dist/pipeline/dynamic-list-types.d.ts +3 -6
  198. package/dist/pipeline/dynamic-list-types.js +3 -16
  199. package/dist/pipeline/dynamic-list-types.js.map +1 -1
  200. package/dist/pipeline/parallel-progress.d.ts +59 -0
  201. package/dist/pipeline/parallel-progress.js +263 -0
  202. package/dist/pipeline/parallel-progress.js.map +1 -0
  203. package/dist/pipeline/pipeline-runner.d.ts +49 -8
  204. package/dist/pipeline/pipeline-runner.js +419 -231
  205. package/dist/pipeline/pipeline-runner.js.map +1 -1
  206. package/dist/pipeline/pipeline-shared.d.ts +59 -1
  207. package/dist/pipeline/pipeline-shared.js +127 -12
  208. package/dist/pipeline/pipeline-shared.js.map +1 -1
  209. package/dist/pipeline/policy-verifier.js +16 -3
  210. package/dist/pipeline/policy-verifier.js.map +1 -1
  211. package/dist/pipeline/proxy-mcp-connections.js +17 -2
  212. package/dist/pipeline/proxy-mcp-connections.js.map +1 -1
  213. package/dist/pipeline/scenario-generator.d.ts +4 -8
  214. package/dist/pipeline/scenario-generator.js +13 -46
  215. package/dist/pipeline/scenario-generator.js.map +1 -1
  216. package/dist/pipeline/server-prefilter.d.ts +31 -0
  217. package/dist/pipeline/server-prefilter.js +115 -0
  218. package/dist/pipeline/server-prefilter.js.map +1 -0
  219. package/dist/pipeline/types.d.ts +5 -0
  220. package/dist/sandbox/index.d.ts +14 -0
  221. package/dist/sandbox/index.js +335 -116
  222. package/dist/sandbox/index.js.map +1 -1
  223. package/dist/sandbox/ironcurtain-protocol.d.ts +52 -0
  224. package/dist/sandbox/ironcurtain-protocol.js +185 -0
  225. package/dist/sandbox/ironcurtain-protocol.js.map +1 -0
  226. package/dist/session/agent-session.d.ts +6 -0
  227. package/dist/session/agent-session.js +16 -5
  228. package/dist/session/agent-session.js.map +1 -1
  229. package/dist/session/cli-transport.js +0 -6
  230. package/dist/session/cli-transport.js.map +1 -1
  231. package/dist/session/index.d.ts +35 -14
  232. package/dist/session/index.js +324 -130
  233. package/dist/session/index.js.map +1 -1
  234. package/dist/session/preflight.d.ts +50 -13
  235. package/dist/session/preflight.js +252 -80
  236. package/dist/session/preflight.js.map +1 -1
  237. package/dist/session/prompts.d.ts +2 -5
  238. package/dist/session/prompts.js.map +1 -1
  239. package/dist/session/session-manager.d.ts +61 -6
  240. package/dist/session/session-manager.js +72 -10
  241. package/dist/session/session-manager.js.map +1 -1
  242. package/dist/session/session-metadata.d.ts +21 -5
  243. package/dist/session/session-metadata.js +34 -12
  244. package/dist/session/session-metadata.js.map +1 -1
  245. package/dist/session/types.d.ts +311 -5
  246. package/dist/session/types.js +57 -1
  247. package/dist/session/types.js.map +1 -1
  248. package/dist/signal/setup-signal.js +13 -21
  249. package/dist/signal/setup-signal.js.map +1 -1
  250. package/dist/signal/signal-bot-daemon.d.ts +16 -1
  251. package/dist/signal/signal-bot-daemon.js +39 -27
  252. package/dist/signal/signal-bot-daemon.js.map +1 -1
  253. package/dist/signal/signal-container.js +7 -1
  254. package/dist/signal/signal-container.js.map +1 -1
  255. package/dist/skills/discovery.d.ts +77 -0
  256. package/dist/skills/discovery.js +206 -0
  257. package/dist/skills/discovery.js.map +1 -0
  258. package/dist/skills/index.d.ts +4 -0
  259. package/dist/skills/index.js +3 -0
  260. package/dist/skills/index.js.map +1 -0
  261. package/dist/skills/staging.d.ts +28 -0
  262. package/dist/skills/staging.js +68 -0
  263. package/dist/skills/staging.js.map +1 -0
  264. package/dist/skills/types.d.ts +46 -0
  265. package/dist/skills/types.js +2 -0
  266. package/dist/skills/types.js.map +1 -0
  267. package/dist/trusted-process/approval-whitelist.d.ts +0 -2
  268. package/dist/trusted-process/approval-whitelist.js +17 -17
  269. package/dist/trusted-process/approval-whitelist.js.map +1 -1
  270. package/dist/trusted-process/async-mutex.d.ts +32 -0
  271. package/dist/trusted-process/async-mutex.js +50 -0
  272. package/dist/trusted-process/async-mutex.js.map +1 -0
  273. package/dist/trusted-process/audit-log.d.ts +38 -1
  274. package/dist/trusted-process/audit-log.js +82 -13
  275. package/dist/trusted-process/audit-log.js.map +1 -1
  276. package/dist/trusted-process/call-circuit-breaker.js +2 -1
  277. package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
  278. package/dist/trusted-process/control-server.d.ts +101 -0
  279. package/dist/trusted-process/control-server.js +282 -0
  280. package/dist/trusted-process/control-server.js.map +1 -0
  281. package/dist/trusted-process/domain-utils.d.ts +6 -6
  282. package/dist/trusted-process/domain-utils.js +7 -23
  283. package/dist/trusted-process/domain-utils.js.map +1 -1
  284. package/dist/trusted-process/error-prefixes.d.ts +16 -0
  285. package/dist/trusted-process/error-prefixes.js +17 -0
  286. package/dist/trusted-process/error-prefixes.js.map +1 -0
  287. package/dist/trusted-process/index.d.ts +40 -23
  288. package/dist/trusted-process/index.js +131 -225
  289. package/dist/trusted-process/index.js.map +1 -1
  290. package/dist/trusted-process/list-matcher.d.ts +17 -0
  291. package/dist/trusted-process/list-matcher.js +31 -0
  292. package/dist/trusted-process/list-matcher.js.map +1 -0
  293. package/dist/trusted-process/mcp-client-manager.d.ts +35 -6
  294. package/dist/trusted-process/mcp-client-manager.js +40 -45
  295. package/dist/trusted-process/mcp-client-manager.js.map +1 -1
  296. package/dist/trusted-process/mcp-proxy-server.d.ts +31 -120
  297. package/dist/trusted-process/mcp-proxy-server.js +166 -657
  298. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  299. package/dist/trusted-process/policy-engine.d.ts +16 -4
  300. package/dist/trusted-process/policy-engine.js +90 -45
  301. package/dist/trusted-process/policy-engine.js.map +1 -1
  302. package/dist/trusted-process/policy-roots.d.ts +6 -0
  303. package/dist/trusted-process/policy-roots.js +20 -0
  304. package/dist/trusted-process/policy-roots.js.map +1 -1
  305. package/dist/trusted-process/sandbox-integration.d.ts +12 -0
  306. package/dist/trusted-process/sandbox-integration.js +26 -0
  307. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  308. package/dist/trusted-process/tool-call-coordinator.d.ts +269 -0
  309. package/dist/trusted-process/tool-call-coordinator.js +508 -0
  310. package/dist/trusted-process/tool-call-coordinator.js.map +1 -0
  311. package/dist/trusted-process/tool-call-pipeline.d.ts +205 -0
  312. package/dist/trusted-process/tool-call-pipeline.js +798 -0
  313. package/dist/trusted-process/tool-call-pipeline.js.map +1 -0
  314. package/dist/trusted-process/tool-description-hints.d.ts +1 -1
  315. package/dist/types/argument-roles.d.ts +1 -1
  316. package/dist/types/argument-roles.js +14 -1
  317. package/dist/types/argument-roles.js.map +1 -1
  318. package/dist/types/audit.d.ts +9 -0
  319. package/dist/{session → types}/errors.d.ts +8 -2
  320. package/dist/{session → types}/errors.js +8 -2
  321. package/dist/types/errors.js.map +1 -0
  322. package/dist/types/reserved-servers.d.ts +11 -0
  323. package/dist/types/reserved-servers.js +12 -0
  324. package/dist/types/reserved-servers.js.map +1 -0
  325. package/dist/types/server-listing.d.ts +14 -0
  326. package/dist/types/server-listing.js +2 -0
  327. package/dist/types/server-listing.js.map +1 -0
  328. package/dist/types/slug.d.ts +2 -0
  329. package/dist/types/slug.js +2 -0
  330. package/dist/types/slug.js.map +1 -1
  331. package/dist/types/url-normalize.d.ts +16 -0
  332. package/dist/types/url-normalize.js +33 -0
  333. package/dist/types/url-normalize.js.map +1 -0
  334. package/dist/utils/error-message.d.ts +8 -0
  335. package/dist/utils/error-message.js +11 -0
  336. package/dist/utils/error-message.js.map +1 -0
  337. package/dist/utils/exec-error.d.ts +19 -0
  338. package/dist/utils/exec-error.js +16 -0
  339. package/dist/utils/exec-error.js.map +1 -0
  340. package/dist/utils/is-plain-object.d.ts +7 -0
  341. package/dist/utils/is-plain-object.js +12 -0
  342. package/dist/utils/is-plain-object.js.map +1 -0
  343. package/dist/utils/preflight-checks.d.ts +16 -0
  344. package/dist/utils/preflight-checks.js +178 -0
  345. package/dist/utils/preflight-checks.js.map +1 -0
  346. package/dist/web-ui/__tests__/json-rpc-dispatch.test.d.ts +7 -0
  347. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +725 -0
  348. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -0
  349. package/dist/web-ui/dispatch/escalation-dispatch.d.ts +7 -0
  350. package/dist/web-ui/dispatch/escalation-dispatch.js +52 -0
  351. package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -0
  352. package/dist/web-ui/dispatch/job-dispatch.d.ts +8 -0
  353. package/dist/web-ui/dispatch/job-dispatch.js +83 -0
  354. package/dist/web-ui/dispatch/job-dispatch.js.map +1 -0
  355. package/dist/web-ui/dispatch/persona-dispatch.d.ts +6 -0
  356. package/dist/web-ui/dispatch/persona-dispatch.js +116 -0
  357. package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -0
  358. package/dist/web-ui/dispatch/session-dispatch.d.ts +9 -0
  359. package/dist/web-ui/dispatch/session-dispatch.js +201 -0
  360. package/dist/web-ui/dispatch/session-dispatch.js.map +1 -0
  361. package/dist/web-ui/dispatch/token-stream-dispatch.d.ts +15 -0
  362. package/dist/web-ui/dispatch/token-stream-dispatch.js +55 -0
  363. package/dist/web-ui/dispatch/token-stream-dispatch.js.map +1 -0
  364. package/dist/web-ui/dispatch/types.d.ts +31 -0
  365. package/dist/web-ui/dispatch/types.js +68 -0
  366. package/dist/web-ui/dispatch/types.js.map +1 -0
  367. package/dist/web-ui/dispatch/workflow-dispatch.d.ts +103 -0
  368. package/dist/web-ui/dispatch/workflow-dispatch.js +925 -0
  369. package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -0
  370. package/dist/web-ui/json-rpc-dispatch.d.ts +12 -0
  371. package/dist/web-ui/json-rpc-dispatch.js +34 -0
  372. package/dist/web-ui/json-rpc-dispatch.js.map +1 -0
  373. package/dist/web-ui/state-graph.d.ts +13 -0
  374. package/dist/web-ui/state-graph.js +82 -0
  375. package/dist/web-ui/state-graph.js.map +1 -0
  376. package/dist/web-ui/token-stream-bridge.d.ts +76 -0
  377. package/dist/web-ui/token-stream-bridge.js +265 -0
  378. package/dist/web-ui/token-stream-bridge.js.map +1 -0
  379. package/dist/web-ui/web-event-bus.d.ts +152 -0
  380. package/dist/web-ui/web-event-bus.js +19 -0
  381. package/dist/web-ui/web-event-bus.js.map +1 -0
  382. package/dist/web-ui/web-session-transport.d.ts +31 -0
  383. package/dist/web-ui/web-session-transport.js +109 -0
  384. package/dist/web-ui/web-session-transport.js.map +1 -0
  385. package/dist/web-ui/web-ui-server.d.ts +76 -0
  386. package/dist/web-ui/web-ui-server.js +461 -0
  387. package/dist/web-ui/web-ui-server.js.map +1 -0
  388. package/dist/web-ui/web-ui-types.d.ts +290 -0
  389. package/dist/web-ui/web-ui-types.js +32 -0
  390. package/dist/web-ui/web-ui-types.js.map +1 -0
  391. package/dist/web-ui/ws-utils.d.ts +5 -0
  392. package/dist/web-ui/ws-utils.js +12 -0
  393. package/dist/web-ui/ws-utils.js.map +1 -0
  394. package/dist/web-ui-static/assets/index-BFO5z1js.js +76 -0
  395. package/dist/web-ui-static/assets/index-CCUwrkIL.css +1 -0
  396. package/dist/web-ui-static/assets/orbitron-latin-700-normal-4jsRXGGJ.woff2 +0 -0
  397. package/dist/web-ui-static/assets/orbitron-latin-700-normal-DmYOh-W9.woff +0 -0
  398. package/dist/web-ui-static/index.html +14 -0
  399. package/dist/workflow/artifacts.d.ts +59 -0
  400. package/dist/workflow/artifacts.js +188 -0
  401. package/dist/workflow/artifacts.js.map +1 -0
  402. package/dist/workflow/checkpoint-selection.d.ts +34 -0
  403. package/dist/workflow/checkpoint-selection.js +48 -0
  404. package/dist/workflow/checkpoint-selection.js.map +1 -0
  405. package/dist/workflow/checkpoint.d.ts +35 -0
  406. package/dist/workflow/checkpoint.js +84 -0
  407. package/dist/workflow/checkpoint.js.map +1 -0
  408. package/dist/workflow/cli-support.d.ts +92 -0
  409. package/dist/workflow/cli-support.js +423 -0
  410. package/dist/workflow/cli-support.js.map +1 -0
  411. package/dist/workflow/definition-loader.d.ts +32 -0
  412. package/dist/workflow/definition-loader.js +56 -0
  413. package/dist/workflow/definition-loader.js.map +1 -0
  414. package/dist/workflow/discovery.d.ts +78 -0
  415. package/dist/workflow/discovery.js +203 -0
  416. package/dist/workflow/discovery.js.map +1 -0
  417. package/dist/workflow/errors.d.ts +66 -0
  418. package/dist/workflow/errors.js +49 -0
  419. package/dist/workflow/errors.js.map +1 -0
  420. package/dist/workflow/guards.d.ts +8 -0
  421. package/dist/workflow/guards.js +38 -0
  422. package/dist/workflow/guards.js.map +1 -0
  423. package/dist/workflow/lint-integration.d.ts +64 -0
  424. package/dist/workflow/lint-integration.js +56 -0
  425. package/dist/workflow/lint-integration.js.map +1 -0
  426. package/dist/workflow/lint.d.ts +66 -0
  427. package/dist/workflow/lint.js +410 -0
  428. package/dist/workflow/lint.js.map +1 -0
  429. package/dist/workflow/machine-builder.d.ts +90 -0
  430. package/dist/workflow/machine-builder.js +485 -0
  431. package/dist/workflow/machine-builder.js.map +1 -0
  432. package/dist/workflow/message-log.d.ts +85 -0
  433. package/dist/workflow/message-log.js +40 -0
  434. package/dist/workflow/message-log.js.map +1 -0
  435. package/dist/workflow/orchestrator.d.ts +450 -0
  436. package/dist/workflow/orchestrator.js +1941 -0
  437. package/dist/workflow/orchestrator.js.map +1 -0
  438. package/dist/workflow/prompt-builder.d.ts +39 -0
  439. package/dist/workflow/prompt-builder.js +242 -0
  440. package/dist/workflow/prompt-builder.js.map +1 -0
  441. package/dist/workflow/run-state-command.d.ts +9 -0
  442. package/dist/workflow/run-state-command.js +414 -0
  443. package/dist/workflow/run-state-command.js.map +1 -0
  444. package/dist/workflow/run-state-debug-capture.d.ts +40 -0
  445. package/dist/workflow/run-state-debug-capture.js +101 -0
  446. package/dist/workflow/run-state-debug-capture.js.map +1 -0
  447. package/dist/workflow/status-parser.d.ts +68 -0
  448. package/dist/workflow/status-parser.js +259 -0
  449. package/dist/workflow/status-parser.js.map +1 -0
  450. package/dist/workflow/types.d.ts +498 -0
  451. package/dist/workflow/types.js +83 -0
  452. package/dist/workflow/types.js.map +1 -0
  453. package/dist/workflow/validate.d.ts +58 -0
  454. package/dist/workflow/validate.js +499 -0
  455. package/dist/workflow/validate.js.map +1 -0
  456. package/dist/workflow/workflow-command.d.ts +26 -0
  457. package/dist/workflow/workflow-command.js +648 -0
  458. package/dist/workflow/workflow-command.js.map +1 -0
  459. package/dist/workflow/workflow-discovery.d.ts +25 -0
  460. package/dist/workflow/workflow-discovery.js +100 -0
  461. package/dist/workflow/workflow-discovery.js.map +1 -0
  462. package/dist/workflow/workflow-manager.d.ts +122 -0
  463. package/dist/workflow/workflow-manager.js +308 -0
  464. package/dist/workflow/workflow-manager.js.map +1 -0
  465. package/dist/workflow/workflows/design-and-code/workflow.yaml +169 -0
  466. package/dist/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  467. package/dist/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  468. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  469. package/dist/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  470. package/dist/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  471. package/dist/workflow/workflows/test-email-summary/workflow.yaml +89 -0
  472. package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
  473. package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
  474. package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
  475. package/dist/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
  476. package/dist/workflow/worktree.d.ts +43 -0
  477. package/dist/workflow/worktree.js +106 -0
  478. package/dist/workflow/worktree.js.map +1 -0
  479. package/docker/Dockerfile.base +12 -1
  480. package/docker/Dockerfile.base.arm64 +17 -1
  481. package/docker/entrypoint-claude-code.sh +46 -0
  482. package/docker/entrypoint-goose.sh +25 -0
  483. package/package.json +22 -8
  484. package/packages/web-ui/README.md +113 -0
  485. package/packages/web-ui/docs/workflow-state-machine.png +0 -0
  486. package/src/config/generated/compiled-policy.json +528 -161
  487. package/src/config/generated/test-scenarios.json +4810 -170
  488. package/src/config/generated/tool-annotations.json +34 -47
  489. package/src/config/mcp-servers.json +2 -2
  490. package/src/workflow/workflows/design-and-code/workflow.yaml +169 -0
  491. package/src/workflow/workflows/test-email-summary/skills/email-formatting/SKILL.md +55 -0
  492. package/src/workflow/workflows/test-email-summary/skills/gmail-query-syntax/SKILL.md +63 -0
  493. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/SKILL.md +41 -0
  494. package/src/workflow/workflows/test-email-summary/skills/json-schema-emails/schema.json +34 -0
  495. package/src/workflow/workflows/test-email-summary/skills/summary-style-guide/SKILL.md +51 -0
  496. package/src/workflow/workflows/test-email-summary/workflow.yaml +89 -0
  497. package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +201 -0
  498. package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +166 -0
  499. package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +185 -0
  500. package/src/workflow/workflows/vuln-discovery/workflow.yaml +920 -0
  501. package/dist/pipeline/llm-logger.js.map +0 -1
  502. package/dist/session/errors.js.map +0 -1
@@ -7,34 +7,24 @@
7
7
  */
8
8
  import { existsSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
9
9
  import { dirname, resolve } from 'node:path';
10
- import { loadConfig } from '../config/index.js';
11
- import { getIronCurtainHome, getPackageConfigDir, getSessionDir, getSessionSandboxDir, getSessionEscalationDir, getSessionAuditLogPath, getSessionLogPath, getSessionLlmLogPath, getSessionAutoApproveLlmLogPath, } from '../config/paths.js';
10
+ import { loadConfig, applyAllowedDirectoryToMcpArgs, loadGeneratedPolicy, getPackageGeneratedDir, } from '../config/index.js';
11
+ import { extractRequiredServers } from '../trusted-process/policy-roots.js';
12
+ import { getSessionDir, getSessionSandboxDir, getSessionEscalationDir, getSessionAuditLogPath, getSessionLogPath, getSessionLlmLogPath, getSessionAutoApproveLlmLogPath, SESSION_LOG_FILENAME, SESSION_METADATA_FILENAME, } from '../config/paths.js';
13
+ import { validatePolicyDir as sharedValidatePolicyDir } from '../config/validate-policy-dir.js';
12
14
  import * as logger from '../logger.js';
13
- import { resolveRealPath } from '../types/argument-roles.js';
14
- import { resolvePersona, applyServerAllowlist } from '../persona/resolve.js';
15
+ import { resolvePersona, applyServerAllowlist, filterMcpServersByPolicy } from '../persona/resolve.js';
16
+ import { resolveSkillsForSession } from '../skills/discovery.js';
15
17
  import { buildPersonaSystemPromptAugmentation } from '../persona/persona-prompt.js';
16
18
  import { resolveMemoryDbPath } from '../memory/resolve-memory-path.js';
17
19
  import { buildMemoryServerConfig, MEMORY_SERVER_NAME } from '../memory/memory-annotations.js';
18
20
  import { buildMemorySystemPrompt, adaptMemoryToolNames } from '../memory/memory-prompt.js';
21
+ import { isMemoryEnabledFor } from '../memory/memory-policy.js';
22
+ import { createJobId } from '../cron/types.js';
23
+ import { loadJob } from '../cron/job-store.js';
19
24
  import { AgentSession } from './agent-session.js';
20
- import { SessionError } from './errors.js';
21
- import { saveSessionMetadata, loadSessionMetadata } from './session-metadata.js';
22
- import { isEqualOrInside } from './workspace-validation.js';
23
- import { createSessionId } from './types.js';
24
- /**
25
- * Creates and initializes a new session.
26
- *
27
- * This is the only public entry point for session creation.
28
- * The concrete implementations are not exported -- callers
29
- * depend on the Session interface only.
30
- *
31
- * When mode is 'docker', spawns an external agent in a Docker
32
- * container with MCP proxy mediation. Otherwise (default), creates
33
- * the built-in AgentSession using UTCP Code Mode + AI SDK.
34
- *
35
- * @throws {SessionError} with code SESSION_INIT_FAILED if
36
- * sandbox or MCP connection setup fails.
37
- */
25
+ import { SessionError } from '../types/errors.js';
26
+ import { saveSessionMetadata, saveSessionMetadataTo, loadSessionMetadata } from './session-metadata.js';
27
+ import { bundleIdFromSessionId, createAgentConversationId, createSessionId } from './types.js';
38
28
  export async function createSession(options = {}) {
39
29
  // When resuming, restore persisted session settings (persona, workspace, etc.)
40
30
  const effectiveOptions = applyResumeMetadata(options);
@@ -44,6 +34,39 @@ export async function createSession(options = {}) {
44
34
  }
45
35
  return createBuiltinSession(effectiveOptions);
46
36
  }
37
+ /**
38
+ * Entry point for callers outside the workflow orchestrator — CLI,
39
+ * daemon, signal bot, web UI. For Docker mode: resumes the persisted
40
+ * `AgentConversationId` when `resumeSessionId` is set (so
41
+ * `ironcurtain --resume <id>` continues the prior Claude conversation
42
+ * via `--resume <conv-id>` on the adapter), otherwise mints fresh.
43
+ * Builtin mode ignores the field.
44
+ *
45
+ * Legacy-session fallback: sessions created before
46
+ * `agentConversationId` was persisted in metadata will have no stored
47
+ * id. Those resumes mint fresh, and the agent CLI starts a new
48
+ * conversation — the prior transcript is unreachable but nothing is
49
+ * corrupted. New sessions created post-refactor always persist the id,
50
+ * so subsequent resumes recover the conversation.
51
+ *
52
+ * Workflow callers must not use this: they must thread
53
+ * `agentConversationId` from `context.agentConversationsByState` via
54
+ * `createSession()` directly so `freshSession: false` states resume
55
+ * the prior conversation.
56
+ */
57
+ export async function createStandaloneSession(options) {
58
+ if (options.mode?.kind === 'docker') {
59
+ const persistedId = options.resumeSessionId
60
+ ? loadSessionMetadata(options.resumeSessionId)?.agentConversationId
61
+ : undefined;
62
+ return createSession({
63
+ ...options,
64
+ mode: options.mode,
65
+ agentConversationId: persistedId ?? createAgentConversationId(),
66
+ });
67
+ }
68
+ return createSession({ ...options, mode: options.mode });
69
+ }
47
70
  /**
48
71
  * Merges persisted session metadata into options when resuming.
49
72
  * Returns options unchanged for new sessions or when no metadata exists
@@ -63,6 +86,14 @@ function applyResumeMetadata(options) {
63
86
  ...(metadata.workspacePath !== undefined ? { workspacePath: metadata.workspacePath } : {}),
64
87
  ...(metadata.policyDir !== undefined ? { policyDir: metadata.policyDir } : {}),
65
88
  ...(metadata.disableAutoApprove !== undefined ? { disableAutoApprove: metadata.disableAutoApprove } : {}),
89
+ // Caller-supplied `agentConversationId` wins over the persisted
90
+ // one (workflow orchestrator always passes its own explicitly).
91
+ // Only the CLI / daemon `createStandaloneSession` path leaves the
92
+ // field unset, and that's exactly the path we want to restore from
93
+ // metadata so `--resume` continues the prior Claude conversation.
94
+ ...(options.agentConversationId === undefined && metadata.agentConversationId !== undefined
95
+ ? { agentConversationId: metadata.agentConversationId }
96
+ : {}),
66
97
  };
67
98
  }
68
99
  /**
@@ -92,7 +123,8 @@ async function createBuiltinSession(options) {
92
123
  await session.initialize();
93
124
  }
94
125
  catch (error) {
95
- // Clean up on init failure
126
+ // Clean up on init failure. Teardown logger so error messages from
127
+ // callers (orchestrator, XState) go to the terminal, not the log file.
96
128
  await session.close().catch(() => { });
97
129
  if (!loggerWasActive)
98
130
  logger.teardown();
@@ -103,96 +135,178 @@ async function createBuiltinSession(options) {
103
135
  /**
104
136
  * Creates a DockerAgentSession that runs an external agent in a container.
105
137
  *
106
- * Uses prepareDockerInfrastructure() for shared Docker setup (proxies,
107
- * orientation, image resolution), then creates the session with pre-built
108
- * infrastructure so initialize() only does container creation + watchers.
138
+ * Two paths:
139
+ * - Standalone (default): calls `createDockerInfrastructure()` to stand up
140
+ * the full infrastructure bundle (proxies, orientation, image, running
141
+ * agent container, sidecar and internal network for TCP mode). The
142
+ * session is constructed with `ownsInfra=true`, so `close()` tears down
143
+ * the bundle.
144
+ * - Borrow (`options.workflow.infrastructure` set): uses the caller-supplied
145
+ * bundle as-is and constructs the session with `ownsInfra=false`. The
146
+ * caller retains full responsibility for the bundle's lifetime; the
147
+ * session's `close()` only tears down session-local state.
148
+ *
149
+ * In both paths the session wires up escalation watcher and audit tailer
150
+ * and writes any per-session files (CLAUDE.md, effective system prompt)
151
+ * using fields on the bundle.
109
152
  */
110
153
  async function createDockerSession(agentId, options) {
111
154
  const config = options.config ?? loadConfig();
112
155
  const sessionId = createSessionId();
113
156
  const effectiveSessionId = options.resumeSessionId ?? sessionId;
157
+ // Agent-CLI conversation id must be minted by the caller. Docker-mode
158
+ // callers pass `DockerSessionOptions`, which makes this field required
159
+ // at the type level. The runtime guard catches callers that widen to
160
+ // the base `SessionOptions` type and forget to supply it — the factory
161
+ // will not mint on their behalf because the single-conversation model
162
+ // makes the minting site load-bearing (workflow orchestrator reuses
163
+ // per state; each CLI run mints fresh). See §11 step 4 and §3
164
+ // "Identity flow" in docs/designs/workflow-session-identity.md.
165
+ if (!options.agentConversationId) {
166
+ throw new SessionError('agentConversationId is required for Docker sessions: mint via createAgentConversationId() at the call site', 'SESSION_INIT_FAILED');
167
+ }
168
+ const agentConversationId = options.agentConversationId;
114
169
  const loggerWasActive = logger.isActive();
115
170
  const sessionConfig = buildSessionConfig(config, effectiveSessionId, sessionId, options);
116
- const { prepareDockerInfrastructure } = await import('../docker/docker-infrastructure.js');
117
- const { DockerAgentSession } = await import('../docker/docker-agent-session.js');
118
- const { buildDockerClaudeMd } = await import('../docker/claude-md-seed.js');
119
- const claudeMdContent = buildDockerClaudeMd({
120
- personaName: options.persona,
121
- memoryEnabled: config.userConfig.memory.enabled,
122
- });
123
- const infra = await prepareDockerInfrastructure(sessionConfig.config, { kind: 'docker', agent: agentId }, sessionConfig.sessionDir, sessionConfig.sandboxDir, sessionConfig.escalationDir, sessionConfig.auditLogPath, sessionId);
124
- // Write CLAUDE.md into conversation state dir (unconditionally, even on
125
- // resume, since persona/memory config may change between sessions).
126
- // Clean up stale CLAUDE.md when memory is disabled to avoid leftover rules.
127
- if (infra.conversationStateDir) {
128
- const claudeMdPath = resolve(infra.conversationStateDir, 'CLAUDE.md');
129
- if (claudeMdContent) {
130
- writeFileSync(claudeMdPath, claudeMdContent);
171
+ // Wrap the entire infrastructure + init sequence so that logger.teardown()
172
+ // runs on ANY failure, not just session.initialize() failures.
173
+ // buildSessionConfig() calls logger.setup() which hijacks console globally;
174
+ // if we don't teardown on error, all subsequent console output (including
175
+ // error messages from the orchestrator and XState) silently goes to a log
176
+ // file instead of the terminal.
177
+ let session;
178
+ // Track the infra bundle in outer scope so the catch path can clean
179
+ // up containers/proxies even if failure happens BEFORE the session
180
+ // instance exists (e.g., writeFileSync for CLAUDE.md throws, or the
181
+ // DockerAgentSession constructor throws). Without this, the session
182
+ // is undefined and `session?.close()` is a no-op — the container,
183
+ // sidecar, network, and proxies all leak.
184
+ let infra;
185
+ // Tracks whether THIS factory allocated the infra bundle. When true and
186
+ // the session never reaches a constructed state, the catch path tears
187
+ // down the bundle directly. When false (borrow path), the caller owns
188
+ // the bundle and the factory must NEVER destroy it, even on error.
189
+ let builtInfra = false;
190
+ try {
191
+ const { DockerAgentSession } = await import('../docker/docker-agent-session.js');
192
+ const { buildDockerClaudeMd } = await import('../docker/claude-md-seed.js');
193
+ if (options.workflow?.infrastructure) {
194
+ // Borrow path: the orchestrator owns the bundle's lifetime. We do
195
+ // not call createDockerInfrastructure(); we do not destroy on close.
196
+ // The orchestrator is also responsible for `setTokenSessionId`: it
197
+ // flips the MITM proxy's routing target to the active agent's session
198
+ // ID before each run and clears it on session end.
199
+ infra = options.workflow.infrastructure;
131
200
  }
132
201
  else {
133
- try {
134
- unlinkSync(claudeMdPath);
202
+ // Standalone path: factory creates and owns the bundle.
203
+ // Single-session invariant (§2.1 of workflow-session-identity): the
204
+ // SessionId value doubles as the BundleId. The helper preserves
205
+ // the deterministic `ironcurtain-<sessionId[0:12]>` container
206
+ // name for prior-crash recovery.
207
+ const bundleId = bundleIdFromSessionId(sessionId);
208
+ const { createDockerInfrastructure } = await import('../docker/docker-infrastructure.js');
209
+ infra = await createDockerInfrastructure(sessionConfig.config, { kind: 'docker', agent: agentId }, sessionConfig.sessionDir, sessionConfig.sandboxDir, sessionConfig.escalationDir, bundleId, undefined, undefined, sessionConfig.resolvedSkills);
210
+ // Standalone sessions use their bundle for the session's entire
211
+ // lifetime; pin the token-stream routing ID to this session's ID.
212
+ // Clearing on close is unnecessary because the bundle itself is
213
+ // destroyed by `session.close()` (ownsInfra=true).
214
+ infra.setTokenSessionId(sessionId);
215
+ builtInfra = true;
216
+ }
217
+ const claudeMdContent = buildDockerClaudeMd({
218
+ personaName: options.persona,
219
+ memoryEnabled: sessionConfig.memoryEnabled,
220
+ });
221
+ // Write CLAUDE.md into conversation state dir (unconditionally, even on
222
+ // resume, since persona/memory config may change between sessions).
223
+ // Clean up stale CLAUDE.md when memory is disabled to avoid leftover rules.
224
+ if (infra.conversationStateDir) {
225
+ const claudeMdPath = resolve(infra.conversationStateDir, 'CLAUDE.md');
226
+ if (claudeMdContent) {
227
+ writeFileSync(claudeMdPath, claudeMdContent);
135
228
  }
136
- catch {
137
- /* not present */
229
+ else {
230
+ try {
231
+ unlinkSync(claudeMdPath);
232
+ }
233
+ catch {
234
+ /* not present */
235
+ }
138
236
  }
139
237
  }
140
- }
141
- const session = new DockerAgentSession({
142
- config: sessionConfig.config,
143
- sessionId,
144
- adapter: infra.adapter,
145
- docker: infra.docker,
146
- proxy: infra.proxy,
147
- mitmProxy: infra.mitmProxy,
148
- ca: infra.ca,
149
- fakeKeys: infra.fakeKeys,
150
- sessionDir: sessionConfig.sessionDir,
151
- sandboxDir: sessionConfig.sandboxDir,
152
- escalationDir: sessionConfig.escalationDir,
153
- auditLogPath: sessionConfig.auditLogPath,
154
- useTcp: infra.useTcp,
155
- onEscalation: options.onEscalation,
156
- onEscalationExpired: options.onEscalationExpired,
157
- onEscalationResolved: options.onEscalationResolved,
158
- onDiagnostic: options.onDiagnostic,
159
- preBuiltInfrastructure: {
160
- systemPrompt: sessionConfig.systemPromptAugmentation
161
- ? `${infra.systemPrompt}\n\n${sessionConfig.systemPromptAugmentation}`
162
- : infra.systemPrompt,
163
- image: infra.image,
164
- mitmAddr: infra.mitmAddr,
165
- },
166
- });
167
- try {
238
+ const systemPromptOverride = sessionConfig.systemPromptAugmentation
239
+ ? `${infra.systemPrompt}\n\n${sessionConfig.systemPromptAugmentation}`
240
+ : undefined;
241
+ session = new DockerAgentSession({
242
+ config: sessionConfig.config,
243
+ sessionId,
244
+ agentConversationId,
245
+ infra,
246
+ // Ownership mirrors who allocated the bundle: standalone path owns
247
+ // and tears down on close; borrow path leaves the bundle alive for
248
+ // the external orchestrator.
249
+ ownsInfra: builtInfra,
250
+ agentModelOverride: options.agentModelOverride,
251
+ onEscalation: options.onEscalation,
252
+ onEscalationExpired: options.onEscalationExpired,
253
+ onEscalationResolved: options.onEscalationResolved,
254
+ onDiagnostic: options.onDiagnostic,
255
+ systemPromptOverride,
256
+ });
168
257
  await session.initialize();
258
+ return session;
169
259
  }
170
260
  catch (error) {
171
- await session.close().catch(() => { });
261
+ // If the session was constructed, its close() respects the ownsInfra
262
+ // flag we passed in: standalone sessions (ownsInfra=true) destroy the
263
+ // bundle they own; borrow-mode sessions (ownsInfra=false) leave the
264
+ // caller's bundle intact. We need only invoke close() -- no
265
+ // conditional cleanup here.
266
+ //
267
+ // Otherwise, if we allocated the bundle ourselves but failed before
268
+ // the session was constructed (e.g., the DockerAgentSession
269
+ // constructor threw, or the CLAUDE.md write threw), destroy it
270
+ // directly. NEVER destroy a caller-supplied bundle on this branch --
271
+ // `builtInfra` is false in borrow mode, so the guard below skips it.
272
+ if (session) {
273
+ await session.close().catch(() => { });
274
+ }
275
+ else if (builtInfra && infra) {
276
+ const { destroyDockerInfrastructure } = await import('../docker/docker-infrastructure.js');
277
+ await destroyDockerInfrastructure(infra).catch(() => { });
278
+ }
172
279
  if (!loggerWasActive)
173
280
  logger.teardown();
174
- throw new SessionError(`Docker session initialization failed: ${error instanceof Error ? error.message : String(error)}`, 'SESSION_INIT_FAILED');
281
+ throw error instanceof SessionError
282
+ ? error
283
+ : new SessionError(`Docker session failed: ${error instanceof Error ? error.message : String(error)}`, 'SESSION_INIT_FAILED');
175
284
  }
176
- return session;
177
285
  }
178
286
  /**
179
287
  * Validates that a policyDir path resolves to a location under the
180
288
  * IronCurtain home directory or the package config directory. Prevents
181
289
  * loading attacker-controlled policy files from arbitrary filesystem locations.
182
290
  *
183
- * The package config directory is allowed so that built-in policy variants
184
- * (e.g., the read-only policy for constitution generation) can be loaded
185
- * without copying files into the user home.
291
+ * Thin wrapper around the shared validator in `config/validate-policy-dir.ts`
292
+ * so failures surface as `SessionError` with our standard code — the
293
+ * shared helper throws `PolicyDirValidationError`, which other callers
294
+ * (e.g., the coordinator's `loadPolicy` RPC) surface in their own way.
295
+ *
296
+ * Returns the realpath-canonicalized path. Callers must use the return
297
+ * value for all subsequent reads: feeding the original (possibly-symlinked)
298
+ * path to downstream artifact loaders would reopen the symlink-swap TOCTOU
299
+ * window the validator was introduced to close.
186
300
  *
187
301
  * @throws {SessionError} if the path escapes all trusted directories.
188
302
  */
189
303
  function validatePolicyDir(policyDir) {
190
- const resolvedPolicy = resolveRealPath(policyDir);
191
- const trustedDirs = [getIronCurtainHome(), getPackageConfigDir()].map(resolveRealPath);
192
- if (!trustedDirs.some((dir) => isEqualOrInside(resolvedPolicy, dir))) {
193
- throw new SessionError(`policyDir must be under a trusted directory. ` +
194
- `Received: ${resolvedPolicy}; ` +
195
- `trusted: ${trustedDirs.join(', ')}`, 'SESSION_INIT_FAILED');
304
+ try {
305
+ return sharedValidatePolicyDir(policyDir);
306
+ }
307
+ catch (err) {
308
+ const message = err instanceof Error ? err.message : String(err);
309
+ throw new SessionError(message, 'SESSION_INIT_FAILED');
196
310
  }
197
311
  }
198
312
  /**
@@ -211,10 +325,24 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
211
325
  let { workspacePath, policyDir, systemPromptAugmentation } = opts;
212
326
  const { resumeSessionId, disableAutoApprove } = opts;
213
327
  let serverAllowlist;
214
- // Resolve persona early -- derives policyDir, workspace, server filter,
215
- // and system prompt augmentation from the persona definition.
328
+ let personaDef = undefined;
329
+ // Borrow-mode invariant: per-state artifact dir / slug only make sense
330
+ // alongside an infrastructure bundle. The nested record keeps these
331
+ // fields colocated; the runtime guard catches the all-or-nothing
332
+ // mismatch (the union of the two-bit input domain isn't expressible
333
+ // at the type level without a discriminator on the workflow record).
334
+ if (opts.workflow?.stateDir && !opts.workflow.infrastructure) {
335
+ throw new SessionError('workflow.stateDir requires workflow.infrastructure; borrow-mode artifacts have no owner without a bundle', 'SESSION_INIT_FAILED');
336
+ }
337
+ // Resolve persona early -- derives policyDir, workspace, and server
338
+ // filter from the persona definition. The persona system prompt
339
+ // augmentation is deferred until after the memory gate is computed
340
+ // with the full (persona + job) scope, so the augmentation and the
341
+ // server bolt-on cannot disagree when both opts.persona and opts.jobId
342
+ // are set.
216
343
  if (opts.persona) {
217
344
  const resolved = resolvePersona(opts.persona);
345
+ personaDef = resolved.persona;
218
346
  if (policyDir) {
219
347
  logger.warn('Both persona and policyDir specified; using persona.');
220
348
  }
@@ -224,35 +352,70 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
224
352
  if (!workspacePath) {
225
353
  workspacePath = resolved.workspacePath;
226
354
  }
227
- // Build persona system prompt augmentation (includes MCP memory prompt when enabled).
228
- const memoryEnabled = config.userConfig.memory.enabled;
229
- const personaAugmentation = buildPersonaSystemPromptAugmentation(resolved.persona, memoryEnabled);
355
+ logger.info(`Persona "${opts.persona}" resolved: policyDir=${policyDir}`);
356
+ }
357
+ // Single gate computation with the full scope. Reused for the persona
358
+ // augmentation, the cron-job memory prompt, and the server bolt-on so
359
+ // the prompt cannot mention memory while the relay is gated off (or
360
+ // vice versa). Skip the loadJob disk read when the global kill switch
361
+ // would short-circuit anyway.
362
+ const memoryConfig = config.userConfig.memory;
363
+ const jobDef = memoryConfig.enabled && opts.jobId ? loadJob(createJobId(opts.jobId)) : undefined;
364
+ const memoryEnabled = isMemoryEnabledFor({
365
+ persona: personaDef,
366
+ job: jobDef,
367
+ userConfig: config.userConfig,
368
+ });
369
+ if (personaDef) {
370
+ const personaAugmentation = buildPersonaSystemPromptAugmentation(personaDef, memoryEnabled);
230
371
  systemPromptAugmentation = systemPromptAugmentation
231
372
  ? `${personaAugmentation}\n\n${systemPromptAugmentation}`
232
373
  : personaAugmentation;
233
- logger.info(`Persona "${opts.persona}" resolved: policyDir=${policyDir}`);
234
374
  }
235
375
  if (policyDir) {
236
- validatePolicyDir(policyDir);
376
+ // Use the validator's realpath-canonicalized return value. Passing
377
+ // the original (possibly-symlinked) path through to `generatedDir`
378
+ // would reopen the symlink-swap TOCTOU window the validator was
379
+ // introduced to close: downstream artifact reads (compiled policy,
380
+ // dynamic lists) must happen against the same canonical path the
381
+ // containment check approved.
382
+ policyDir = validatePolicyDir(policyDir);
237
383
  }
238
- const sessionDir = getSessionDir(effectiveSessionId);
384
+ // Paths differ by mode:
385
+ // - Borrow mode: per-state artifacts go under `workflow.stateDir` when
386
+ // the orchestrator supplied one; otherwise fall back to the bundle's
387
+ // bundleDir (legacy path, still used by factory-level tests). The
388
+ // bundle owns workspace/escalation/audit on `workflow.infrastructure`.
389
+ // - Standalone/CLI: everything lives under `{home}/sessions/{id}/`.
390
+ const borrowInfra = opts.workflow?.infrastructure;
391
+ const artifactDir = borrowInfra ? (opts.workflow.stateDir ?? borrowInfra.bundleDir) : undefined;
392
+ const sessionDir = artifactDir ?? getSessionDir(effectiveSessionId);
239
393
  const sandboxDir = workspacePath ?? getSessionSandboxDir(effectiveSessionId);
240
- const escalationDir = getSessionEscalationDir(effectiveSessionId);
241
- const auditLogPath = getSessionAuditLogPath(effectiveSessionId);
242
- // Create the directory when not using an explicit --workspace flag.
243
- // When persona is set, `workspacePath` was derived internally (not
244
- // from the caller), so we still need to ensure it exists.
245
- // Only skip creation for explicit user-provided workspace paths.
246
- if (!opts.workspacePath) {
247
- mkdirSync(sandboxDir, { recursive: true });
394
+ const escalationDir = borrowInfra ? borrowInfra.escalationDir : getSessionEscalationDir(effectiveSessionId);
395
+ const auditLogPath = borrowInfra ? borrowInfra.auditLogPath : getSessionAuditLogPath(effectiveSessionId);
396
+ // Standalone mode creates its own session tree. Borrow mode relies on
397
+ // directories the orchestrator and bundle already own.
398
+ if (!borrowInfra) {
399
+ if (!opts.workspacePath) {
400
+ mkdirSync(sandboxDir, { recursive: true });
401
+ }
402
+ mkdirSync(escalationDir, { recursive: true });
248
403
  }
249
- mkdirSync(escalationDir, { recursive: true });
250
- const sessionLogPath = getSessionLogPath(effectiveSessionId);
404
+ const sessionLogPath = artifactDir
405
+ ? resolve(artifactDir, SESSION_LOG_FILENAME)
406
+ : getSessionLogPath(effectiveSessionId);
407
+ // llm-interactions / auto-approve-llm still live under the standalone
408
+ // session dir even in borrow mode (per-turn LLM logs are not scoped to
409
+ // the workflow state artifact dir; config.llmLogPath is only read when
410
+ // the builtin agent is active, and borrow-mode sessions are Docker).
251
411
  const llmLogPath = getSessionLlmLogPath(effectiveSessionId);
252
412
  const autoApproveLlmLogPath = getSessionAutoApproveLlmLogPath(effectiveSessionId);
253
- // Set up session logging -- captures all console output to file
413
+ // Set up session logging -- captures all console output to file.
414
+ // In borrow mode, setup() retargets the singleton to the new state's
415
+ // log file; a prior state's teardown in session.close() releases the
416
+ // console hijack first.
254
417
  logger.setup({ logFilePath: sessionLogPath });
255
- logger.info(`Session ${sessionId} created`);
418
+ logger.info(`Session ${sessionId} created${opts.workflow?.stateSlug ? ` (state=${opts.workflow.stateSlug})` : ''}`);
256
419
  logger.info(`${workspacePath ? 'Workspace' : 'Sandbox'}: ${sandboxDir}`);
257
420
  logger.info(`Escalation dir: ${escalationDir}`);
258
421
  logger.info(`Audit log: ${auditLogPath}`);
@@ -260,6 +423,18 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
260
423
  if (resumeSessionId) {
261
424
  logger.info(`Resumed from session: ${resumeSessionId}`);
262
425
  }
426
+ // Build userConfig overrides (auto-approver disable, resource budget).
427
+ // Composed incrementally so multiple overrides don't clobber each other.
428
+ let patchedUserConfig = config.userConfig;
429
+ if (disableAutoApprove) {
430
+ patchedUserConfig = { ...patchedUserConfig, autoApprove: { ...patchedUserConfig.autoApprove, enabled: false } };
431
+ }
432
+ if (opts.resourceBudgetOverrides) {
433
+ patchedUserConfig = {
434
+ ...patchedUserConfig,
435
+ resourceBudget: { ...patchedUserConfig.resourceBudget, ...opts.resourceBudgetOverrides },
436
+ };
437
+ }
263
438
  // Override config paths for this session's isolated directories.
264
439
  // Deep-clone mcpServers so patching doesn't mutate the caller's config.
265
440
  const sessionConfig = {
@@ -280,19 +455,23 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
280
455
  }
281
456
  : {}),
282
457
  mcpServers: JSON.parse(JSON.stringify(config.mcpServers)),
283
- // Disable auto-approver for headless sessions (no interactive user context)
284
- ...(disableAutoApprove
285
- ? { userConfig: { ...config.userConfig, autoApprove: { ...config.userConfig.autoApprove, enabled: false } } }
286
- : {}),
458
+ userConfig: patchedUserConfig,
287
459
  };
288
460
  // Apply server allowlist if persona specifies one
289
461
  if (serverAllowlist) {
290
462
  sessionConfig.mcpServers = applyServerAllowlist(sessionConfig.mcpServers, serverAllowlist);
291
463
  }
464
+ const { compiledPolicy: policyForFilter } = loadGeneratedPolicy({
465
+ policyDir: sessionConfig.generatedDir,
466
+ toolAnnotationsDir: sessionConfig.toolAnnotationsDir ?? sessionConfig.generatedDir,
467
+ fallbackDir: getPackageGeneratedDir(),
468
+ });
469
+ sessionConfig.mcpServers = filterMcpServersByPolicy(sessionConfig.mcpServers, extractRequiredServers(policyForFilter));
292
470
  // Inject the memory MCP server for persona and cron job sessions only.
293
471
  // Default (ad-hoc) sessions are stateless and don't benefit from memory.
294
- const memoryConfig = config.userConfig.memory;
295
- if (memoryConfig.enabled && (opts.persona || opts.jobId)) {
472
+ // The gate decision was made once above; reuse it here so the prompt
473
+ // and the relay cannot diverge.
474
+ if (memoryEnabled) {
296
475
  const dbPath = resolveMemoryDbPath({
297
476
  persona: opts.persona,
298
477
  jobId: opts.jobId,
@@ -315,18 +494,47 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
315
494
  }
316
495
  }
317
496
  // Patch MCP server args to use the session-specific sandbox directory
318
- patchMcpServerAllowedDirectory(sessionConfig, sandboxDir);
497
+ applyAllowedDirectoryToMcpArgs(sessionConfig.mcpServers, sandboxDir);
319
498
  // Persist session settings so --resume can restore them.
320
499
  // Only write on initial creation (not when resuming).
321
500
  if (!resumeSessionId) {
322
- saveSessionMetadata(effectiveSessionId, {
501
+ const metadata = {
323
502
  createdAt: new Date().toISOString(),
324
503
  ...(opts.persona ? { persona: opts.persona } : {}),
325
504
  ...(opts.workspacePath ? { workspacePath: opts.workspacePath } : {}),
326
505
  // Only store policyDir when no persona is set (persona derives its own)
327
506
  ...(!opts.persona && policyDir ? { policyDir } : {}),
328
507
  ...(opts.disableAutoApprove ? { disableAutoApprove: true } : {}),
508
+ // Persist the agent-CLI conversation id so `ironcurtain --resume`
509
+ // can continue the same Claude conversation rather than starting
510
+ // fresh. Docker-mode sessions only; builtin has no agent CLI.
511
+ ...(opts.agentConversationId ? { agentConversationId: opts.agentConversationId } : {}),
512
+ };
513
+ if (artifactDir) {
514
+ saveSessionMetadataTo(resolve(artifactDir, SESSION_METADATA_FILENAME), metadata);
515
+ }
516
+ else {
517
+ saveSessionMetadata(effectiveSessionId, metadata);
518
+ }
519
+ }
520
+ // Borrow-mode: re-stage the bundle in-place so per-state persona skills
521
+ // become visible without remounting. Standalone mode returns the set
522
+ // through SessionDirConfig for the docker factory to stage at bundle
523
+ // creation. Builtin (non-Docker) sessions never mount skills, so we
524
+ // skip the discovery walk entirely to avoid spurious filesystem reads
525
+ // and `[skills] Ignoring …` warnings.
526
+ const dockerMode = opts.mode?.kind === 'docker';
527
+ let resolvedSkills;
528
+ if (borrowInfra || dockerMode) {
529
+ resolvedSkills = resolveSkillsForSession({
530
+ ...(opts.persona ? { personaName: opts.persona } : {}),
531
+ ...(opts.workflow?.skillsDir ? { workflowSkillsDir: opts.workflow.skillsDir } : {}),
532
+ ...(opts.workflow?.skillFilter ? { workflowSkillFilter: opts.workflow.skillFilter } : {}),
533
+ ...(opts.workflow?.disableAllSkills ? { disableAllSkills: true } : {}),
329
534
  });
535
+ if (borrowInfra) {
536
+ borrowInfra.restageSkills(resolvedSkills);
537
+ }
330
538
  }
331
539
  return {
332
540
  config: sessionConfig,
@@ -335,24 +543,10 @@ export function buildSessionConfig(config, effectiveSessionId, sessionId, opts =
335
543
  escalationDir,
336
544
  auditLogPath,
337
545
  systemPromptAugmentation,
546
+ memoryEnabled,
547
+ resolvedSkills: borrowInfra ? [] : resolvedSkills,
338
548
  };
339
549
  }
340
- /**
341
- * Patches the filesystem MCP server's allowed directory argument
342
- * to use the session-specific sandbox directory, mirroring the
343
- * logic in loadConfig() that syncs ALLOWED_DIRECTORY.
344
- */
345
- export function patchMcpServerAllowedDirectory(config, sandboxDir) {
346
- const fsServer = config.mcpServers['filesystem'];
347
- if (!fsServer)
348
- return;
349
- // Replace any existing allowed directory path in args.
350
- // The config may have the original default or a previously patched value.
351
- const lastArgIndex = fsServer.args.length - 1;
352
- if (lastArgIndex >= 0) {
353
- fsServer.args[lastArgIndex] = sandboxDir;
354
- }
355
- }
356
- export { SessionError, SessionNotReadyError, SessionClosedError, BudgetExhaustedError } from './errors.js';
550
+ export { SessionError, SessionNotReadyError, SessionClosedError, BudgetExhaustedError } from '../types/errors.js';
357
551
  export { resolveSessionMode, PreflightError } from './preflight.js';
358
552
  //# sourceMappingURL=index.js.map