@provos/ironcurtain 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/README.md +23 -19
  2. package/dist/cli-help.d.ts +12 -0
  3. package/dist/cli-help.js +21 -0
  4. package/dist/cli-help.js.map +1 -1
  5. package/dist/cli.js +14 -6
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config/agent-model-guard.d.ts +14 -0
  8. package/dist/config/agent-model-guard.js +24 -0
  9. package/dist/config/agent-model-guard.js.map +1 -0
  10. package/dist/config/config-command.d.ts +33 -1
  11. package/dist/config/config-command.js +723 -11
  12. package/dist/config/config-command.js.map +1 -1
  13. package/dist/config/first-start.js +2 -8
  14. package/dist/config/first-start.js.map +1 -1
  15. package/dist/config/model-provider.d.ts +9 -0
  16. package/dist/config/model-provider.js +13 -0
  17. package/dist/config/model-provider.js.map +1 -1
  18. package/dist/config/openrouter-catalog.d.ts +46 -0
  19. package/dist/config/openrouter-catalog.js +111 -0
  20. package/dist/config/openrouter-catalog.js.map +1 -0
  21. package/dist/config/openrouter-models-fallback.d.ts +15 -0
  22. package/dist/config/openrouter-models-fallback.js +49 -0
  23. package/dist/config/openrouter-models-fallback.js.map +1 -0
  24. package/dist/config/paths.d.ts +48 -6
  25. package/dist/config/paths.js +63 -8
  26. package/dist/config/paths.js.map +1 -1
  27. package/dist/config/types.d.ts +8 -0
  28. package/dist/config/user-config.d.ts +179 -6
  29. package/dist/config/user-config.js +282 -5
  30. package/dist/config/user-config.js.map +1 -1
  31. package/dist/daemon/daemon-command.js +14 -5
  32. package/dist/daemon/daemon-command.js.map +1 -1
  33. package/dist/daemon/ironcurtain-daemon.d.ts +18 -0
  34. package/dist/daemon/ironcurtain-daemon.js +28 -1
  35. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  36. package/dist/daemon-client/daemon-client.d.ts +123 -0
  37. package/dist/daemon-client/daemon-client.js +324 -0
  38. package/dist/daemon-client/daemon-client.js.map +1 -0
  39. package/dist/docker/adapters/claude-code.js +109 -1
  40. package/dist/docker/adapters/claude-code.js.map +1 -1
  41. package/dist/docker/adapters/codex.d.ts +18 -0
  42. package/dist/docker/adapters/codex.js +376 -0
  43. package/dist/docker/adapters/codex.js.map +1 -0
  44. package/dist/docker/adapters/goose.js +38 -2
  45. package/dist/docker/adapters/goose.js.map +1 -1
  46. package/dist/docker/agent-adapter.d.ts +34 -6
  47. package/dist/docker/agent-adapter.js +2 -0
  48. package/dist/docker/agent-adapter.js.map +1 -1
  49. package/dist/docker/agent-registry.js +5 -0
  50. package/dist/docker/agent-registry.js.map +1 -1
  51. package/dist/docker/apple-container-manager.d.ts +49 -0
  52. package/dist/docker/apple-container-manager.js +486 -0
  53. package/dist/docker/apple-container-manager.js.map +1 -0
  54. package/dist/docker/ca.js +9 -2
  55. package/dist/docker/ca.js.map +1 -1
  56. package/dist/docker/code-mode-proxy.d.ts +11 -0
  57. package/dist/docker/code-mode-proxy.js +3 -1
  58. package/dist/docker/code-mode-proxy.js.map +1 -1
  59. package/dist/docker/container-lifecycle.d.ts +2 -2
  60. package/dist/docker/container-lifecycle.js +9 -0
  61. package/dist/docker/container-lifecycle.js.map +1 -1
  62. package/dist/docker/container-runtime.d.ts +29 -0
  63. package/dist/docker/container-runtime.js +76 -0
  64. package/dist/docker/container-runtime.js.map +1 -0
  65. package/dist/docker/docker-agent-session.d.ts +31 -0
  66. package/dist/docker/docker-agent-session.js +97 -7
  67. package/dist/docker/docker-agent-session.js.map +1 -1
  68. package/dist/docker/docker-infrastructure.d.ts +232 -15
  69. package/dist/docker/docker-infrastructure.js +762 -118
  70. package/dist/docker/docker-infrastructure.js.map +1 -1
  71. package/dist/docker/docker-manager.d.ts +25 -5
  72. package/dist/docker/docker-manager.js +388 -21
  73. package/dist/docker/docker-manager.js.map +1 -1
  74. package/dist/docker/docker-probe.d.ts +45 -0
  75. package/dist/docker/docker-probe.js +86 -0
  76. package/dist/docker/docker-probe.js.map +1 -0
  77. package/dist/docker/docker-progress-sink.d.ts +1 -1
  78. package/dist/docker/docker-resource-lifecycle.d.ts +71 -0
  79. package/dist/docker/docker-resource-lifecycle.js +454 -0
  80. package/dist/docker/docker-resource-lifecycle.js.map +1 -0
  81. package/dist/docker/gc-command.d.ts +2 -0
  82. package/dist/docker/gc-command.js +37 -0
  83. package/dist/docker/gc-command.js.map +1 -0
  84. package/dist/docker/mitm-proxy.d.ts +58 -5
  85. package/dist/docker/mitm-proxy.js +338 -26
  86. package/dist/docker/mitm-proxy.js.map +1 -1
  87. package/dist/docker/network-topology.d.ts +88 -0
  88. package/dist/docker/network-topology.js +167 -0
  89. package/dist/docker/network-topology.js.map +1 -0
  90. package/dist/docker/oauth-credentials.d.ts +13 -0
  91. package/dist/docker/oauth-credentials.js +125 -0
  92. package/dist/docker/oauth-credentials.js.map +1 -1
  93. package/dist/docker/openrouter.d.ts +107 -0
  94. package/dist/docker/openrouter.js +236 -0
  95. package/dist/docker/openrouter.js.map +1 -0
  96. package/dist/docker/orientation.js +1 -1
  97. package/dist/docker/orientation.js.map +1 -1
  98. package/dist/docker/package-types.d.ts +1 -1
  99. package/dist/docker/platform.d.ts +3 -1
  100. package/dist/docker/platform.js +3 -1
  101. package/dist/docker/platform.js.map +1 -1
  102. package/dist/docker/provider-config.d.ts +35 -1
  103. package/dist/docker/provider-config.js +93 -3
  104. package/dist/docker/provider-config.js.map +1 -1
  105. package/dist/docker/provision-lock.d.ts +46 -0
  106. package/dist/docker/provision-lock.js +152 -0
  107. package/dist/docker/provision-lock.js.map +1 -0
  108. package/dist/docker/pty-session.d.ts +22 -0
  109. package/dist/docker/pty-session.js +238 -84
  110. package/dist/docker/pty-session.js.map +1 -1
  111. package/dist/docker/pty-types.d.ts +15 -0
  112. package/dist/docker/pty-types.js +8 -0
  113. package/dist/docker/pty-types.js.map +1 -1
  114. package/dist/docker/registry-proxy.d.ts +46 -0
  115. package/dist/docker/registry-proxy.js +255 -1
  116. package/dist/docker/registry-proxy.js.map +1 -1
  117. package/dist/docker/resource-limits.d.ts +14 -5
  118. package/dist/docker/resource-limits.js +11 -8
  119. package/dist/docker/resource-limits.js.map +1 -1
  120. package/dist/docker/sse-extractor.js +55 -5
  121. package/dist/docker/sse-extractor.js.map +1 -1
  122. package/dist/docker/stream-delay.d.ts +86 -0
  123. package/dist/docker/stream-delay.js +218 -0
  124. package/dist/docker/stream-delay.js.map +1 -0
  125. package/dist/docker/token-stream-types.d.ts +15 -0
  126. package/dist/docker/trajectory-capture.d.ts +79 -0
  127. package/dist/docker/trajectory-capture.js +521 -0
  128. package/dist/docker/trajectory-capture.js.map +1 -0
  129. package/dist/docker/trajectory-reassembler.d.ts +286 -0
  130. package/dist/docker/trajectory-reassembler.js +1010 -0
  131. package/dist/docker/trajectory-reassembler.js.map +1 -0
  132. package/dist/docker/trajectory-tap.d.ts +100 -0
  133. package/dist/docker/trajectory-tap.js +410 -0
  134. package/dist/docker/trajectory-tap.js.map +1 -0
  135. package/dist/docker/trajectory-types.d.ts +187 -0
  136. package/dist/docker/trajectory-types.js +43 -0
  137. package/dist/docker/trajectory-types.js.map +1 -0
  138. package/dist/docker/types.d.ts +116 -12
  139. package/dist/doctor/checks.d.ts +39 -6
  140. package/dist/doctor/checks.js +129 -25
  141. package/dist/doctor/checks.js.map +1 -1
  142. package/dist/doctor/doctor-command.js +39 -9
  143. package/dist/doctor/doctor-command.js.map +1 -1
  144. package/dist/doctor/mcp-liveness.d.ts +1 -1
  145. package/dist/doctor/mcp-liveness.js +12 -4
  146. package/dist/doctor/mcp-liveness.js.map +1 -1
  147. package/dist/escalation/escalation-watcher.js +6 -0
  148. package/dist/escalation/escalation-watcher.js.map +1 -1
  149. package/dist/{mux → escalation}/trusted-input.d.ts +7 -4
  150. package/dist/{mux → escalation}/trusted-input.js +8 -5
  151. package/dist/escalation/trusted-input.js.map +1 -0
  152. package/dist/hash.js.map +1 -1
  153. package/dist/index.js +65 -16
  154. package/dist/index.js.map +1 -1
  155. package/dist/mux/mux-app.d.ts +9 -0
  156. package/dist/mux/mux-app.js +18 -16
  157. package/dist/mux/mux-app.js.map +1 -1
  158. package/dist/mux/mux-command.js +26 -8
  159. package/dist/mux/mux-command.js.map +1 -1
  160. package/dist/mux/mux-input-handler.d.ts +26 -2
  161. package/dist/mux/mux-input-handler.js +93 -11
  162. package/dist/mux/mux-input-handler.js.map +1 -1
  163. package/dist/mux/mux-renderer.d.ts +2 -1
  164. package/dist/mux/mux-renderer.js +67 -0
  165. package/dist/mux/mux-renderer.js.map +1 -1
  166. package/dist/mux/provider-profile-snapshot.d.ts +44 -0
  167. package/dist/mux/provider-profile-snapshot.js +56 -0
  168. package/dist/mux/provider-profile-snapshot.js.map +1 -0
  169. package/dist/mux/types.d.ts +3 -2
  170. package/dist/mux/types.js +6 -2
  171. package/dist/mux/types.js.map +1 -1
  172. package/dist/observe/observe-command.js +112 -125
  173. package/dist/observe/observe-command.js.map +1 -1
  174. package/dist/persona/broad-policy-validator.d.ts +95 -0
  175. package/dist/persona/broad-policy-validator.js +171 -0
  176. package/dist/persona/broad-policy-validator.js.map +1 -0
  177. package/dist/persona/compile-persona-policy.d.ts +54 -2
  178. package/dist/persona/compile-persona-policy.js +29 -4
  179. package/dist/persona/compile-persona-policy.js.map +1 -1
  180. package/dist/persona/event-bus-progress-reporter.d.ts +57 -0
  181. package/dist/persona/event-bus-progress-reporter.js +66 -0
  182. package/dist/persona/event-bus-progress-reporter.js.map +1 -0
  183. package/dist/persona/persona-command.js +46 -76
  184. package/dist/persona/persona-command.js.map +1 -1
  185. package/dist/persona/persona-compile-orchestrator.d.ts +101 -0
  186. package/dist/persona/persona-compile-orchestrator.js +504 -0
  187. package/dist/persona/persona-compile-orchestrator.js.map +1 -0
  188. package/dist/persona/persona-service.d.ts +137 -0
  189. package/dist/persona/persona-service.js +263 -0
  190. package/dist/persona/persona-service.js.map +1 -0
  191. package/dist/persona/policy-mutation-audit.d.ts +109 -0
  192. package/dist/persona/policy-mutation-audit.js +179 -0
  193. package/dist/persona/policy-mutation-audit.js.map +1 -0
  194. package/dist/persona/rule-delta.d.ts +30 -0
  195. package/dist/persona/rule-delta.js +82 -0
  196. package/dist/persona/rule-delta.js.map +1 -0
  197. package/dist/persona/types.d.ts +12 -0
  198. package/dist/pipeline/annotate.js +10 -2
  199. package/dist/pipeline/annotate.js.map +1 -1
  200. package/dist/pipeline/compile.js +14 -2
  201. package/dist/pipeline/compile.js.map +1 -1
  202. package/dist/pipeline/constitution-compiler.js.map +1 -1
  203. package/dist/pipeline/generate-with-repair.js.map +1 -1
  204. package/dist/pipeline/pipeline-runner.d.ts +63 -4
  205. package/dist/pipeline/pipeline-runner.js +159 -50
  206. package/dist/pipeline/pipeline-runner.js.map +1 -1
  207. package/dist/pipeline/pipeline-shared.d.ts +32 -3
  208. package/dist/pipeline/pipeline-shared.js +76 -8
  209. package/dist/pipeline/pipeline-shared.js.map +1 -1
  210. package/dist/pipeline/refresh-lists.js +3 -5
  211. package/dist/pipeline/refresh-lists.js.map +1 -1
  212. package/dist/pipeline/scenario-schema-validator.js.map +1 -1
  213. package/dist/{mux → pty}/pty-bridge.d.ts +41 -3
  214. package/dist/{mux → pty}/pty-bridge.js +71 -18
  215. package/dist/pty/pty-bridge.js.map +1 -0
  216. package/dist/pty/resolve-ironcurtain-bin.d.ts +17 -0
  217. package/dist/pty/resolve-ironcurtain-bin.js +22 -0
  218. package/dist/pty/resolve-ironcurtain-bin.js.map +1 -0
  219. package/dist/sandbox/index.d.ts +19 -4
  220. package/dist/sandbox/index.js +31 -18
  221. package/dist/sandbox/index.js.map +1 -1
  222. package/dist/servers/fetch-server.js +2 -0
  223. package/dist/servers/fetch-server.js.map +1 -1
  224. package/dist/session/index.d.ts +1 -1
  225. package/dist/session/index.js +20 -2
  226. package/dist/session/index.js.map +1 -1
  227. package/dist/session/preflight.d.ts +20 -35
  228. package/dist/session/preflight.js +92 -109
  229. package/dist/session/preflight.js.map +1 -1
  230. package/dist/session/resource-budget-tracker.js +8 -0
  231. package/dist/session/resource-budget-tracker.js.map +1 -1
  232. package/dist/session/session-manager.d.ts +7 -0
  233. package/dist/session/session-manager.js.map +1 -1
  234. package/dist/session/types.d.ts +23 -0
  235. package/dist/signal/bot-command.js +6 -1
  236. package/dist/signal/bot-command.js.map +1 -1
  237. package/dist/signal/setup-signal.js +1 -1
  238. package/dist/signal/setup-signal.js.map +1 -1
  239. package/dist/signal/signal-bot-daemon.d.ts +1 -0
  240. package/dist/signal/signal-bot-daemon.js +9 -2
  241. package/dist/signal/signal-bot-daemon.js.map +1 -1
  242. package/dist/signal/signal-container.d.ts +3 -3
  243. package/dist/signal/signal-container.js +2 -2
  244. package/dist/signal/signal-container.js.map +1 -1
  245. package/dist/trusted-process/call-circuit-breaker.d.ts +6 -0
  246. package/dist/trusted-process/call-circuit-breaker.js +22 -3
  247. package/dist/trusted-process/call-circuit-breaker.js.map +1 -1
  248. package/dist/trusted-process/container-command.d.ts +35 -0
  249. package/dist/trusted-process/container-command.js +49 -0
  250. package/dist/trusted-process/container-command.js.map +1 -0
  251. package/dist/trusted-process/mcp-error-utils.js +4 -1
  252. package/dist/trusted-process/mcp-error-utils.js.map +1 -1
  253. package/dist/trusted-process/mcp-proxy-server.js +37 -3
  254. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  255. package/dist/trusted-process/policy-engine.js +13 -2
  256. package/dist/trusted-process/policy-engine.js.map +1 -1
  257. package/dist/trusted-process/tcp-server-transport.d.ts +19 -1
  258. package/dist/trusted-process/tcp-server-transport.js +56 -10
  259. package/dist/trusted-process/tcp-server-transport.js.map +1 -1
  260. package/dist/trusted-process/tool-call-coordinator.d.ts +22 -8
  261. package/dist/trusted-process/tool-call-coordinator.js +149 -65
  262. package/dist/trusted-process/tool-call-coordinator.js.map +1 -1
  263. package/dist/trusted-process/tool-call-pipeline.d.ts +8 -0
  264. package/dist/trusted-process/tool-call-pipeline.js +68 -34
  265. package/dist/trusted-process/tool-call-pipeline.js.map +1 -1
  266. package/dist/util/atomic-write.d.ts +31 -0
  267. package/dist/util/atomic-write.js +43 -0
  268. package/dist/util/atomic-write.js.map +1 -0
  269. package/dist/utils/preflight-checks.js +1 -3
  270. package/dist/utils/preflight-checks.js.map +1 -1
  271. package/dist/utils/stdio-guard.d.ts +1 -0
  272. package/dist/utils/stdio-guard.js +27 -0
  273. package/dist/utils/stdio-guard.js.map +1 -0
  274. package/dist/web-ui/__tests__/config-dispatch.test.d.ts +13 -0
  275. package/dist/web-ui/__tests__/config-dispatch.test.js +383 -0
  276. package/dist/web-ui/__tests__/config-dispatch.test.js.map +1 -0
  277. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js +6 -0
  278. package/dist/web-ui/__tests__/json-rpc-dispatch.test.js.map +1 -1
  279. package/dist/web-ui/conn-registry.d.ts +14 -0
  280. package/dist/web-ui/conn-registry.js +25 -0
  281. package/dist/web-ui/conn-registry.js.map +1 -0
  282. package/dist/web-ui/dispatch/config-dispatch.d.ts +15 -0
  283. package/dist/web-ui/dispatch/config-dispatch.js +277 -0
  284. package/dist/web-ui/dispatch/config-dispatch.js.map +1 -0
  285. package/dist/web-ui/dispatch/escalation-dispatch.js +11 -2
  286. package/dist/web-ui/dispatch/escalation-dispatch.js.map +1 -1
  287. package/dist/web-ui/dispatch/persona-dispatch.d.ts +46 -2
  288. package/dist/web-ui/dispatch/persona-dispatch.js +209 -56
  289. package/dist/web-ui/dispatch/persona-dispatch.js.map +1 -1
  290. package/dist/web-ui/dispatch/pty-dispatch.d.ts +15 -0
  291. package/dist/web-ui/dispatch/pty-dispatch.js +66 -0
  292. package/dist/web-ui/dispatch/pty-dispatch.js.map +1 -0
  293. package/dist/web-ui/dispatch/session-dispatch.js +89 -6
  294. package/dist/web-ui/dispatch/session-dispatch.js.map +1 -1
  295. package/dist/web-ui/dispatch/types.d.ts +35 -0
  296. package/dist/web-ui/dispatch/types.js +42 -1
  297. package/dist/web-ui/dispatch/types.js.map +1 -1
  298. package/dist/web-ui/dispatch/workflow-dispatch.js +116 -5
  299. package/dist/web-ui/dispatch/workflow-dispatch.js.map +1 -1
  300. package/dist/web-ui/json-rpc-dispatch.js +4 -1
  301. package/dist/web-ui/json-rpc-dispatch.js.map +1 -1
  302. package/dist/web-ui/pty-session-manager.d.ts +234 -0
  303. package/dist/web-ui/pty-session-manager.js +599 -0
  304. package/dist/web-ui/pty-session-manager.js.map +1 -0
  305. package/dist/web-ui/web-event-bus.d.ts +34 -1
  306. package/dist/web-ui/web-event-bus.js.map +1 -1
  307. package/dist/web-ui/web-session-transport.js.map +1 -1
  308. package/dist/web-ui/web-ui-server.d.ts +22 -0
  309. package/dist/web-ui/web-ui-server.js +41 -0
  310. package/dist/web-ui/web-ui-server.js.map +1 -1
  311. package/dist/web-ui/web-ui-types.d.ts +212 -5
  312. package/dist/web-ui/web-ui-types.js.map +1 -1
  313. package/dist/web-ui-static/assets/index-CCVI4nEK.css +1 -0
  314. package/dist/web-ui-static/assets/index-Dm012gvY.js +114 -0
  315. package/dist/web-ui-static/index.html +2 -2
  316. package/dist/workflow/cli-shared.d.ts +62 -0
  317. package/dist/workflow/cli-shared.js +117 -0
  318. package/dist/workflow/cli-shared.js.map +1 -0
  319. package/dist/workflow/cli-support.js +0 -2
  320. package/dist/workflow/cli-support.js.map +1 -1
  321. package/dist/workflow/container-snapshots.d.ts +29 -0
  322. package/dist/workflow/container-snapshots.js +137 -0
  323. package/dist/workflow/container-snapshots.js.map +1 -0
  324. package/dist/workflow/daemon-gate-commands.d.ts +21 -0
  325. package/dist/workflow/daemon-gate-commands.js +595 -0
  326. package/dist/workflow/daemon-gate-commands.js.map +1 -0
  327. package/dist/workflow/discovery.d.ts +29 -0
  328. package/dist/workflow/discovery.js +67 -7
  329. package/dist/workflow/discovery.js.map +1 -1
  330. package/dist/workflow/lane-template.d.ts +89 -0
  331. package/dist/workflow/lane-template.js +141 -0
  332. package/dist/workflow/lane-template.js.map +1 -0
  333. package/dist/workflow/lint.d.ts +7 -1
  334. package/dist/workflow/lint.js +186 -3
  335. package/dist/workflow/lint.js.map +1 -1
  336. package/dist/workflow/machine-builder.d.ts +90 -2
  337. package/dist/workflow/machine-builder.js +222 -29
  338. package/dist/workflow/machine-builder.js.map +1 -1
  339. package/dist/workflow/message-log.d.ts +15 -1
  340. package/dist/workflow/message-log.js.map +1 -1
  341. package/dist/workflow/orchestrator.d.ts +505 -1
  342. package/dist/workflow/orchestrator.js +1725 -69
  343. package/dist/workflow/orchestrator.js.map +1 -1
  344. package/dist/workflow/prompt-builder.js +2 -1
  345. package/dist/workflow/prompt-builder.js.map +1 -1
  346. package/dist/workflow/run-state-command.js +1 -1
  347. package/dist/workflow/run-state-command.js.map +1 -1
  348. package/dist/workflow/shutdown-signals.d.ts +7 -0
  349. package/dist/workflow/shutdown-signals.js +40 -0
  350. package/dist/workflow/shutdown-signals.js.map +1 -0
  351. package/dist/workflow/status-parser.js +19 -4
  352. package/dist/workflow/status-parser.js.map +1 -1
  353. package/dist/workflow/types.d.ts +123 -23
  354. package/dist/workflow/types.js +2 -0
  355. package/dist/workflow/types.js.map +1 -1
  356. package/dist/workflow/validate.d.ts +23 -1
  357. package/dist/workflow/validate.js +311 -22
  358. package/dist/workflow/validate.js.map +1 -1
  359. package/dist/workflow/workflow-command.d.ts +0 -17
  360. package/dist/workflow/workflow-command.js +118 -94
  361. package/dist/workflow/workflow-command.js.map +1 -1
  362. package/dist/workflow/workflow-discovery.d.ts +2 -0
  363. package/dist/workflow/workflow-discovery.js +22 -1
  364. package/dist/workflow/workflow-discovery.js.map +1 -1
  365. package/dist/workflow/workflow-manager.d.ts +34 -0
  366. package/dist/workflow/workflow-manager.js +52 -1
  367. package/dist/workflow/workflow-manager.js.map +1 -1
  368. package/dist/workflow/workflows/design-and-code/README.md +50 -0
  369. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  370. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  371. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  372. package/dist/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  373. package/dist/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  374. package/dist/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  375. package/dist/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  376. package/dist/workflow/workflows/evolve/README.md +66 -0
  377. package/dist/workflow/workflows/evolve/scripts/LICENSE +201 -0
  378. package/dist/workflow/workflows/evolve/scripts/README.md +314 -0
  379. package/dist/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  380. package/dist/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  381. package/dist/workflow/workflows/evolve/scripts/evolve-db +16 -0
  382. package/dist/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  383. package/dist/workflow/workflows/evolve/scripts/evolve-files +16 -0
  384. package/dist/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  385. package/dist/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  386. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  387. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  388. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  389. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  390. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  391. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  392. package/dist/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  393. package/dist/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  394. package/dist/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  395. package/dist/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  396. package/dist/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  397. package/dist/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  398. package/dist/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  399. package/dist/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  400. package/dist/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  401. package/dist/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  402. package/dist/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  403. package/dist/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  404. package/dist/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  405. package/dist/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  406. package/dist/workflow/workflows/evolve/workflow.yaml +646 -0
  407. package/dist/workflow/workflows/test-email-summary/workflow.yaml +1 -0
  408. package/dist/workflow/workflows/vuln-discovery/README.md +54 -0
  409. package/dist/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +6 -1
  410. package/dist/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +1 -0
  411. package/dist/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +4 -0
  412. package/dist/workflow/workflows/vuln-discovery/workflow.yaml +13 -8
  413. package/docker/Dockerfile.base +22 -0
  414. package/docker/Dockerfile.base.arm64 +18 -0
  415. package/docker/Dockerfile.claude-code +3 -1
  416. package/docker/Dockerfile.codex +17 -0
  417. package/docker/Dockerfile.goose +3 -1
  418. package/docker/entrypoint-claude-code.sh +28 -45
  419. package/docker/entrypoint-codex.sh +42 -0
  420. package/docker/entrypoint-goose.sh +5 -24
  421. package/docker/entrypoint-uid-remap.sh +93 -0
  422. package/package.json +39 -30
  423. package/src/workflow/workflows/design-and-code/README.md +50 -0
  424. package/src/workflow/workflows/deterministic-eval-smoke/scripts/format_report.js +29 -0
  425. package/src/workflow/workflows/deterministic-eval-smoke/scripts/package.json +5 -0
  426. package/src/workflow/workflows/deterministic-eval-smoke/scripts/requirements.txt +1 -0
  427. package/src/workflow/workflows/deterministic-eval-smoke/scripts/run_eval.py +41 -0
  428. package/src/workflow/workflows/deterministic-eval-smoke/workflow.yaml +57 -0
  429. package/src/workflow/workflows/deterministic-verdict-smoke/scripts/classify.py +18 -0
  430. package/src/workflow/workflows/deterministic-verdict-smoke/workflow.yaml +51 -0
  431. package/src/workflow/workflows/evolve/README.md +66 -0
  432. package/src/workflow/workflows/evolve/scripts/LICENSE +201 -0
  433. package/src/workflow/workflows/evolve/scripts/README.md +314 -0
  434. package/src/workflow/workflows/evolve/scripts/evolve-brief +16 -0
  435. package/src/workflow/workflows/evolve/scripts/evolve-cognition +16 -0
  436. package/src/workflow/workflows/evolve/scripts/evolve-db +16 -0
  437. package/src/workflow/workflows/evolve/scripts/evolve-eval +16 -0
  438. package/src/workflow/workflows/evolve/scripts/evolve-files +16 -0
  439. package/src/workflow/workflows/evolve/scripts/evolve-summary +16 -0
  440. package/src/workflow/workflows/evolve/scripts/evolve_core/__init__.py +14 -0
  441. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/__init__.py +17 -0
  442. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/base.py +21 -0
  443. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/factory.py +90 -0
  444. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/greedy.py +17 -0
  445. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/island.py +316 -0
  446. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/random.py +18 -0
  447. package/src/workflow/workflows/evolve/scripts/evolve_core/algorithms/ucb1.py +55 -0
  448. package/src/workflow/workflows/evolve/scripts/evolve_core/best_snapshot.py +62 -0
  449. package/src/workflow/workflows/evolve/scripts/evolve_core/cli.py +752 -0
  450. package/src/workflow/workflows/evolve/scripts/evolve_core/cognition.py +127 -0
  451. package/src/workflow/workflows/evolve/scripts/evolve_core/database.py +218 -0
  452. package/src/workflow/workflows/evolve/scripts/evolve_core/diff.py +57 -0
  453. package/src/workflow/workflows/evolve/scripts/evolve_core/embedding.py +84 -0
  454. package/src/workflow/workflows/evolve/scripts/evolve_core/file_lock.py +45 -0
  455. package/src/workflow/workflows/evolve/scripts/evolve_core/run_state.py +343 -0
  456. package/src/workflow/workflows/evolve/scripts/evolve_core/sampling_config.py +173 -0
  457. package/src/workflow/workflows/evolve/scripts/evolve_core/structures.py +93 -0
  458. package/src/workflow/workflows/evolve/scripts/evolve_core/vector_index.py +191 -0
  459. package/src/workflow/workflows/evolve/scripts/evolve_result.py +1503 -0
  460. package/src/workflow/workflows/evolve/scripts/requirements.txt +2 -0
  461. package/src/workflow/workflows/evolve/workflow.yaml +646 -0
  462. package/src/workflow/workflows/test-email-summary/workflow.yaml +1 -0
  463. package/src/workflow/workflows/vuln-discovery/README.md +54 -0
  464. package/src/workflow/workflows/vuln-discovery/skills/harness-design-fuzzing/SKILL.md +6 -1
  465. package/src/workflow/workflows/vuln-discovery/skills/memory-safety-c-cpp/SKILL.md +1 -0
  466. package/src/workflow/workflows/vuln-discovery/skills/vulnerability-triage/SKILL.md +4 -0
  467. package/src/workflow/workflows/vuln-discovery/workflow.yaml +13 -8
  468. package/dist/mux/pty-bridge.js.map +0 -1
  469. package/dist/mux/trusted-input.js.map +0 -1
  470. package/dist/web-ui-static/assets/index-BFO5z1js.js +0 -76
  471. package/dist/web-ui-static/assets/index-CCUwrkIL.css +0 -1
  472. package/packages/web-ui/README.md +0 -113
