@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
@@ -1,3 +1,4 @@
1
+ import type { BundleId } from '../session/types.js';
1
2
  /**
2
3
  * Returns the IronCurtain home directory.
3
4
  * Defaults to ~/.ironcurtain, overridable via IRONCURTAIN_HOME env var.
@@ -7,6 +8,14 @@ export declare function getIronCurtainHome(): string;
7
8
  * Returns the sessions base directory: {home}/sessions/
8
9
  */
9
10
  export declare function getSessionsDir(): string;
11
+ /**
12
+ * Filenames shared across session-scoped and workflow-state-scoped
13
+ * layouts. Centralized so the "single source of truth" for how session
14
+ * artifacts are named does not drift between helpers and call sites.
15
+ */
16
+ export declare const SESSION_LOG_FILENAME = "session.log";
17
+ export declare const SESSION_METADATA_FILENAME = "session-metadata.json";
18
+ export declare const SESSION_STATE_FILENAME = "session-state.json";
10
19
  /**
11
20
  * Returns the session directory for a given session ID:
12
21
  * {home}/sessions/{sessionId}/
@@ -126,6 +135,12 @@ export declare function getReadOnlyPolicyDir(): string;
126
135
  * (either the user's IronCurtain home or the package config dir).
127
136
  */
128
137
  export declare function getPackageConfigDir(): string;
138
+ /**
139
+ * Reads just the user constitution text (without base principles).
140
+ * Returns empty string if no user constitution file exists, because
141
+ * an absent user constitution is a valid state (means "no server-specific guidance").
142
+ */
143
+ export declare function loadUserConstitutionText(): string;
129
144
  /**
130
145
  * Loads the combined constitution text (base + optional user constitution).
131
146
  * If ~/.ironcurtain/constitution.md exists, it replaces the package-bundled base.
@@ -137,6 +152,20 @@ export declare function loadConstitutionText(packageBasePath: string): string;
137
152
  * Loads the combined constitution and returns its SHA-256 hex digest.
138
153
  */
139
154
  export declare function computeConstitutionHash(basePath: string): string;
155
+ /**
156
+ * Returns the user workflows directory: {home}/workflows/
157
+ * Users can place custom workflow definitions here.
158
+ */
159
+ export declare function getUserWorkflowsDir(): string;
160
+ /**
161
+ * Returns the user-global skills directory: {home}/skills/
162
+ *
163
+ * Skills placed here are layered into every Docker session regardless
164
+ * of workflow or persona. See `src/skills/discovery.ts` for the
165
+ * resolution order (user-global → persona → workflow, last-wins on
166
+ * collision) and `docs/designs/skills-capability.md` for the format.
167
+ */
168
+ export declare function getUserSkillsDir(): string;
140
169
  /**
141
170
  * Returns the OAuth directory: {home}/oauth/
142
171
  * Stores provider credentials and token files.
@@ -159,6 +188,14 @@ export declare function getOAuthCredentialsPath(providerId: string): string;
159
188
  * can communicate with a running daemon (e.g., add-job, run-job).
160
189
  */
161
190
  export declare function getDaemonSocketPath(): string;
191
+ /**
192
+ * Returns the web UI state file path: {home}/web-ui.json
193
+ *
194
+ * The daemon writes connection info (port + auth token) here on startup
195
+ * so CLI commands (e.g., `observe`) can connect to the WebSocket server.
196
+ * The file is removed on daemon shutdown.
197
+ */
198
+ export declare function getWebUiStatePath(): string;
162
199
  /**
163
200
  * Returns the jobs base directory: {home}/jobs/
164
201
  */
@@ -182,3 +219,147 @@ export declare function getJobWorkspaceDir(jobId: string): string;
182
219
  * {home}/jobs/{jobId}/runs/
183
220
  */
184
221
  export declare function getJobRunsDir(jobId: string): string;
