@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,127 @@
1
+ """Persistent cognition store with semantic retrieval."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import uuid
7
+ from pathlib import Path
8
+ from threading import RLock
9
+ from typing import Dict, List, Optional, Tuple
10
+
11
+ from .embedding import EmbeddingService
12
+ from .structures import CognitionItem
13
+ from .vector_index import FAISSIndex
14
+
15
+
16
+ class Cognition:
17
+ """Persistent cognition store with embedding-backed retrieval."""
18
+
19
+ def __init__(
20
+ self,
21
+ storage_dir: Path,
22
+ embedding_model: str = "sentence-transformers/all-MiniLM-L6-v2",
23
+ embedding_dim: int = 384,
24
+ retrieval_top_k: int = 5,
25
+ score_threshold: float = 0.3,
26
+ faiss_index_type: str = "IP",
27
+ ):
28
+ self.storage_dir = Path(storage_dir)
29
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
30
+ self.lock = RLock()
31
+ self.retrieval_top_k = retrieval_top_k
32
+ self.score_threshold = score_threshold
33
+ self.items: Dict[str, CognitionItem] = {}
34
+ self.embedding = EmbeddingService(
35
+ model_name=embedding_model,
36
+ dimension=embedding_dim,
37
+ )
38
+ self.faiss = FAISSIndex(
39
+ dimension=embedding_dim,
40
+ index_type=faiss_index_type,
41
+ storage_path=self.storage_dir / "faiss",
42
+ )
43
+ self.str_to_int: Dict[str, int] = {}
44
+ self.int_to_str: Dict[int, str] = {}
45
+ self.next_int_id = 0
46
+ self._load()
47
+
48
+ def add(self, item: CognitionItem) -> str:
49
+ with self.lock:
50
+ if not item.id:
51
+ item.id = str(uuid.uuid4())
52
+ self.items[item.id] = item
53
+ if item.content:
54
+ int_id = self._get_int_id(item.id)
55
+ self.faiss.add(int_id, self.embedding.encode(item.content))
56
+ self._save()
57
+ return item.id
58
+
59
+ def add_batch(self, items: List[CognitionItem]) -> List[str]:
60
+ return [self.add(item) for item in items]
61
+
62
+ def search(self, query: str, top_k: Optional[int] = None) -> List[CognitionItem]:
63
+ return [item for item, _ in self.retrieve(query, top_k=top_k)]
64
+
65
+ def retrieve(
66
+ self,
67
+ query: str,
68
+ top_k: Optional[int] = None,
69
+ score_threshold: Optional[float] = None,
70
+ ) -> List[Tuple[CognitionItem, float]]:
71
+ top_k = top_k or self.retrieval_top_k
72
+ threshold = self.score_threshold if score_threshold is None else score_threshold
73
+ results = self.faiss.search(self.embedding.encode(query), top_k, threshold)
74
+
75
+ enriched: List[Tuple[CognitionItem, float]] = []
76
+ for int_id, score in results:
77
+ item_id = self.int_to_str.get(int_id)
78
+ if item_id and item_id in self.items:
79
+ enriched.append((self.items[item_id], score))
80
+ return enriched
81
+
82
+ def get_all(self) -> List[CognitionItem]:
83
+ return list(self.items.values())
84
+
85
+ def reset(self) -> None:
86
+ with self.lock:
87
+ self.items.clear()
88
+ self.str_to_int.clear()
89
+ self.int_to_str.clear()
90
+ self.next_int_id = 0
91
+ self.faiss.reset()
92
+ data_file = self.storage_dir / "cognition.json"
93
+ if data_file.exists():
94
+ data_file.unlink()
95
+
96
+ def _get_int_id(self, item_id: str) -> int:
97
+ if item_id not in self.str_to_int:
98
+ self.str_to_int[item_id] = self.next_int_id
99
+ self.int_to_str[self.next_int_id] = item_id
100
+ self.next_int_id += 1
101
+ return self.str_to_int[item_id]
102
+
103
+ def _save(self) -> None:
104
+ payload = {
105
+ "items": {item_id: item.to_dict() for item_id, item in self.items.items()},
106
+ "str_to_int": self.str_to_int,
107
+ "int_to_str": {str(key): value for key, value in self.int_to_str.items()},
108
+ "next_int_id": self.next_int_id,
109
+ }
110
+ with open(self.storage_dir / "cognition.json", "w", encoding="utf-8") as handle:
111
+ json.dump(payload, handle, ensure_ascii=False, indent=2)
112
+ self.faiss.save()
113
+
114
+ def _load(self) -> None:
115
+ data_file = self.storage_dir / "cognition.json"
116
+ if not data_file.exists():
117
+ return
118
+ with open(data_file, "r", encoding="utf-8") as handle:
119
+ payload = json.load(handle)
120
+ for item_id, raw_item in payload.get("items", {}).items():
121
+ self.items[item_id] = CognitionItem.from_dict(raw_item)
122
+ self.str_to_int = payload.get("str_to_int", {})
123
+ self.int_to_str = {int(key): value for key, value in payload.get("int_to_str", {}).items()}
124
+ self.next_int_id = payload.get("next_int_id", 0)
125
+
126
+ def __len__(self) -> int:
127
+ return len(self.items)
@@ -0,0 +1,218 @@
1
+ """Persistent node database for skill-driven evolution runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import copy
6
+ import json
7
+ import os
8
+ import tempfile
9
+ import time
10
+ from contextlib import contextmanager
11
+ from pathlib import Path
12
+ from threading import RLock
13
+ from typing import Any, Dict, List, Optional, Tuple
14
+
15
+ from .algorithms import get_sampler
16
+ from .embedding import EmbeddingService
17
+ from .file_lock import InterProcessFileLock
18
+ from .structures import Node
19
+ from .vector_index import FAISSIndex
20
+
21
+
22
+ class Database:
23
+ """Persistent experiment database with sampler pluggability."""
24
+
25
+ def __init__(
26
+ self,
27
+ storage_dir: Path,
28
+ embedding_model: str = "sentence-transformers/all-MiniLM-L6-v2",
29
+ embedding_dim: int = 384,
30
+ sampling_algorithm: str = "ucb1",
31
+ sampling_kwargs: Optional[Dict[str, Any]] = None,
32
+ faiss_index_type: str = "IP",
33
+ max_size: Optional[int] = None,
34
+ ):
35
+ self.storage_dir = Path(storage_dir)
36
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
37
+ self.lock = RLock()
38
+ self.lock_path = self.storage_dir / ".database.lock"
39
+ self.nodes: Dict[int, Node] = {}
40
+ self.next_id = 0
41
+ self.max_size = max_size
42
+ self.embedding_dim = embedding_dim
43
+ self.faiss_index_type = faiss_index_type
44
+ self.embedding = EmbeddingService(
45
+ model_name=embedding_model,
46
+ dimension=embedding_dim,
47
+ )
48
+ self.sampling_algorithm = sampling_algorithm
49
+ self.sampling_kwargs = sampling_kwargs or {}
50
+ self.default_sampler = get_sampler(sampling_algorithm, **self.sampling_kwargs)
51
+ self.faiss = self._build_faiss_index()
52
+
53
+ def sample(self, n: int, algorithm: Optional[str] = None, **kwargs) -> List[Node]:
54
+ with self._database_guard():
55
+ nodes = list(self.nodes.values())
56
+ sampler = (
57
+ get_sampler(algorithm, **kwargs) if algorithm else self.default_sampler
58
+ )
59
+ selected = sampler.sample(nodes, n)
60
+ self._save_locked()
61
+ return self._clone_nodes(selected)
62
+
63
+ def add(self, node: Node) -> int:
64
+ node_id, _ = self.add_with_previous_nodes(node)
65
+ return node_id
66
+
67
+ def add_with_previous_nodes(self, node: Node) -> Tuple[int, List[Node]]:
68
+ with self._database_guard():
69
+ previous_nodes = self._clone_nodes(self.nodes.values())
70
+ if self.max_size is not None and len(self.nodes) >= self.max_size:
71
+ self._remove_worst_node_locked()
72
+
73
+ node.id = self.next_id
74
+ self.next_id += 1
75
+ self.nodes[node.id] = node
76
+ if hasattr(self.default_sampler, "on_node_added"):
77
+ self.default_sampler.on_node_added(node)
78
+
79
+ context_text = node.get_context_text()
80
+ if context_text:
81
+ vector = self.embedding.encode(context_text)
82
+ self.faiss.add(node.id, vector)
83
+
84
+ self._save_locked()
85
+ return node.id, previous_nodes
86
+
87
+ def get_all(self) -> List[Node]:
88
+ with self._database_guard():
89
+ return self._clone_nodes(self.nodes.values())
90
+
91
+ def get(self, node_id: int) -> Optional[Node]:
92
+ with self._database_guard():
93
+ node = self.nodes.get(node_id)
94
+ if node is None:
95
+ return None
96
+ return self._clone_node(node)
97
+
98
+ def remove(self, node_id: int) -> bool:
99
+ with self._database_guard():
100
+ if node_id not in self.nodes:
101
+ return False
102
+ node = self.nodes[node_id]
103
+ if hasattr(self.default_sampler, "on_node_removed"):
104
+ self.default_sampler.on_node_removed(node)
105
+ del self.nodes[node_id]
106
+ self.faiss.remove(node_id)
107
+ self._save_locked()
108
+ return True
109
+
110
+ def reset(self) -> None:
111
+ with self._database_guard(refresh=False):
112
+ if hasattr(self.default_sampler, "reset"):
113
+ self.default_sampler.reset()
114
+ self.nodes.clear()
115
+ self.next_id = 0
116
+ self.faiss.reset()
117
+ data_file = self.storage_dir / "nodes.json"
118
+ if data_file.exists():
119
+ data_file.unlink()
120
+
121
+ def get_sampler_stats(self) -> Optional[Dict[str, Any]]:
122
+ _, sampler_stats = self.snapshot()
123
+ return sampler_stats
124
+
125
+ def snapshot(self) -> Tuple[List[Node], Optional[Dict[str, Any]]]:
126
+ with self._database_guard():
127
+ return self._clone_nodes(self.nodes.values()), self._get_sampler_stats_locked()
128
+
129
+ def _remove_worst_node_locked(self) -> None:
130
+ if not self.nodes:
131
+ return
132
+ worst_id = min(self.nodes, key=lambda node_id: (self.nodes[node_id].score, node_id))
133
+ node = self.nodes[worst_id]
134
+ if hasattr(self.default_sampler, "on_node_removed"):
135
+ self.default_sampler.on_node_removed(node)
136
+ del self.nodes[worst_id]
137
+ self.faiss.remove(worst_id)
138
+
139
+ def _save_locked(self) -> None:
140
+ payload = {
141
+ "next_id": self.next_id,
142
+ "nodes": {str(node_id): node.to_dict() for node_id, node in self.nodes.items()},
143
+ }
144
+ if hasattr(self.default_sampler, "get_state"):
145
+ payload["sampler_state"] = self.default_sampler.get_state()
146
+
147
+ temp_path: Optional[Path] = None
148
+ try:
149
+ with tempfile.NamedTemporaryFile(
150
+ mode="w",
151
+ encoding="utf-8",
152
+ dir=self.storage_dir,
153
+ prefix="nodes.",
154
+ suffix=".tmp",
155
+ delete=False,
156
+ ) as handle:
157
+ json.dump(payload, handle, ensure_ascii=False, indent=2)
158
+ handle.flush()
159
+ os.fsync(handle.fileno())
160
+ temp_path = Path(handle.name)
161
+
162
+ os.replace(temp_path, self.storage_dir / "nodes.json")
163
+ finally:
164
+ if temp_path is not None and temp_path.exists():
165
+ temp_path.unlink(missing_ok=True)
166
+
167
+ self.faiss.save()
168
+
169
+ def _load_locked(self) -> None:
170
+ data_file = self.storage_dir / "nodes.json"
171
+ self.nodes = {}
172
+ self.next_id = 0
173
+ self.default_sampler = get_sampler(self.sampling_algorithm, **self.sampling_kwargs)
174
+ self.faiss = self._build_faiss_index()
175
+ if not data_file.exists():
176
+ return
177
+ with open(data_file, "r", encoding="utf-8") as handle:
178
+ payload = json.load(handle)
179
+ self.next_id = payload.get("next_id", 0)
180
+ for node_id, node_data in payload.get("nodes", {}).items():
181
+ self.nodes[int(node_id)] = Node.from_dict(node_data)
182
+ if hasattr(self.default_sampler, "load_state") and "sampler_state" in payload:
183
+ self.default_sampler.load_state(payload["sampler_state"])
184
+ if hasattr(self.default_sampler, "rebuild_from_nodes"):
185
+ self.default_sampler.rebuild_from_nodes(list(self.nodes.values()))
186
+
187
+ def _build_faiss_index(self) -> FAISSIndex:
188
+ return FAISSIndex(
189
+ dimension=self.embedding_dim,
190
+ index_type=self.faiss_index_type,
191
+ storage_path=self.storage_dir / "faiss",
192
+ )
193
+
194
+ def _get_sampler_stats_locked(self) -> Optional[Dict[str, Any]]:
195
+ if hasattr(self.default_sampler, "get_island_stats"):
196
+ return self.default_sampler.get_island_stats(list(self.nodes.values()))
197
+ return None
198
+
199
+ @staticmethod
200
+ def _clone_node(node: Node) -> Node:
201
+ return Node.from_dict(copy.deepcopy(node.to_dict()))
202
+
203
+ def _clone_nodes(self, nodes: List[Node] | Any) -> List[Node]:
204
+ return [self._clone_node(node) for node in nodes]
205
+
206
+ @contextmanager
207
+ def _database_guard(self, refresh: bool = True):
208
+ with self.lock:
209
+ with InterProcessFileLock(self.lock_path):
210
+ if refresh:
211
+ self._load_locked()
212
+ hold_ms = int(os.environ.get("EVOLVE_DB_TEST_HOLD_LOCK_MS", "0") or 0)
213
+ if hold_ms > 0:
214
+ time.sleep(hold_ms / 1000.0)
215
+ yield
216
+
217
+ def __len__(self) -> int:
218
+ return len(self.get_all())
@@ -0,0 +1,57 @@
1
+ """Helpers for diff-style code edits."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from difflib import unified_diff
7
+ from typing import List, Optional, Tuple
8
+
9
+
10
+ def extract_diffs(
11
+ llm_response: str,
12
+ pattern: str = r"<<<<<<< SEARCH\n(.*?)=======\n(.*?)>>>>>>> REPLACE",
13
+ ) -> List[Tuple[str, str]]:
14
+ matches = re.findall(pattern, llm_response, re.DOTALL)
15
+ return [(search.strip("\n"), replace.strip("\n")) for search, replace in matches]
16
+
17
+
18
+ def apply_diff(
19
+ original_code: str,
20
+ llm_response: str,
21
+ pattern: str = r"<<<<<<< SEARCH\n(.*?)=======\n(.*?)>>>>>>> REPLACE",
22
+ ) -> str:
23
+ diff_blocks = extract_diffs(llm_response, pattern)
24
+ if not diff_blocks:
25
+ raise ValueError("No valid diff blocks found")
26
+
27
+ updated_code = original_code
28
+ for search, replace in diff_blocks:
29
+ if search not in updated_code:
30
+ raise ValueError("SEARCH block not found in target text")
31
+ updated_code = updated_code.replace(search, replace, 1)
32
+ return updated_code
33
+
34
+
35
+ def parse_full_rewrite(llm_response: str, language: str = "python") -> Optional[str]:
36
+ pattern = rf"```{language}\n(.*?)```"
37
+ match = re.search(pattern, llm_response, re.DOTALL)
38
+ if match:
39
+ return match.group(1).strip()
40
+ stripped = llm_response.strip()
41
+ return stripped or None
42
+
43
+
44
+ def format_diff_summary(diff_blocks: List[Tuple[str, str]]) -> str:
45
+ parts: List[str] = []
46
+ for search, replace in diff_blocks:
47
+ lines = list(
48
+ unified_diff(
49
+ search.splitlines(),
50
+ replace.splitlines(),
51
+ fromfile="before",
52
+ tofile="after",
53
+ lineterm="",
54
+ )
55
+ )
56
+ parts.append("\n".join(lines))
57
+ return "\n\n".join(parts)
@@ -0,0 +1,84 @@
1
+ """Embedding helpers with a graceful local fallback."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ import re
7
+ from typing import List, Union
8
+
9
+ import numpy as np
10
+
11
+ try:
12
+ from sentence_transformers import SentenceTransformer
13
+
14
+ ST_AVAILABLE = True
15
+ except Exception:
16
+ SentenceTransformer = None
17
+ ST_AVAILABLE = False
18
+
19
+
20
+ class EmbeddingService:
21
+ """Encode text with sentence-transformers when available, else hash tokens."""
22
+
23
+ def __init__(
24
+ self,
25
+ model_name: str = "sentence-transformers/all-MiniLM-L6-v2",
26
+ device: str = "cpu",
27
+ dimension: int = 384,
28
+ ):
29
+ self.dimension = dimension
30
+ self.model = None
31
+
32
+ if ST_AVAILABLE and SentenceTransformer is not None:
33
+ try:
34
+ self.model = SentenceTransformer(model_name, device=device)
35
+ self.dimension = self.model.get_sentence_embedding_dimension()
36
+ except Exception:
37
+ self.model = None
38
+
39
+ def encode(
40
+ self,
41
+ texts: Union[str, List[str]],
42
+ normalize: bool = True,
43
+ ) -> np.ndarray:
44
+ if isinstance(texts, str):
45
+ texts = [texts]
46
+
47
+ if self.model is not None:
48
+ embeddings = self.model.encode(
49
+ texts,
50
+ normalize_embeddings=normalize,
51
+ show_progress_bar=False,
52
+ )
53
+ return np.array(embeddings, dtype=np.float32)
54
+
55
+ vectors = np.vstack([self._fallback_encode_one(text) for text in texts]).astype(
56
+ np.float32
57
+ )
58
+ if normalize:
59
+ vectors = self._normalize(vectors)
60
+ return vectors
61
+
62
+ def get_dimension(self) -> int:
63
+ return self.dimension
64
+
65
+ def _fallback_encode_one(self, text: str) -> np.ndarray:
66
+ vector = np.zeros(self.dimension, dtype=np.float32)
67
+ tokens = re.findall(r"[A-Za-z0-9_]+", text.lower())
68
+ if not tokens:
69
+ return vector
70
+
71
+ for token in tokens:
72
+ hashed = hash(token)
73
+ index = abs(hashed) % self.dimension
74
+ sign = -1.0 if hashed % 2 else 1.0
75
+ weight = 1.0 + math.log1p(len(token))
76
+ vector[index] += sign * weight
77
+
78
+ return vector
79
+
80
+ @staticmethod
81
+ def _normalize(vectors: np.ndarray) -> np.ndarray:
82
+ norms = np.linalg.norm(vectors, axis=1, keepdims=True)
83
+ norms[norms == 0] = 1.0
84
+ return vectors / norms
@@ -0,0 +1,45 @@
1
+ """Cross-process file locking helpers for the Evolve skill runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ if os.name == "nt":
9
+ import msvcrt
10
+ else:
11
+ import fcntl
12
+
13
+
14
+ class InterProcessFileLock:
15
+ """Hold an exclusive lock on a sentinel file across processes."""
16
+
17
+ def __init__(self, path: Path):
18
+ self.path = Path(path)
19
+ self._handle = None
20
+
21
+ def __enter__(self) -> "InterProcessFileLock":
22
+ self.path.parent.mkdir(parents=True, exist_ok=True)
23
+ self._handle = open(self.path, "a+b")
24
+ self._handle.seek(0)
25
+ self._handle.write(b"\0")
26
+ self._handle.flush()
27
+ self._handle.seek(0)
28
+
29
+ if os.name == "nt":
30
+ msvcrt.locking(self._handle.fileno(), msvcrt.LK_LOCK, 1)
31
+ else:
32
+ fcntl.flock(self._handle.fileno(), fcntl.LOCK_EX)
33
+ return self
34
+
35
+ def __exit__(self, exc_type, exc, tb) -> None:
36
+ if self._handle is None:
37
+ return
38
+
39
+ self._handle.seek(0)
40
+ if os.name == "nt":
41
+ msvcrt.locking(self._handle.fileno(), msvcrt.LK_UNLCK, 1)
42
+ else:
43
+ fcntl.flock(self._handle.fileno(), fcntl.LOCK_UN)
44
+ self._handle.close()
45
+ self._handle = None