@@ -0,0 +1,90 @@
1
+ """Factory for sampler implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import importlib.util
7
+ from pathlib import Path
8
+ import sys
9
+
10
+ from .base import BaseSampler
11
+ from .greedy import GreedySampler
12
+ from .island import IslandSampler
13
+ from .random import RandomSampler
14
+ from .ucb1 import UCB1Sampler
15
+
16
+
17
+ def get_sampler(algorithm: str, **kwargs) -> BaseSampler:
18
+ kwargs = {key: value for key, value in kwargs.items() if value is not None}
19
+ if algorithm == "custom":
20
+ custom_path = str(kwargs.pop("custom_sampler_path", "") or "").strip()
21
+ custom_class = str(kwargs.pop("custom_sampler_class", "") or "").strip()
22
+ custom_search_paths = kwargs.pop("custom_sampler_search_paths", None)
23
+ sampler_class = load_custom_sampler_class(
24
+ custom_path,
25
+ custom_class,
26
+ search_paths=custom_search_paths,
27
+ )
28
+ sampler = sampler_class(**kwargs)
29
+ if not callable(getattr(sampler, "sample", None)):
30
+ raise ValueError(
31
+ f"Custom sampler '{custom_class}' must define a callable sample(nodes, n) method."
32
+ )
33
+ return sampler
34
+
35
+ samplers = {
36
+ "greedy": GreedySampler,
37
+ "island": IslandSampler,
38
+ "random": RandomSampler,
39
+ "ucb1": UCB1Sampler,
40
+ }
41
+ if algorithm not in samplers:
42
+ raise ValueError(
43
+ f"Unknown sampling algorithm: {algorithm}. Available: {sorted(samplers)}"
44
+ )
45
+ return samplers[algorithm](**kwargs)
46
+
47
+
48
+ def load_custom_sampler_class(
49
+ custom_path: str,
50
+ custom_class: str,
51
+ search_paths: list[str] | None = None,
52
+ ):
53
+ if not custom_path or not custom_class:
54
+ raise ValueError(
55
+ "Custom sampling requires both custom_sampler_path and custom_sampler_class."
56
+ )
57
+
58
+ path = Path(custom_path)
59
+ if not path.exists():
60
+ raise ValueError(f"Custom sampler path does not exist: {path}")
61
+
62
+ module_name = "evolve_custom_sampler_" + hashlib.sha1(str(path).encode("utf-8")).hexdigest()
63
+ spec = importlib.util.spec_from_file_location(module_name, path)
64
+ if spec is None or spec.loader is None:
65
+ raise ValueError(f"Unable to load custom sampler module from: {path}")
66
+
67
+ module = importlib.util.module_from_spec(spec)
68
+ effective_search_paths: list[str] = []
69
+ for candidate in [str(path.parent), *(search_paths or [])]:
70
+ if candidate and candidate not in effective_search_paths:
71
+ effective_search_paths.append(candidate)
72
+
73
+ inserted_paths: list[str] = []
74
+ try:
75
+ for candidate in reversed(effective_search_paths):
76
+ if candidate not in sys.path:
77
+ sys.path.insert(0, candidate)
78
+ inserted_paths.append(candidate)
79
+ spec.loader.exec_module(module)
80
+ finally:
81
+ for candidate in inserted_paths:
82
+ if candidate in sys.path:
83
+ sys.path.remove(candidate)
84
+
85
+ sampler_class = getattr(module, custom_class, None)
86
+ if sampler_class is None:
87
+ raise ValueError(
88
+ f"Custom sampler class '{custom_class}' was not found in: {path}"
89
+ )
90
+ return sampler_class
@@ -0,0 +1,17 @@
1
+ """Greedy sampling strategy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List, TYPE_CHECKING
6
+
7
+ from .base import BaseSampler
8
+
9
+ if TYPE_CHECKING:
10
+ from ..structures import Node
11
+
12
+
13
+ class GreedySampler(BaseSampler):
14
+ def sample(self, nodes: List["Node"], n: int) -> List["Node"]:
15
+ if not nodes:
16
+ return []
17
+ return sorted(nodes, key=lambda node: node.score, reverse=True)[:n]
@@ -0,0 +1,316 @@
1
+ """Island-based sampler with optional diversity features."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ast
6
+ import random
7
+ import time
8
+ from typing import Any, Dict, List, Optional, Set, Tuple, TYPE_CHECKING
9
+
10
+ from .base import BaseSampler
11
+
12
+ if TYPE_CHECKING:
13
+ from ..structures import Node
14
+
15
+
16
+ class IslandSampler(BaseSampler):
17
+ def __init__(
18
+ self,
19
+ num_islands: int = 5,
20
+ migration_interval: int = 10,
21
+ migration_rate: float = 0.1,
22
+ exploration_ratio: float = 0.2,
23
+ exploitation_ratio: float = 0.3,
24
+ c: float = 1.414,
25
+ feature_dimensions: Optional[List[str]] = None,
26
+ feature_bins: int = 10,
27
+ ):
28
+ self.num_islands = num_islands
29
+ self.migration_interval = migration_interval
30
+ self.migration_rate = migration_rate
31
+ self.exploration_ratio = exploration_ratio
32
+ self.exploitation_ratio = exploitation_ratio
33
+ self.c = c
34
+ self.feature_dimensions = feature_dimensions or []
35
+ self.feature_bins = feature_bins
36
+
37
+ self.islands: List[Set[int]] = [set() for _ in range(num_islands)]
38
+ self.island_generations: List[int] = [0] * num_islands
39
+ self.island_best_nodes: List[Optional[int]] = [None] * num_islands
40
+ self.island_feature_maps: List[Dict[Tuple[int, ...], int]] = [
41
+ {} for _ in range(num_islands)
42
+ ]
43
+ self.feature_stats: Dict[str, Dict[str, Any]] = {}
44
+ self.current_island = 0
45
+ self.last_migration_generation = 0
46
+ self.archive: Set[int] = set()
47
+ self.archive_size = 100
48
+ self.diversity_cache: Dict[int, Dict[str, float]] = {}
49
+ self.diversity_cache_size = 1000
50
+ self.diversity_reference_set: List[str] = []
51
+ self.diversity_reference_size = 20
52
+ self.all_nodes: Dict[int, "Node"] = {}
53
+
54
+ def sample(self, nodes: List["Node"], n: int) -> List["Node"]:
55
+ if not nodes:
56
+ return []
57
+
58
+ for node in nodes:
59
+ if node.id is not None:
60
+ self.all_nodes[node.id] = node
61
+
62
+ if self._should_migrate():
63
+ self._migrate(nodes)
64
+
65
+ island_id = self.current_island % self.num_islands
66
+ island_nodes = self._get_island_nodes(island_id, nodes) or nodes
67
+ self.island_generations[island_id] += 1
68
+ self.current_island = (self.current_island + 1) % self.num_islands
69
+
70
+ selected: List["Node"] = []
71
+ while len(selected) < min(n, len(island_nodes)):
72
+ roll = random.random()
73
+ if roll < self.exploration_ratio:
74
+ candidate = self._sample_random(island_nodes)
75
+ elif roll < self.exploration_ratio + self.exploitation_ratio:
76
+ candidate = self._sample_from_archive(nodes) or self._sample_weighted(
77
+ island_nodes
78
+ )
79
+ else:
80
+ candidate = self._sample_weighted(island_nodes)
81
+
82
+ if candidate is None or candidate in selected:
83
+ continue
84
+ candidate.visit_count += 1
85
+ selected.append(candidate)
86
+
87
+ return selected
88
+
89
+ def on_node_added(self, node: "Node") -> None:
90
+ if node.id is None:
91
+ return
92
+ self.all_nodes[node.id] = node
93
+ island_id = node.meta_info.get("island", self.current_island) % self.num_islands
94
+ node.meta_info["island"] = island_id
95
+ self.islands[island_id].add(node.id)
96
+ self._update_archive(node)
97
+ if self.feature_dimensions:
98
+ feature_coords = self._calculate_feature_coords(node)
99
+ if feature_coords is not None:
100
+ self.island_feature_maps[island_id][tuple(feature_coords)] = node.id
101
+
102
+ def on_node_removed(self, node: "Node") -> None:
103
+ if node.id is None:
104
+ return
105
+ self.all_nodes.pop(node.id, None)
106
+ island_id = node.meta_info.get("island")
107
+ if island_id is not None and island_id < len(self.islands):
108
+ self.islands[island_id].discard(node.id)
109
+ self.archive.discard(node.id)
110
+
111
+ def get_state(self) -> Dict[str, Any]:
112
+ return {
113
+ "archive": list(self.archive),
114
+ "current_island": self.current_island,
115
+ "diversity_cache": {str(key): value for key, value in self.diversity_cache.items()},
116
+ "diversity_reference_set": self.diversity_reference_set,
117
+ "feature_stats": self.feature_stats,
118
+ "island_best_nodes": self.island_best_nodes,
119
+ "island_feature_maps": [
120
+ {str(key): value for key, value in feature_map.items()}
121
+ for feature_map in self.island_feature_maps
122
+ ],
123
+ "island_generations": self.island_generations,
124
+ "last_migration_generation": self.last_migration_generation,
125
+ }
126
+
127
+ def load_state(self, state: Dict[str, Any]) -> None:
128
+ self.archive = set(state.get("archive", []))
129
+ self.current_island = state.get("current_island", 0) % self.num_islands
130
+ self.diversity_cache = {
131
+ int(key): value for key, value in state.get("diversity_cache", {}).items()
132
+ }
133
+ self.diversity_reference_set = state.get("diversity_reference_set", [])
134
+ self.feature_stats = state.get("feature_stats", {})
135
+ self.island_best_nodes = state.get("island_best_nodes", [None] * self.num_islands)
136
+ self.island_generations = state.get("island_generations", [0] * self.num_islands)
137
+ self.last_migration_generation = state.get("last_migration_generation", 0)
138
+
139
+ feature_maps = []
140
+ for raw_feature_map in state.get("island_feature_maps", []):
141
+ feature_maps.append(
142
+ {ast.literal_eval(key): value for key, value in raw_feature_map.items()}
143
+ )
144
+ if feature_maps:
145
+ self.island_feature_maps = feature_maps
146
+
147
+ def rebuild_from_nodes(self, nodes: List["Node"]) -> None:
148
+ self.islands = [set() for _ in range(self.num_islands)]
149
+ self.all_nodes = {node.id: node for node in nodes if node.id is not None}
150
+ self.island_feature_maps = [{} for _ in range(self.num_islands)]
151
+
152
+ for node in nodes:
153
+ if node.id is None:
154
+ continue
155
+ island_id = node.meta_info.get("island", 0) % self.num_islands
156
+ self.islands[island_id].add(node.id)
157
+ if self.feature_dimensions:
158
+ coords = self._calculate_feature_coords(node)
159
+ if coords is not None:
160
+ self.island_feature_maps[island_id][tuple(coords)] = node.id
161
+
162
+ def get_island_stats(self, nodes: List["Node"]) -> Dict[str, Any]:
163
+ populations = []
164
+ for island_id in range(self.num_islands):
165
+ island_nodes = self._get_island_nodes(island_id, nodes)
166
+ populations.append(
167
+ {
168
+ "island_id": island_id,
169
+ "size": len(island_nodes),
170
+ "best_score": max((node.score for node in island_nodes), default=0.0),
171
+ }
172
+ )
173
+ return {
174
+ "archive_size": len(self.archive),
175
+ "current_island": self.current_island,
176
+ "island_populations": populations,
177
+ "num_islands": self.num_islands,
178
+ }
179
+
180
+ def reset(self) -> None:
181
+ self.islands = [set() for _ in range(self.num_islands)]
182
+ self.island_generations = [0] * self.num_islands
183
+ self.island_best_nodes = [None] * self.num_islands
184
+ self.island_feature_maps = [{} for _ in range(self.num_islands)]
185
+ self.feature_stats.clear()
186
+ self.current_island = 0
187
+ self.last_migration_generation = 0
188
+ self.archive.clear()
189
+ self.diversity_cache.clear()
190
+ self.diversity_reference_set = []
191
+
192
+ def _get_island_nodes(self, island_id: int, nodes: List["Node"]) -> List["Node"]:
193
+ return [node for node in nodes if node.id in self.islands[island_id]]
194
+
195
+ @staticmethod
196
+ def _sample_random(nodes: List["Node"]) -> Optional["Node"]:
197
+ if not nodes:
198
+ return None
199
+ return random.choice(nodes)
200
+
201
+ @staticmethod
202
+ def _sample_weighted(nodes: List["Node"]) -> Optional["Node"]:
203
+ if not nodes:
204
+ return None
205
+ weights = [max(node.score, 0.001) for node in nodes]
206
+ return random.choices(nodes, weights=weights, k=1)[0]
207
+
208
+ def _sample_from_archive(self, nodes: List["Node"]) -> Optional["Node"]:
209
+ archive_nodes = [node for node in nodes if node.id in self.archive]
210
+ if not archive_nodes:
211
+ return None
212
+ return random.choice(archive_nodes)
213
+
214
+ def _update_archive(self, node: "Node") -> None:
215
+ if node.id is None:
216
+ return
217
+ self.archive.add(node.id)
218
+ if len(self.archive) > self.archive_size:
219
+ self.archive = set(sorted(self.archive)[-self.archive_size :])
220
+
221
+ def _calculate_feature_coords(self, node: "Node") -> Optional[List[int]]:
222
+ if not self.feature_dimensions:
223
+ return None
224
+
225
+ coords: List[int] = []
226
+ for feature in self.feature_dimensions:
227
+ if feature == "complexity":
228
+ value = float(len(node.code))
229
+ elif feature == "diversity":
230
+ value = self._get_cached_diversity(node)
231
+ else:
232
+ raw_value = node.results.get(feature)
233
+ if not isinstance(raw_value, (int, float)):
234
+ return None
235
+ value = float(raw_value)
236
+
237
+ self._update_feature_stats(feature, value)
238
+ scaled = self._scale_feature_value(feature, value)
239
+ coords.append(max(0, min(self.feature_bins - 1, int(scaled * self.feature_bins))))
240
+
241
+ return coords
242
+
243
+ def _update_feature_stats(self, feature: str, value: float) -> None:
244
+ stats = self.feature_stats.setdefault(feature, {"min": value, "max": value})
245
+ stats["min"] = min(stats["min"], value)
246
+ stats["max"] = max(stats["max"], value)
247
+
248
+ def _scale_feature_value(self, feature: str, value: float) -> float:
249
+ stats = self.feature_stats.get(feature)
250
+ if not stats:
251
+ return 0.5
252
+ if stats["max"] - stats["min"] < 1e-10:
253
+ return 0.5
254
+ return max(0.0, min(1.0, (value - stats["min"]) / (stats["max"] - stats["min"])))
255
+
256
+ def _get_cached_diversity(self, node: "Node") -> float:
257
+ if not node.code:
258
+ return 0.0
259
+
260
+ code_hash = hash(node.code)
261
+ cached = self.diversity_cache.get(code_hash)
262
+ if cached:
263
+ return cached["value"]
264
+
265
+ if (
266
+ not self.diversity_reference_set
267
+ or len(self.diversity_reference_set) < self.diversity_reference_size
268
+ ):
269
+ self._update_diversity_reference_set()
270
+
271
+ scores = [
272
+ self._fast_code_diversity(node.code, reference)
273
+ for reference in self.diversity_reference_set
274
+ if reference != node.code
275
+ ]
276
+ diversity = sum(scores) / max(1, len(scores)) if scores else 0.0
277
+
278
+ if len(self.diversity_cache) >= self.diversity_cache_size:
279
+ oldest = min(self.diversity_cache.items(), key=lambda item: item[1]["timestamp"])[0]
280
+ del self.diversity_cache[oldest]
281
+ self.diversity_cache[code_hash] = {"timestamp": time.time(), "value": diversity}
282
+ return diversity
283
+
284
+ def _update_diversity_reference_set(self) -> None:
285
+ all_programs = [node.code for node in self.all_nodes.values() if node.code]
286
+ self.diversity_reference_set = all_programs[: self.diversity_reference_size]
287
+
288
+ @staticmethod
289
+ def _fast_code_diversity(code1: str, code2: str) -> float:
290
+ if code1 == code2:
291
+ return 0.0
292
+ chars = len(set(code1).symmetric_difference(set(code2)))
293
+ return abs(len(code1) - len(code2)) * 0.1 + chars * 0.5
294
+
295
+ def _should_migrate(self) -> bool:
296
+ return (
297
+ max(self.island_generations, default=0) - self.last_migration_generation
298
+ ) >= self.migration_interval
299
+
300
+ def _migrate(self, nodes: List["Node"]) -> None:
301
+ if self.num_islands < 2:
302
+ return
303
+ self.last_migration_generation = max(self.island_generations, default=0)
304
+ for island_id in range(self.num_islands):
305
+ island_nodes = sorted(
306
+ self._get_island_nodes(island_id, nodes),
307
+ key=lambda node: node.score,
308
+ reverse=True,
309
+ )
310
+ if not island_nodes:
311
+ continue
312
+ count = max(1, int(len(island_nodes) * self.migration_rate))
313
+ for node in island_nodes[:count]:
314
+ if node.id is None:
315
+ continue
316
+ self.islands[(island_id + 1) % self.num_islands].add(node.id)
@@ -0,0 +1,18 @@
1
+ """Random sampling strategy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import random
6
+ from typing import List, TYPE_CHECKING
7
+
8
+ from .base import BaseSampler
9
+
10
+ if TYPE_CHECKING:
11
+ from ..structures import Node
12
+
13
+
14
+ class RandomSampler(BaseSampler):
15
+ def sample(self, nodes: List["Node"], n: int) -> List["Node"]:
16
+ if not nodes:
17
+ return []
18
+ return random.sample(nodes, min(n, len(nodes)))
@@ -0,0 +1,55 @@
1
+ """UCB1-based sampling strategy."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import random
7
+ from typing import List, TYPE_CHECKING
8
+
9
+ from .base import BaseSampler
10
+
11
+ if TYPE_CHECKING:
12
+ from ..structures import Node
13
+
14
+
15
+ class UCB1Sampler(BaseSampler):
16
+ def __init__(self, c: float = 1.414):
17
+ self.c = c
18
+
19
+ def sample(self, nodes: List["Node"], n: int) -> List["Node"]:
20
+ if not nodes:
21
+ return []
22
+
23
+ total_visits = sum(node.visit_count for node in nodes)
24
+ if total_visits == 0:
25
+ selected = random.sample(nodes, min(n, len(nodes)))
26
+ for node in selected:
27
+ node.visit_count += 1
28
+ return selected
29
+
30
+ scored_nodes = [node.score for node in nodes if node.visit_count > 0]
31
+ if not scored_nodes:
32
+ selected = random.sample(nodes, min(n, len(nodes)))
33
+ for node in selected:
34
+ node.visit_count += 1
35
+ return selected
36
+
37
+ min_score = min(scored_nodes)
38
+ max_score = max(scored_nodes)
39
+ score_range = max(max_score - min_score, 1.0)
40
+
41
+ ucb_scores = []
42
+ for node in nodes:
43
+ if node.visit_count == 0:
44
+ value = float("inf")
45
+ else:
46
+ normalized = (node.score - min_score) / score_range
47
+ exploration = self.c * math.sqrt(math.log(total_visits) / node.visit_count)
48
+ value = normalized + exploration
49
+ ucb_scores.append((node, value))
50
+
51
+ ucb_scores.sort(key=lambda item: item[1], reverse=True)
52
+ selected = [node for node, _ in ucb_scores[: min(n, len(nodes))]]
53
+ for node in selected:
54
+ node.visit_count += 1
55
+ return selected
@@ -0,0 +1,62 @@
1
+ """Utilities for persisting the best-scoring step outputs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from threading import Lock
8
+ from typing import List, Optional
9
+
10
+ from .structures import Node
11
+
12
+
13
+ class BestSnapshotManager:
14
+ """Maintain a `steps/best` directory with the strongest snapshots."""
15
+
16
+ def __init__(self, steps_dir: Path):
17
+ self.steps_dir = Path(steps_dir)
18
+ self.steps_dir.mkdir(parents=True, exist_ok=True)
19
+ self.best_dir = self.steps_dir / "best"
20
+ self.best_dir.mkdir(parents=True, exist_ok=True)
21
+ self.best_score = float("-inf")
22
+ self._lock = Lock()
23
+
24
+ def init_from_nodes(self, nodes: List[Node]) -> None:
25
+ if nodes:
26
+ self.best_score = max(node.score for node in nodes)
27
+
28
+ def update_if_better(
29
+ self,
30
+ node: Node,
31
+ step_name: str,
32
+ source_step_dir: Optional[Path] = None,
33
+ ) -> bool:
34
+ with self._lock:
35
+ if node.score <= self.best_score:
36
+ return False
37
+ self.best_score = node.score
38
+ self._write_snapshot(node, step_name, source_step_dir)
39
+ return True
40
+
41
+ def _write_snapshot(
42
+ self,
43
+ node: Node,
44
+ step_name: str,
45
+ source_step_dir: Optional[Path],
46
+ ) -> None:
47
+ best_step_dir = self.best_dir / step_name
48
+ best_step_dir.mkdir(parents=True, exist_ok=True)
49
+ (best_step_dir / "code").write_text(node.code or "", encoding="utf-8")
50
+
51
+ results_file = best_step_dir / "results.json"
52
+ source_results_file = source_step_dir / "results.json" if source_step_dir else None
53
+ if source_results_file and source_results_file.exists():
54
+ results_file.write_text(
55
+ source_results_file.read_text(encoding="utf-8"),
56
+ encoding="utf-8",
57
+ )
58
+ else:
59
+ results_file.write_text(
60
+ json.dumps(node.results or {}, ensure_ascii=False, indent=2),
61
+ encoding="utf-8",
62
+ )