222
+ /**
223
+ * Returns the workflow runs base directory: {home}/workflow-runs/
224
+ */
225
+ export declare function getWorkflowRunsDir(): string;
226
+ /**
227
+ * Returns the directory for a specific workflow run:
228
+ * {home}/workflow-runs/{workflowId}/
229
+ */
230
+ export declare function getWorkflowRunDir(workflowId: string): string;
231
+ /**
232
+ * Returns the containers root for a workflow run:
233
+ * {home}/workflow-runs/{workflowId}/containers/
234
+ *
235
+ * Each bundle (one Docker container + its MITM/Code-Mode proxies, CA,
236
+ * fake keys, audit log, and per-state artifacts) lives under
237
+ * `containers/<bundleId>/`. A single-scope workflow has one entry; a
238
+ * bifurcated workflow with distinct `containerScope` values has one
239
+ * entry per distinct scope.
240
+ */
241
+ export declare function getWorkflowContainersDir(workflowId: string): string;
242
+ /**
243
+ * Returns the outer directory for a single bundle within a workflow run:
244
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/
245
+ *
246
+ * Contains the bundle's audit log, its inner `bundle/` directory with
247
+ * sockets/CA/fake keys/orientation, and the per-state `states/` tree.
248
+ */
249
+ export declare function getBundleDir(workflowId: string, bundleId: BundleId): string;
250
+ /**
251
+ * Returns the inner bundle artifact directory:
252
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/bundle/
253
+ *
254
+ * Holds the bundle-scoped files that `DockerInfrastructure` manages:
255
+ * MCP/MITM sockets, CA cert, fake keys, orientation scripts, and the
256
+ * effective system prompt. Bind-mounted into the Docker container for
257
+ * the UDS endpoints.
258
+ */
259
+ export declare function getBundleBundleDir(workflowId: string, bundleId: BundleId): string;
260
+ /**
261
+ * Returns the per-bundle audit log path:
262
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/audit.jsonl
263
+ *
264
+ * One audit file per bundle (coordinator). Each entry is tagged with
265
+ * the active `persona` so consumers can reconstruct per-persona /
266
+ * per-re-entry slices by scanning.
267
+ */
268
+ export declare function getBundleAuditLogPath(workflowId: string, bundleId: BundleId): string;
269
+ /**
270
+ * Returns the per-bundle states root:
271
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/states/
272
+ *
273
+ * Each agent state invocation (including re-entries) that borrows this
274
+ * bundle gets its own subdirectory keyed by `{stateId}.{N}`.
275
+ */
276
+ export declare function getBundleStatesDir(workflowId: string, bundleId: BundleId): string;
277
+ /**
278
+ * Returns the per-invocation artifact directory for a single state:
279
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/states/{stateSlug}/
280
+ *
281
+ * Holds this invocation's `session.log` and `session-metadata.json`.
282
+ * `stateSlug` is `{stateId}.{N}` (e.g., `fetch.1`, `plan.2`).
283
+ */
284
+ export declare function getInvocationDir(workflowId: string, bundleId: BundleId, stateSlug: string): string;
285
+ /**
286
+ * Picks the next available `{stateId}.{N}` slug in `statesDir`. Returns
287
+ * `${stateId}.1` when no matching dir exists; otherwise the max existing
288
+ * N plus 1. Used so every state entry (true logical re-visits AND resume
289
+ * legs of a single visit) gets its own forensic dir — `session.log` and
290
+ * `session-metadata.json` no longer interleave across resume attempts.
291
+ */
292
+ export declare function nextStateSlug(statesDir: string, stateId: string): string;
293
+ /**
294
+ * Returns the coordinator control socket path for a bundle:
295
+ * {home}/run/{bundleId[0:12]}/ctrl.sock
296
+ *
297
+ * The coordinator listens on this UDS to accept policy hot-swap
298
+ * requests from the orchestrator. The socket sits under the bundle's
299
+ * per-bundle runtime root (mode `0o700`) so filesystem permissions
300
+ * gate access.
301
+ *
302
+ * The directory slug is truncated to 12 chars of `bundleId` so the
303
+ * assembled path fits under macOS `sockaddr_un.sun_path` (104 chars).
304
+ * 16^12 ≈ 2.8e14 combinations — collisions in the host-wide
305
+ * `run/<slug>/` namespace are astronomical.
306
+ */
307
+ export declare function getBundleControlSocketPath(bundleId: BundleId): string;
308
+ /**
309
+ * Returns the per-bundle runtime root directory:
310
+ * {home}/run/{bundleId[0:12]}/
311
+ *
312
+ * Holds the coordinator control socket plus two subdirectories
313
+ * (`sockets/` and `host/`) so the bind-mounted UDS files can be
314
+ * co-located without exposing host-only sockets to the container.
315
+ */
316
+ export declare function getBundleRuntimeRoot(bundleId: BundleId): string;
317
+ /**
318
+ * Returns the per-bundle container-visible sockets directory:
319
+ * {home}/run/{bundleId[0:12]}/sockets/
320
+ *
321
+ * Two UDS files live here:
322
+ * - `proxy.sock` (Code Mode proxy — bind-mounted into container)
323
+ * - `mitm-proxy.sock` (MITM proxy — bind-mounted into container)
324
+ *
325
+ * This directory is what `prepareDockerInfrastructure()` bind-mounts as
326
+ * `/run/ironcurtain/` inside the container (read-write). Only these two
327
+ * socket files live here — no audit logs, escalation files, or other
328
+ * session artifacts.
329
+ */
330
+ export declare function getBundleSocketsDir(bundleId: BundleId): string;
331
+ /**
332
+ * Returns the per-bundle host-only directory:
333
+ * {home}/run/{bundleId[0:12]}/host/
334
+ *
335
+ * Holds UDS files that must NOT be visible to the container
336
+ * (currently just the MITM control socket).
337
+ */
338
+ export declare function getBundleHostOnlyDir(bundleId: BundleId): string;
339
+ /**
340
+ * Code Mode proxy UDS path for a bundle:
341
+ * {home}/run/{bundleId[0:12]}/sockets/proxy.sock
342
+ *
343
+ * Bound by the host-side Code Mode proxy; reachable inside the
344
+ * container as `/run/ironcurtain/proxy.sock` via the bind mount on
345
+ * `getBundleSocketsDir()`.
346
+ */
347
+ export declare function getBundleProxySocketPath(bundleId: BundleId): string;
348
+ /**
349
+ * MITM proxy UDS path for a bundle:
350
+ * {home}/run/{bundleId[0:12]}/sockets/mitm-proxy.sock
351
+ *
352
+ * Bound by the host-side MITM proxy; reachable inside the container as
353
+ * `/run/ironcurtain/mitm-proxy.sock` via the bind mount on
354
+ * `getBundleSocketsDir()`.
355
+ */
356
+ export declare function getBundleMitmProxySocketPath(bundleId: BundleId): string;
357
+ /**
358
+ * MITM control UDS path for a bundle:
359
+ * {home}/run/{bundleId[0:12]}/host/mitm-control.sock
360
+ *
361
+ * Host-local only — lives under `host/` rather than `sockets/` so the
362
+ * bind mount that exposes `sockets/` at `/run/ironcurtain/` does not
363
+ * expose this socket to the container.
364
+ */
365
+ export declare function getBundleMitmControlSocketPath(bundleId: BundleId): string;
@@ -1,9 +1,34 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { existsSync, readFileSync } from 'node:fs';
2
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, resolve } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { SESSION_STATE_FILENAME } from '../docker/pty-types.js';
6
+ import { JOB_ID_PATTERN } from '../types/slug.js';
7
+ /**
8
+ * Short slug length used in file/directory names that must fit under
9
+ * `sockaddr_un.sun_path` (macOS ~104 bytes, Linux ~108 bytes). 12 hex
10
+ * chars give 16^12 ≈ 2.8e14 combinations, so collisions in the
11
+ * host-wide `~/.ironcurtain/run/<slug>/` namespace are astronomical —
12
+ * important because bundle teardown runs `rmSync(runtimeRoot)` and a
13
+ * collision would wipe another live bundle's sockets. The full UUID
14
+ * remains the authoritative identity in Docker labels and directory
15
+ * paths; only socket-adjacent names use the short form. The new
16
+ * `~/.ironcurtain/run/<12chars>/sockets/<name>.sock` layout has ample
17
+ * sun_path budget for the longer slug on both macOS (104) and Linux
18
+ * (108).
19
+ */
20
+ const BUNDLE_SLUG_LEN = 12;
21
+ /**
22
+ * Derives the short slug from a `BundleId`. Strips hyphens first so the
23
+ * resulting `BUNDLE_SLUG_LEN` characters are all hex — a raw
24
+ * `substring(0, 12)` on a canonical UUID would include the hyphen at
25
+ * position 8 and yield only 11 hex digits of entropy (16^11 ≈ 1.8e13).
26
+ * 12 hex digits give 16^12 ≈ 2.8e14, matching the collision-space math
27
+ * the layout depends on.
28
+ */
29
+ function toBundleSlug(bundleId) {
30
+ return bundleId.replace(/-/g, '').substring(0, BUNDLE_SLUG_LEN);
31
+ }
7
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
33
  /**
9
34
  * Returns the IronCurtain home directory.
@@ -19,12 +44,37 @@ export function getSessionsDir() {
19
44
  return resolve(getIronCurtainHome(), 'sessions');
20
45
  }
21
46
  /**
22
- * Validates that a session ID contains only safe characters
23
- * (alphanumeric, hyphens, underscores) to prevent path traversal.
47
+ * Characters permitted in any identifier that gets embedded in a
48
+ * filesystem path by this module (session IDs, workflow IDs, persona
49
+ * slugs, daemon log names). Rejects path separators, glob
50
+ * metacharacters, NUL bytes, and everything else that could escape the
51
+ * target directory.
52
+ */
53
+ const PATH_SAFE_SLUG_RE = /^[a-zA-Z0-9_-]+$/;
54
+ function assertPathSafeSlug(kind, value) {
55
+ if (!PATH_SAFE_SLUG_RE.test(value)) {
56
+ throw new Error(`Invalid ${kind}: ${value}`);
57
+ }
58
+ }
59
+ /**
60
+ * Filenames shared across session-scoped and workflow-state-scoped
61
+ * layouts. Centralized so the "single source of truth" for how session
62
+ * artifacts are named does not drift between helpers and call sites.
63
+ */
64
+ export const SESSION_LOG_FILENAME = 'session.log';
65
+ export const SESSION_METADATA_FILENAME = 'session-metadata.json';
66
+ export const SESSION_STATE_FILENAME = 'session-state.json';
67
+ /**
68
+ * Validates a workflow state slug of the form `{stateId}.{visitCount}`
69
+ * (e.g., `fetch.1`, `plan.2`). Accepts dot-separated segments where
70
+ * each segment satisfies `PATH_SAFE_SLUG_RE`. Empty segments — produced
71
+ * by `..`, leading or trailing dots — fail the per-segment check.
24
72
  */
25
- function validateSessionId(sessionId) {
26
- if (!/^[a-zA-Z0-9_-]+$/.test(sessionId)) {
27
- throw new Error(`Invalid session ID: ${sessionId}`);
73
+ function assertStateSlug(value) {
74
+ if (value.length === 0)
75
+ throw new Error(`Invalid state slug: ${value}`);
76
+ for (const segment of value.split('.')) {
77
+ assertPathSafeSlug('state slug segment', segment);
28
78
  }
29
79
  }
30
80
  /**
@@ -32,7 +82,7 @@ function validateSessionId(sessionId) {
32
82
  * {home}/sessions/{sessionId}/
33
83
  */
34
84
  export function getSessionDir(sessionId) {
35
- validateSessionId(sessionId);
85
+ assertPathSafeSlug('session ID', sessionId);
36
86
  return resolve(getSessionsDir(), sessionId);
37
87
  }
38
88
  /**
@@ -54,7 +104,7 @@ export function getSessionEscalationDir(sessionId) {
54
104
  * {home}/sessions/{sessionId}/session-metadata.json
55
105
  */
56
106
  export function getSessionMetadataPath(sessionId) {
57
- return resolve(getSessionDir(sessionId), 'session-metadata.json');
107
+ return resolve(getSessionDir(sessionId), SESSION_METADATA_FILENAME);
58
108
  }
59
109
  /**
60
110
  * Returns the audit log path for a given session:
@@ -75,7 +125,7 @@ export function getSessionInteractionLogPath(sessionId) {
75
125
  * {home}/sessions/{sessionId}/session.log
76
126
  */
77
127
  export function getSessionLogPath(sessionId) {
78
- return resolve(getSessionDir(sessionId), 'session.log');
128
+ return resolve(getSessionDir(sessionId), SESSION_LOG_FILENAME);
79
129
  }
80
130
  /**
81
131
  * Returns the LLM interaction log path for a given session:
@@ -142,9 +192,7 @@ export function getLogsDir() {
142
192
  * E.g., getDaemonLogPath('signal-bot') → {home}/logs/signal-bot.log
143
193
  */
144
194
  export function getDaemonLogPath(name) {
145
- if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
146
- throw new Error(`Invalid daemon log name: ${name}`);
147
- }
195
+ assertPathSafeSlug('daemon log name', name);
148
196
  return resolve(getLogsDir(), `${name}.log`);
149
197
  }
150
198
  /**
@@ -194,6 +242,22 @@ export function getReadOnlyPolicyDir() {
194
242
  export function getPackageConfigDir() {
195
243
  return resolve(__dirname);
196
244
  }
245
+ /**
246
+ * Reads just the user constitution text (without base principles).
247
+ * Returns empty string if no user constitution file exists, because
248
+ * an absent user constitution is a valid state (means "no server-specific guidance").
249
+ */
250
+ export function loadUserConstitutionText() {
251
+ const userPath = getUserConstitutionPath();
252
+ const fallbackPath = getBaseUserConstitutionPath();
253
+ if (existsSync(userPath)) {
254
+ return readFileSync(userPath, 'utf-8');
255
+ }
256
+ if (existsSync(fallbackPath)) {
257
+ return readFileSync(fallbackPath, 'utf-8');
258
+ }
259
+ return '';
260
+ }
197
261
  /**
198
262
  * Loads the combined constitution text (base + optional user constitution).
199
263
  * If ~/.ironcurtain/constitution.md exists, it replaces the package-bundled base.
@@ -209,17 +273,10 @@ export function loadConstitutionText(packageBasePath) {
209
273
  const base = readFileSync(basePath, 'utf-8');
210
274
  const userPath = getUserConstitutionPath();
211
275
  const userFallbackPath = getBaseUserConstitutionPath();
212
- let effectiveUserPath;
213
- if (existsSync(userPath)) {
214
- effectiveUserPath = userPath;
215
- }
216
- else if (existsSync(userFallbackPath)) {
217
- effectiveUserPath = userFallbackPath;
218
- }
219
- else {
276
+ if (!existsSync(userPath) && !existsSync(userFallbackPath)) {
220
277
  throw new Error(`User constitution not found: tried ${userPath} and ${userFallbackPath}`);
221
278
  }
222
- const user = readFileSync(effectiveUserPath, 'utf-8');
279
+ const user = loadUserConstitutionText();
223
280
  return `${base}\n\n${user}`;
224
281
  }
225
282
  /**
@@ -229,6 +286,24 @@ export function computeConstitutionHash(basePath) {
229
286
  const text = loadConstitutionText(basePath);
230
287
  return createHash('sha256').update(text).digest('hex');
231
288
  }
289
+ /**
290
+ * Returns the user workflows directory: {home}/workflows/
291
+ * Users can place custom workflow definitions here.
292
+ */
293
+ export function getUserWorkflowsDir() {
294
+ return resolve(getIronCurtainHome(), 'workflows');
295
+ }
296
+ /**
297
+ * Returns the user-global skills directory: {home}/skills/
298
+ *
299
+ * Skills placed here are layered into every Docker session regardless
300
+ * of workflow or persona. See `src/skills/discovery.ts` for the
301
+ * resolution order (user-global → persona → workflow, last-wins on
302
+ * collision) and `docs/designs/skills-capability.md` for the format.
303
+ */
304
+ export function getUserSkillsDir() {
305
+ return resolve(getIronCurtainHome(), 'skills');
306
+ }
232
307
  // ---------------------------------------------------------------------------
233
308
  // OAuth paths
234
309
  // ---------------------------------------------------------------------------
@@ -276,6 +351,16 @@ export function getOAuthCredentialsPath(providerId) {
276
351
  export function getDaemonSocketPath() {
277
352
  return resolve(getIronCurtainHome(), 'daemon.sock');
278
353
  }
354
+ /**
355
+ * Returns the web UI state file path: {home}/web-ui.json
356
+ *
357
+ * The daemon writes connection info (port + auth token) here on startup
358
+ * so CLI commands (e.g., `observe`) can connect to the WebSocket server.
359
+ * The file is removed on daemon shutdown.
360
+ */
361
+ export function getWebUiStatePath() {
362
+ return resolve(getIronCurtainHome(), 'web-ui.json');
363
+ }
279
364
  // ---------------------------------------------------------------------------
280
365
  // Job paths (cron mode)
281
366
  // ---------------------------------------------------------------------------
@@ -285,7 +370,6 @@ export function getDaemonSocketPath() {
285
370
  export function getJobsDir() {
286
371
  return resolve(getIronCurtainHome(), 'jobs');
287
372
  }
288
- import { JOB_ID_PATTERN } from '../cron/types.js';
289
373
  /**
290
374
  * Validates that a job ID contains only safe characters.
291
375
  */
@@ -322,4 +406,223 @@ export function getJobWorkspaceDir(jobId) {
322
406
  export function getJobRunsDir(jobId) {
323
407
  return resolve(getJobDir(jobId), 'runs');
324
408
  }
409
+ // ---------------------------------------------------------------------------
410
+ // Workflow run paths
411
+ // ---------------------------------------------------------------------------
412
+ /**
413
+ * Returns the workflow runs base directory: {home}/workflow-runs/
414
+ */
415
+ export function getWorkflowRunsDir() {
416
+ return resolve(getIronCurtainHome(), 'workflow-runs');
417
+ }
418
+ /**
419
+ * Returns the directory for a specific workflow run:
420
+ * {home}/workflow-runs/{workflowId}/
421
+ */
422
+ export function getWorkflowRunDir(workflowId) {
423
+ assertPathSafeSlug('workflow ID', workflowId);
424
+ return resolve(getWorkflowRunsDir(), workflowId);
425
+ }
426
+ /**
427
+ * Returns the containers root for a workflow run:
428
+ * {home}/workflow-runs/{workflowId}/containers/
429
+ *
430
+ * Each bundle (one Docker container + its MITM/Code-Mode proxies, CA,
431
+ * fake keys, audit log, and per-state artifacts) lives under
432
+ * `containers/<bundleId>/`. A single-scope workflow has one entry; a
433
+ * bifurcated workflow with distinct `containerScope` values has one
434
+ * entry per distinct scope.
435
+ */
436
+ export function getWorkflowContainersDir(workflowId) {
437
+ return resolve(getWorkflowRunDir(workflowId), 'containers');
438
+ }
439
+ /**
440
+ * Returns the outer directory for a single bundle within a workflow run:
441
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/
442
+ *
443
+ * Contains the bundle's audit log, its inner `bundle/` directory with
444
+ * sockets/CA/fake keys/orientation, and the per-state `states/` tree.
445
+ */
446
+ export function getBundleDir(workflowId, bundleId) {
447
+ assertPathSafeSlug('bundle ID', bundleId);
448
+ return resolve(getWorkflowContainersDir(workflowId), bundleId);
449
+ }
450
+ /**
451
+ * Returns the inner bundle artifact directory:
452
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/bundle/
453
+ *
454
+ * Holds the bundle-scoped files that `DockerInfrastructure` manages:
455
+ * MCP/MITM sockets, CA cert, fake keys, orientation scripts, and the
456
+ * effective system prompt. Bind-mounted into the Docker container for
457
+ * the UDS endpoints.
458
+ */
459
+ export function getBundleBundleDir(workflowId, bundleId) {
460
+ return resolve(getBundleDir(workflowId, bundleId), 'bundle');
461
+ }
462
+ /**
463
+ * Returns the per-bundle audit log path:
464
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/audit.jsonl
465
+ *
466
+ * One audit file per bundle (coordinator). Each entry is tagged with
467
+ * the active `persona` so consumers can reconstruct per-persona /
468
+ * per-re-entry slices by scanning.
469
+ */
470
+ export function getBundleAuditLogPath(workflowId, bundleId) {
471
+ return resolve(getBundleDir(workflowId, bundleId), 'audit.jsonl');
472
+ }
473
+ /**
474
+ * Returns the per-bundle states root:
475
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/states/
476
+ *
477
+ * Each agent state invocation (including re-entries) that borrows this
478
+ * bundle gets its own subdirectory keyed by `{stateId}.{N}`.
479
+ */
480
+ export function getBundleStatesDir(workflowId, bundleId) {
481
+ return resolve(getBundleDir(workflowId, bundleId), 'states');
482
+ }
483
+ /**
484
+ * Returns the per-invocation artifact directory for a single state:
485
+ * {home}/workflow-runs/{workflowId}/containers/{bundleId}/states/{stateSlug}/
486
+ *
487
+ * Holds this invocation's `session.log` and `session-metadata.json`.
488
+ * `stateSlug` is `{stateId}.{N}` (e.g., `fetch.1`, `plan.2`).
489
+ */
490
+ export function getInvocationDir(workflowId, bundleId, stateSlug) {
491
+ assertStateSlug(stateSlug);
492
+ return resolve(getBundleStatesDir(workflowId, bundleId), stateSlug);
493
+ }
494
+ /**
495
+ * Picks the next available `{stateId}.{N}` slug in `statesDir`. Returns
496
+ * `${stateId}.1` when no matching dir exists; otherwise the max existing
497
+ * N plus 1. Used so every state entry (true logical re-visits AND resume
498
+ * legs of a single visit) gets its own forensic dir — `session.log` and
499
+ * `session-metadata.json` no longer interleave across resume attempts.
500
+ */
501
+ export function nextStateSlug(statesDir, stateId) {
502
+ assertPathSafeSlug('state ID', stateId);
503
+ let max = 0;
504
+ if (existsSync(statesDir)) {
505
+ const prefix = `${stateId}.`;
506
+ for (const entry of readdirSync(statesDir, { withFileTypes: true })) {
507
+ if (!entry.isDirectory() || !entry.name.startsWith(prefix))
508
+ continue;
509
+ // Decimal-digit-only suffix; reject "1e6", "0x10", " 1 ", "01",
510
+ // etc. so a stray dir name can't inflate the next slug.
511
+ const suffix = entry.name.slice(prefix.length);
512
+ if (!DECIMAL_SUFFIX_RE.test(suffix))
513
+ continue;
514
+ const n = Number(suffix);
515
+ if (n > max)
516
+ max = n;
517
+ }
518
+ }
519
+ return `${stateId}.${max + 1}`;
520
+ }
521
+ const DECIMAL_SUFFIX_RE = /^(?:0|[1-9]\d*)$/;
522
+ /**
523
+ * Returns the coordinator control socket path for a bundle:
524
+ * {home}/run/{bundleId[0:12]}/ctrl.sock
525
+ *
526
+ * The coordinator listens on this UDS to accept policy hot-swap
527
+ * requests from the orchestrator. The socket sits under the bundle's
528
+ * per-bundle runtime root (mode `0o700`) so filesystem permissions
529
+ * gate access.
530
+ *
531
+ * The directory slug is truncated to 12 chars of `bundleId` so the
532
+ * assembled path fits under macOS `sockaddr_un.sun_path` (104 chars).
533
+ * 16^12 ≈ 2.8e14 combinations — collisions in the host-wide
534
+ * `run/<slug>/` namespace are astronomical.
535
+ */
536
+ export function getBundleControlSocketPath(bundleId) {
537
+ return resolve(getBundleRuntimeRoot(bundleId), 'ctrl.sock');
538
+ }
539
+ // ---------------------------------------------------------------------------
540
+ // Per-bundle UDS endpoints (MCP proxy, MITM proxy, MITM control, coordinator)
541
+ // ---------------------------------------------------------------------------
542
+ //
543
+ // Every UDS file owned by a bundle lives under a single per-bundle root
544
+ // at `~/.ironcurtain/run/<bundleId[0:12]>/`. The directory is created
545
+ // with `0o700` (see `docker-infrastructure.ts`), inheriting the same
546
+ // security posture as the rest of the IronCurtain home. Worst-case
547
+ // assembled path (with a 20-char username) is about 76 bytes —
548
+ // comfortably under macOS 104 and Linux 108 `sockaddr_un.sun_path` caps.
549
+ //
550
+ // Three locations per bundle:
551
+ // run/<bid12>/ctrl.sock → coordinator control socket
552
+ // run/<bid12>/sockets/ → bind-mounted as /run/ironcurtain/
553
+ // run/<bid12>/host/ → host-local only (MITM control)
554
+ // The `sockets/` vs `host/` split keeps the bind-mounted endpoints
555
+ // separate from host-only endpoints so `isEndpointAllowed` is not the
556
+ // only thing standing between the container and the MITM control API.
557
+ /**
558
+ * Returns the per-bundle runtime root directory:
559
+ * {home}/run/{bundleId[0:12]}/
560
+ *
561
+ * Holds the coordinator control socket plus two subdirectories
562
+ * (`sockets/` and `host/`) so the bind-mounted UDS files can be
563
+ * co-located without exposing host-only sockets to the container.
564
+ */
565
+ export function getBundleRuntimeRoot(bundleId) {
566
+ assertPathSafeSlug('bundle ID', bundleId);
567
+ return resolve(getIronCurtainHome(), 'run', toBundleSlug(bundleId));
568
+ }
569
+ /**
570
+ * Returns the per-bundle container-visible sockets directory:
571
+ * {home}/run/{bundleId[0:12]}/sockets/
572
+ *
573
+ * Two UDS files live here:
574
+ * - `proxy.sock` (Code Mode proxy — bind-mounted into container)
575
+ * - `mitm-proxy.sock` (MITM proxy — bind-mounted into container)
576
+ *
577
+ * This directory is what `prepareDockerInfrastructure()` bind-mounts as
578
+ * `/run/ironcurtain/` inside the container (read-write). Only these two
579
+ * socket files live here — no audit logs, escalation files, or other
580
+ * session artifacts.
581
+ */
582
+ export function getBundleSocketsDir(bundleId) {
583
+ return resolve(getBundleRuntimeRoot(bundleId), 'sockets');
584
+ }
585
+ /**
586
+ * Returns the per-bundle host-only directory:
587
+ * {home}/run/{bundleId[0:12]}/host/
588
+ *
589
+ * Holds UDS files that must NOT be visible to the container
590
+ * (currently just the MITM control socket).
591
+ */
592
+ export function getBundleHostOnlyDir(bundleId) {
593
+ return resolve(getBundleRuntimeRoot(bundleId), 'host');
594
+ }
595
+ /**
596
+ * Code Mode proxy UDS path for a bundle:
597
+ * {home}/run/{bundleId[0:12]}/sockets/proxy.sock
598
+ *
599
+ * Bound by the host-side Code Mode proxy; reachable inside the
600
+ * container as `/run/ironcurtain/proxy.sock` via the bind mount on
601
+ * `getBundleSocketsDir()`.
602
+ */
603
+ export function getBundleProxySocketPath(bundleId) {
604
+ return resolve(getBundleSocketsDir(bundleId), 'proxy.sock');
605
+ }
606
+ /**
607
+ * MITM proxy UDS path for a bundle:
608
+ * {home}/run/{bundleId[0:12]}/sockets/mitm-proxy.sock
609
+ *
610
+ * Bound by the host-side MITM proxy; reachable inside the container as
611
+ * `/run/ironcurtain/mitm-proxy.sock` via the bind mount on
612
+ * `getBundleSocketsDir()`.
613
+ */
614
+ export function getBundleMitmProxySocketPath(bundleId) {
615
+ return resolve(getBundleSocketsDir(bundleId), 'mitm-proxy.sock');
616
+ }
617
+ /**
618
+ * MITM control UDS path for a bundle:
619
+ * {home}/run/{bundleId[0:12]}/host/mitm-control.sock
620
+ *
621
+ * Host-local only — lives under `host/` rather than `sockets/` so the
622
+ * bind mount that exposes `sockets/` at `/run/ironcurtain/` does not
623
+ * expose this socket to the container.
624
+ */
625
+ export function getBundleMitmControlSocketPath(bundleId) {
626
+ return resolve(getBundleHostOnlyDir(bundleId), 'mitm-control.sock');
627
+ }
325
628
  //# sourceMappingURL=paths.js.map