talos-code 0.0.1 → 0.2.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 (407) hide show
  1. package/CHANGELOG.md +87 -0
  2. package/LICENSE +661 -0
  3. package/README.md +116 -3
  4. package/THIRD_PARTY_NOTICES.md +40 -0
  5. package/dist/archive/zip.js +73 -0
  6. package/dist/args.js +100 -0
  7. package/dist/automations/history-store.js +34 -0
  8. package/dist/automations/policy-store.js +43 -0
  9. package/dist/automations/runner.js +107 -0
  10. package/dist/automations/schedule.js +85 -0
  11. package/dist/automations/windows-task.js +53 -0
  12. package/dist/commands/advanced-cli.js +233 -0
  13. package/dist/commands/checkpoint-cli.js +45 -0
  14. package/dist/commands/config-cli.js +107 -0
  15. package/dist/commands/context.js +17 -0
  16. package/dist/commands/extensions-cli.js +207 -0
  17. package/dist/commands/project-cli.js +74 -0
  18. package/dist/commands/project-commands.js +143 -0
  19. package/dist/commands/provider-cli.js +244 -0
  20. package/dist/commands/services-cli.js +248 -0
  21. package/dist/commands/session-cli.js +171 -0
  22. package/dist/commands/system-cli.js +405 -0
  23. package/dist/config/commands.js +60 -0
  24. package/dist/config/load.js +382 -0
  25. package/dist/config/migrations.js +48 -0
  26. package/dist/config/types.js +11 -0
  27. package/dist/diagnostics/development-log.js +154 -0
  28. package/dist/diagnostics/doctor.js +104 -0
  29. package/dist/diagnostics/redact.js +80 -0
  30. package/dist/diagnostics/zip.js +52 -0
  31. package/dist/errors.js +156 -0
  32. package/dist/events/bridge.js +130 -0
  33. package/dist/extensions/installer.js +178 -0
  34. package/dist/extensions/package-schema.js +24 -0
  35. package/dist/headless/result.js +84 -0
  36. package/dist/headless/run.js +230 -0
  37. package/dist/i18n/en/approval.js +46 -0
  38. package/dist/i18n/en/common.js +9 -0
  39. package/dist/i18n/en/credentials.js +92 -0
  40. package/dist/i18n/en/errors.js +267 -0
  41. package/dist/i18n/en/firstrun.js +101 -0
  42. package/dist/i18n/en/providers.js +37 -0
  43. package/dist/i18n/en/screen.js +774 -0
  44. package/dist/i18n/en/tools.js +127 -0
  45. package/dist/i18n/en.js +14 -0
  46. package/dist/i18n/error-view.js +307 -0
  47. package/dist/i18n/index.js +19 -0
  48. package/dist/i18n/kernel-map.js +139 -0
  49. package/dist/io.js +45 -0
  50. package/dist/main.js +331 -0
  51. package/dist/paths.js +41 -0
  52. package/dist/protocol/v2/codec.js +9 -0
  53. package/dist/protocol/v2/events.js +555 -0
  54. package/dist/protocol/v2/index.js +4 -0
  55. package/dist/protocol/v2/replay.js +28 -0
  56. package/dist/protocol/v2/types.js +1 -0
  57. package/dist/provider/control-plane.js +135 -0
  58. package/dist/provider/environment-keys.js +275 -0
  59. package/dist/provider/health.js +110 -0
  60. package/dist/provider/missing-key.js +48 -0
  61. package/dist/provider/model-catalog.js +168 -0
  62. package/dist/provider/openrouter-login.js +172 -0
  63. package/dist/provider/provider-text.js +13 -0
  64. package/dist/provider/store.js +95 -0
  65. package/dist/provider/system-keyring.js +214 -0
  66. package/dist/runtime/active-run.js +63 -0
  67. package/dist/runtime/agent-tree.js +74 -0
  68. package/dist/runtime/attachments.js +84 -0
  69. package/dist/runtime/brokered-executor.js +395 -0
  70. package/dist/runtime/context-archive.js +69 -0
  71. package/dist/runtime/context-status.js +76 -0
  72. package/dist/runtime/create-runtime.js +111 -0
  73. package/dist/runtime/model-profile.js +361 -0
  74. package/dist/runtime/output-store.js +137 -0
  75. package/dist/runtime/provider-attempts.js +185 -0
  76. package/dist/runtime/replay-buffer.js +153 -0
  77. package/dist/runtime/repo.js +95 -0
  78. package/dist/runtime/session-facade.js +135 -0
  79. package/dist/runtime/session-summary.js +125 -0
  80. package/dist/runtime/supervisor.js +265 -0
  81. package/dist/runtime/talos-composition.js +1011 -0
  82. package/dist/runtime/types.js +5 -0
  83. package/dist/runtime/usage-snapshot.js +82 -0
  84. package/dist/security/auto-classifier.js +38 -0
  85. package/dist/security/credential-free-environment.js +54 -0
  86. package/dist/security/evaluate.js +243 -0
  87. package/dist/security/execution-backends.js +236 -0
  88. package/dist/security/execution-broker.js +102 -0
  89. package/dist/security/forge-scan.js +74 -0
  90. package/dist/security/from-approval.js +39 -0
  91. package/dist/security/mxc-execution-backend.js +281 -0
  92. package/dist/security/permission-engine.js +138 -0
  93. package/dist/security/permission-explanation.js +54 -0
  94. package/dist/security/persist.js +179 -0
  95. package/dist/security/plugin-guard.js +230 -0
  96. package/dist/security/process-tree-evidence-store.js +74 -0
  97. package/dist/security/process-tree-probe.js +554 -0
  98. package/dist/security/project-resource-inventory.js +186 -0
  99. package/dist/security/project-trust-gate.js +38 -0
  100. package/dist/security/project-trust.js +367 -0
  101. package/dist/security/rule-parser.js +201 -0
  102. package/dist/security/shell-segmentation.js +68 -0
  103. package/dist/security/trust-authority.js +324 -0
  104. package/dist/security/types.js +1 -0
  105. package/dist/security/workspace-identity.js +102 -0
  106. package/dist/services/automation-facade.js +59 -0
  107. package/dist/services/forge-facade.js +77 -0
  108. package/dist/services/hook-facade.js +240 -0
  109. package/dist/services/index.js +16 -0
  110. package/dist/services/library-facade.js +174 -0
  111. package/dist/services/mcp-facade.js +348 -0
  112. package/dist/services/memory-facade.js +126 -0
  113. package/dist/services/notes-facade.js +157 -0
  114. package/dist/services/plugin-facade.js +308 -0
  115. package/dist/services/research-facade.js +110 -0
  116. package/dist/services/task-board-facade.js +236 -0
  117. package/dist/services/workflow-catalog.js +246 -0
  118. package/dist/sessions/export-format.js +98 -0
  119. package/dist/sessions/metadata-store.js +160 -0
  120. package/dist/sessions/share.js +122 -0
  121. package/dist/sessions/transfer.js +16 -0
  122. package/dist/subcommands.js +62 -0
  123. package/dist/tui/agent-roster.js +36 -0
  124. package/dist/tui/agent-view.js +51 -0
  125. package/dist/tui/app.js +4098 -0
  126. package/dist/tui/approval.js +36 -0
  127. package/dist/tui/boot/boot-sequence.js +76 -0
  128. package/dist/tui/boot/cinematic.js +243 -0
  129. package/dist/tui/boot/desktop-logo.js +82 -0
  130. package/dist/tui/boot.js +3 -0
  131. package/dist/tui/busy-input.js +58 -0
  132. package/dist/tui/catalog-service.js +560 -0
  133. package/dist/tui/components/assistant-stream.js +1 -0
  134. package/dist/tui/components/command-menu.js +68 -0
  135. package/dist/tui/components/composer.js +219 -0
  136. package/dist/tui/components/diff.js +35 -0
  137. package/dist/tui/components/footer.js +48 -0
  138. package/dist/tui/components/header.js +6 -0
  139. package/dist/tui/components/markdown.js +305 -0
  140. package/dist/tui/components/status-indicator.js +38 -0
  141. package/dist/tui/components/terminal-shell.js +214 -0
  142. package/dist/tui/components/thinking-row.js +19 -0
  143. package/dist/tui/components/tool-row.js +97 -0
  144. package/dist/tui/components/transcript-virtualizer.js +165 -0
  145. package/dist/tui/components/transcript.js +43 -0
  146. package/dist/tui/descendant-approvals.js +104 -0
  147. package/dist/tui/diff-model.js +77 -0
  148. package/dist/tui/editor-history.js +21 -0
  149. package/dist/tui/editor.js +210 -0
  150. package/dist/tui/event-adapter.js +436 -0
  151. package/dist/tui/exit-output.js +58 -0
  152. package/dist/tui/external-editor.js +53 -0
  153. package/dist/tui/file-completion.js +89 -0
  154. package/dist/tui/focus-manager.js +5 -0
  155. package/dist/tui/highlight.js +30 -0
  156. package/dist/tui/input-router.js +18 -0
  157. package/dist/tui/interrupt.js +99 -0
  158. package/dist/tui/keybindings.js +194 -0
  159. package/dist/tui/keymap-resolver.js +91 -0
  160. package/dist/tui/launch-state.js +148 -0
  161. package/dist/tui/line-diff.js +57 -0
  162. package/dist/tui/live-activity.js +56 -0
  163. package/dist/tui/metrics.js +123 -0
  164. package/dist/tui/onboarding.js +36 -0
  165. package/dist/tui/overlays/agent-tree.js +43 -0
  166. package/dist/tui/overlays/approval-dialog.js +642 -0
  167. package/dist/tui/overlays/automation-center.js +51 -0
  168. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  169. package/dist/tui/overlays/context-inspector.js +36 -0
  170. package/dist/tui/overlays/effort-line.js +110 -0
  171. package/dist/tui/overlays/forge-center.js +46 -0
  172. package/dist/tui/overlays/help-dialog.js +16 -0
  173. package/dist/tui/overlays/history-picker.js +37 -0
  174. package/dist/tui/overlays/hook-center.js +70 -0
  175. package/dist/tui/overlays/library-center.js +55 -0
  176. package/dist/tui/overlays/mcp-center.js +60 -0
  177. package/dist/tui/overlays/memory-center.js +63 -0
  178. package/dist/tui/overlays/model-picker.js +77 -0
  179. package/dist/tui/overlays/notes-center.js +53 -0
  180. package/dist/tui/overlays/overlay-host.js +8 -0
  181. package/dist/tui/overlays/plugin-center.js +76 -0
  182. package/dist/tui/overlays/provider-picker.js +171 -0
  183. package/dist/tui/overlays/queue-editor.js +32 -0
  184. package/dist/tui/overlays/research-center.js +59 -0
  185. package/dist/tui/overlays/scroll-window.js +234 -0
  186. package/dist/tui/overlays/session-picker.js +115 -0
  187. package/dist/tui/overlays/tasks-center.js +83 -0
  188. package/dist/tui/overlays/theme-picker.js +21 -0
  189. package/dist/tui/overlays/transcript-search.js +58 -0
  190. package/dist/tui/overlays/trust-center.js +29 -0
  191. package/dist/tui/overlays/workflow-center.js +46 -0
  192. package/dist/tui/project-file-index.js +214 -0
  193. package/dist/tui/project-references.js +85 -0
  194. package/dist/tui/project-trust-prompt.js +289 -0
  195. package/dist/tui/prompt-history-store.js +116 -0
  196. package/dist/tui/queue-store.js +110 -0
  197. package/dist/tui/regions/budget.js +59 -0
  198. package/dist/tui/regions/views.js +96 -0
  199. package/dist/tui/render-coordinator.js +148 -0
  200. package/dist/tui/render-scheduler.js +4 -0
  201. package/dist/tui/run.js +69 -0
  202. package/dist/tui/selection-list.js +29 -0
  203. package/dist/tui/session-controller.js +791 -0
  204. package/dist/tui/session-export.js +54 -0
  205. package/dist/tui/setup-wizard.js +46 -0
  206. package/dist/tui/shell-input.js +35 -0
  207. package/dist/tui/shell-layout.js +48 -0
  208. package/dist/tui/shell-model.js +98 -0
  209. package/dist/tui/slash-commands.js +82 -0
  210. package/dist/tui/state.js +151 -0
  211. package/dist/tui/status-bar.js +125 -0
  212. package/dist/tui/status-view.js +40 -0
  213. package/dist/tui/terminal-capabilities.js +9 -0
  214. package/dist/tui/terminal-session.js +11 -0
  215. package/dist/tui/text-width.js +66 -0
  216. package/dist/tui/theme-catalog.js +27 -0
  217. package/dist/tui/theme-store.js +23 -0
  218. package/dist/tui/theme.js +23 -0
  219. package/dist/tui/tool-display.js +135 -0
  220. package/dist/tui/tool-facts.js +1 -0
  221. package/dist/tui/tools/bash-renderer.js +32 -0
  222. package/dist/tui/tools/change.js +61 -0
  223. package/dist/tui/tools/edit-renderer.js +17 -0
  224. package/dist/tui/tools/generic-renderer.js +13 -0
  225. package/dist/tui/tools/list-renderer.js +14 -0
  226. package/dist/tui/tools/read-renderer.js +13 -0
  227. package/dist/tui/tools/registry.js +20 -0
  228. package/dist/tui/tools/row-format.js +187 -0
  229. package/dist/tui/tools/search-renderer.js +38 -0
  230. package/dist/tui/tools/shared.js +97 -0
  231. package/dist/tui/tools/write-renderer.js +15 -0
  232. package/dist/tui/transcript-model.js +441 -0
  233. package/dist/tui/ui-preferences.js +79 -0
  234. package/dist/tui/usage-view.js +77 -0
  235. package/dist/tui/vim-mode.js +99 -0
  236. package/dist/update/check.js +15 -0
  237. package/dist/update/npm.js +51 -0
  238. package/dist/update/run.js +129 -0
  239. package/dist/version.js +2 -0
  240. package/dist/workspace/checkpoint-store.js +210 -0
  241. package/dist/workspace/checkpoint.js +413 -0
  242. package/dist/workspace/restore.js +232 -0
  243. package/package.json +64 -5
  244. package/vendor/context-engine/package.json +11 -0
  245. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  246. package/vendor/context-engine/src/contracts.mjs +48 -0
  247. package/vendor/context-engine/src/engine.mjs +445 -0
  248. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  249. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  250. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  251. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  252. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  253. package/vendor/context-engine/src/profiles.mjs +10 -0
  254. package/vendor/context-engine/src/retrieval.mjs +104 -0
  255. package/vendor/context-engine/src/summary.mjs +97 -0
  256. package/vendor/context-engine/src/usage.mjs +34 -0
  257. package/vendor/harness-ui/package.json +38 -0
  258. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  259. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  260. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  261. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  262. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  263. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  264. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  265. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  266. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  267. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  268. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  269. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  270. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  271. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  272. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  273. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  274. package/vendor/harness-ui/src/config.mjs +697 -0
  275. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  276. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  277. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  278. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  279. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  280. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  281. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  282. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  283. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  284. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  285. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  286. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  287. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  288. package/vendor/harness-ui/src/doctor.mjs +142 -0
  289. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  290. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  291. package/vendor/harness-ui/src/document-report.mjs +331 -0
  292. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  293. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  294. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  295. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  296. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  297. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  298. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  299. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  300. package/vendor/harness-ui/src/git-service.mjs +626 -0
  301. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  302. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  303. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  304. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  305. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  306. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  307. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  308. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  309. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  310. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  311. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  312. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  313. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  314. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  315. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  316. package/vendor/harness-ui/src/library-store.mjs +652 -0
  317. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  318. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  319. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  320. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  321. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  322. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  323. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  324. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  325. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  326. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  327. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  328. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  329. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  330. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  331. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  332. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  333. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  334. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  335. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  336. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  337. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  338. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  339. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  340. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  341. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  342. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  343. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  344. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  345. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  346. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  347. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  348. package/vendor/harness-ui/src/research/card.mjs +235 -0
  349. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  350. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  351. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  352. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  353. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  354. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  355. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  356. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  357. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  358. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  359. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  360. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  361. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  362. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  363. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  364. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  365. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  366. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  367. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  368. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  369. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  370. package/vendor/harness-ui/src/research/report.mjs +203 -0
  371. package/vendor/harness-ui/src/research/run.mjs +527 -0
  372. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  373. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  374. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  375. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  376. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  377. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  378. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  379. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  380. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  381. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  382. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  383. package/vendor/harness-ui/src/session-store.mjs +220 -0
  384. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  385. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  386. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  387. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  388. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  389. package/vendor/harness-ui/src/static-files.mjs +96 -0
  390. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  391. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  392. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  393. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  394. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  395. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  396. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  397. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  398. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  399. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  400. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  401. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  402. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  403. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  404. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  405. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  406. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  407. package/vendor/manifest.json +170 -0
@@ -0,0 +1,559 @@
1
+ import { parentPort, workerData } from 'node:worker_threads';
2
+ import { DatabaseSync, backup } from 'node:sqlite';
3
+ import { readFileSync, mkdirSync, existsSync, openSync, closeSync, unlinkSync } from 'node:fs';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { createHash, randomUUID } from 'node:crypto';
6
+ import { isDeepStrictEqual } from 'node:util';
7
+ import { ContextStoreError } from './sqlite-store.mjs';
8
+ import { verifyContextArchive } from './context-export.mjs';
9
+
10
+ const { databasePath, vectorExtension, faultPoint } = workerData;
11
+ if (databasePath !== ':memory:') mkdirSync(dirname(resolve(databasePath)), { recursive: true });
12
+ const db = new DatabaseSync(databasePath, { timeout: 5000, allowExtension: vectorExtension === true, enableForeignKeyConstraints: true });
13
+ db.exec('PRAGMA journal_mode=WAL; PRAGMA synchronous=FULL; PRAGMA foreign_keys=ON; PRAGMA busy_timeout=5000;');
14
+ if (db.prepare('PRAGMA user_version').get().user_version > 1) throw new ContextStoreError('Unsupported context database version', 'CTX_SCHEMA_UNSUPPORTED');
15
+ db.exec('BEGIN IMMEDIATE');
16
+ try { db.exec(readFileSync(new URL('./migrations/001-context.sql', import.meta.url), 'utf8')); db.exec('COMMIT'); }
17
+ catch (error) { db.exec('ROLLBACK'); throw error; }
18
+ let vector = false;
19
+ if (vectorExtension === true) {
20
+ try {
21
+ const sqliteVec = await import('sqlite-vec');
22
+ sqliteVec.load(db);
23
+ vector = db.prepare('SELECT vec_version() AS version').get().version === 'v0.1.9';
24
+ } catch { vector = false; }
25
+ finally { db.enableLoadExtension(false); }
26
+ }
27
+
28
+ const fail = (message, code = 'CTX_INVALID_INPUT') => { throw new ContextStoreError(message, code); };
29
+ const hash = value => createHash('sha256').update(value).digest('hex');
30
+ const object = value => value !== null && typeof value === 'object' && !Array.isArray(value) && Object.getPrototypeOf(value) === Object.prototype;
31
+ const validId = value => typeof value === 'string' && value.length > 0 && value.length <= 256 && !value.includes('\0');
32
+ const id = (value, name = 'id') => { if (!validId(value)) fail(`Invalid ${name}`); return value; };
33
+ const integer = (value, name = 'revision') => { if (!Number.isSafeInteger(value) || value < 0) fail(`Invalid ${name}`); return value; };
34
+ const iso = value => { if (typeof value !== 'string' || !/^\d{4}-\d\d-\d\dT\d\d:\d\d(?::\d\d(?:\.\d+)?)?(?:Z|[+-]\d\d:\d\d)$/.test(value) || !Number.isFinite(Date.parse(value))) fail('Invalid ISO timestamp'); };
35
+ const textOf = message => typeof message.content === 'string' ? message.content : Array.isArray(message.content) ? message.content.filter(part => part && ['text', 'input_text', 'output_text'].includes(part.type) && typeof part.text === 'string').map(part => part.text).join('\n') : '';
36
+ const run = (sql, ...args) => db.prepare(sql).run(...args);
37
+ const get = (sql, ...args) => db.prepare(sql).get(...args);
38
+ const all = (sql, ...args) => db.prepare(sql).all(...args);
39
+ const parseRows = (sql, field, ...args) => all(sql, ...args).map(row => JSON.parse(row[field]));
40
+ function json(value, ancestors = new Set()) {
41
+ if (value === null || ['string', 'boolean'].includes(typeof value) || (typeof value === 'number' && Number.isFinite(value))) return;
42
+ if ((!object(value) && !Array.isArray(value)) || ancestors.has(value)) fail('Expected finite acyclic JSON data');
43
+ ancestors.add(value);
44
+ if (Array.isArray(value) && Object.keys(value).length !== value.length) fail('Sparse arrays are invalid');
45
+ for (const entry of Object.values(value)) json(entry, ancestors);
46
+ ancestors.delete(value);
47
+ }
48
+ let transactionDepth = 0;
49
+ function tx(fn) {
50
+ const depth = transactionDepth++;
51
+ const name = `context_nested_${depth}`;
52
+ try {
53
+ db.exec(depth ? `SAVEPOINT ${name}` : 'BEGIN IMMEDIATE');
54
+ try { const result = fn(); db.exec(depth ? `RELEASE ${name}` : 'COMMIT'); return result; }
55
+ catch (error) { db.exec(depth ? `ROLLBACK TO ${name}; RELEASE ${name}` : 'ROLLBACK'); throw error; }
56
+ } finally { transactionDepth--; }
57
+ }
58
+ function fault(point) {
59
+ if (faultPoint === `crash-${point}`) process.exit(91);
60
+ if (faultPoint === point) fail(`Injected persistence failure: ${point}`, 'CTX_PERSISTENCE_FAILED');
61
+ }
62
+ function session(sessionId, required = true) {
63
+ id(sessionId, 'sessionId');
64
+ const row = get('SELECT * FROM context_sessions WHERE session_id=?', sessionId);
65
+ if (!row && required) fail('Context session does not exist', 'CTX_SESSION_NOT_FOUND');
66
+ return row;
67
+ }
68
+ function revision(row, expectedRevision, expectedStateRevision) {
69
+ integer(expectedRevision);
70
+ if (row.revision !== expectedRevision || (expectedStateRevision !== undefined && row.state_revision !== integer(expectedStateRevision, 'stateRevision'))) fail('Context changed; reread the current revision', 'CTX_STALE_REVISION');
71
+ }
72
+ function settings(value) {
73
+ json(value);
74
+ const fields = ['auto', 'model', 'triggerRatio', 'targetRatio', 'retainRecentTurns', 'focus', 'nativeMode', 'semanticSearch'];
75
+ if (!object(value) || Object.keys(value).length !== fields.length || fields.some(key => !Object.hasOwn(value, key)) || typeof value.auto !== 'boolean' || typeof value.semanticSearch !== 'boolean' || typeof value.targetRatio !== 'number' || typeof value.triggerRatio !== 'number' || !(value.targetRatio > 0 && value.targetRatio < value.triggerRatio && value.triggerRatio < 1) || !Number.isSafeInteger(value.retainRecentTurns) || value.retainRecentTurns < 0 || value.retainRecentTurns > 100 || typeof value.focus !== 'string' || value.focus.length > 8000 || !['off', 'qualified'].includes(value.nativeMode) || !object(value.model)) fail('Invalid context settings');
76
+ const modelFields = value.model.mode === 'follow-session' ? ['mode'] : value.model.mode === 'explicit' ? ['mode', 'provider', 'model'] : [];
77
+ if (!modelFields.length || Object.keys(value.model).length !== modelFields.length || modelFields.some(key => !validId(value.model[key]))) fail('Invalid context model selection');
78
+ }
79
+ function message(value) {
80
+ json(value);
81
+ if (!object(value) || !['system', 'developer', 'user', 'assistant', 'tool'].includes(value.role) || (!Object.hasOwn(value, 'content') && !(value.role === 'assistant' && Array.isArray(value.tool_calls) && value.tool_calls.length))) fail('Invalid original message');
82
+ if (value.role === 'tool') id(value.tool_call_id, 'tool_call_id');
83
+ if (value.tool_calls !== undefined) {
84
+ if (value.role !== 'assistant' || !Array.isArray(value.tool_calls)) fail('Invalid tool_calls');
85
+ const seen = new Set();
86
+ for (const call of value.tool_calls) {
87
+ if (!object(call) || !validId(call.id) || seen.has(call.id) || call.type !== 'function' || !object(call.function) || !validId(call.function.name) || typeof call.function.arguments !== 'string') fail('Malformed tool call');
88
+ seen.add(call.id);
89
+ }
90
+ }
91
+ }
92
+ function originals(sessionId, afterSequence = 0, throughSequence = Number.MAX_SAFE_INTEGER) {
93
+ return parseRows('SELECT record_json FROM original_records WHERE session_id=? AND sequence>? AND sequence<=? ORDER BY sequence', 'record_json', sessionId, afterSequence, throughSequence);
94
+ }
95
+ function snapshot(sessionId) {
96
+ const row = session(sessionId, false);
97
+ if (!row) return null;
98
+ const active = row.active_version_id === null ? null : get('SELECT version_json FROM context_versions WHERE session_id=? AND id=?', sessionId, row.active_version_id);
99
+ const measured = get('SELECT revision, measured_at, measurement_json FROM context_measurements WHERE session_id=?', sessionId);
100
+ return {
101
+ schema: 'talos.context.snapshot.v1', sessionId, revision: row.revision, stateRevision: row.state_revision, headSequence: row.head_sequence,
102
+ settings: JSON.parse(row.settings_json), metadata: JSON.parse(row.metadata_json), activeVersion: active ? JSON.parse(active.version_json) : null,
103
+ measurement: measured ? { schema: 'talos.context.measurement.v1', revision: measured.revision, measuredAt: measured.measured_at, tokens: JSON.parse(measured.measurement_json) } : null,
104
+ facts: parseRows('SELECT fact_json FROM protected_facts WHERE session_id=? ORDER BY id', 'fact_json', sessionId),
105
+ jobs: parseRows('SELECT job_json FROM compaction_jobs WHERE session_id=? ORDER BY id', 'job_json', sessionId),
106
+ };
107
+ }
108
+ function archive(sessionId) {
109
+ const snap = snapshot(sessionId);
110
+ if (!snap) fail('Context session does not exist', 'CTX_SESSION_NOT_FOUND');
111
+ const payload = {
112
+ schema: 'talos.context.archive.v1', session: snap, records: originals(sessionId),
113
+ versions: methods.listContextVersions({ sessionId }), facts: snap.facts, jobs: snap.jobs,
114
+ usage: methods.readUsage({ sessionId }),
115
+ mutations: all('SELECT idempotency_key AS idempotencyKey, request_fingerprint AS requestFingerprint, result_json FROM context_mutations WHERE session_id=? ORDER BY idempotency_key', sessionId).map(({ result_json, ...entry }) => ({ ...entry, result: JSON.parse(result_json) })),
116
+ blobs: all('SELECT a.id,a.sha256,a.mime_type,b.bytes FROM record_assets a JOIN content_blobs b ON b.sha256=a.sha256 WHERE a.session_id=? ORDER BY a.id', sessionId).map(row => ({ id: row.id, sha256: row.sha256, mimeType: row.mime_type, base64: Buffer.from(row.bytes).toString('base64') })),
117
+ };
118
+ return { ...payload, manifest: { schema: 'talos.context.manifest.v1', algorithm: 'sha256', payloadSha256: hash(JSON.stringify(payload)) } };
119
+ }
120
+ function comparableArchive(value) {
121
+ const { manifest, ...payload } = value;
122
+ payload.mutations = [...(payload.mutations ?? [])].sort((a, b) => a.idempotencyKey.localeCompare(b.idempotencyKey));
123
+ const byId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
124
+ return {
125
+ ...payload,
126
+ // 09/09 — `measurement` è un fatto sul presente che invecchia, non stato archiviato: due archivi
127
+ // della stessa sessione non divergono perché una richiesta è stata misurata nel frattempo.
128
+ session: { ...payload.session, measurement: undefined, facts: [...payload.session.facts].sort(byId), jobs: [...payload.session.jobs].sort(byId) },
129
+ blobs: [...payload.blobs].sort(byId), facts: [...payload.facts].sort(byId), jobs: [...payload.jobs].sort(byId),
130
+ };
131
+ }
132
+ function jobRow(sessionId, jobId) {
133
+ id(sessionId, 'sessionId'); id(jobId, 'jobId');
134
+ const row = get('SELECT job_json FROM compaction_jobs WHERE session_id=? AND id=?', sessionId, jobId);
135
+ return row ? JSON.parse(row.job_json) : null;
136
+ }
137
+ const activeStates = ['queued', 'preparing', 'summarizing', 'validating', 'ready'];
138
+ function validateJob(job, sessionId) {
139
+ json(job);
140
+ if (!object(job) || job.schema !== 'talos.context.job.v1' || job.sessionId !== sessionId || !validId(job.id) || !validId(job.idempotencyKey) || !validId(job.requestFingerprint) || !['compact', 'regenerate', 'restore'].includes(job.kind) || ![...activeStates, 'committed', 'paused', 'cancelled', 'failed'].includes(job.state)) fail('Invalid context job');
141
+ for (const key of ['baseRevision', 'baseStateRevision', 'coveredThrough']) integer(job[key], key);
142
+ iso(job.createdAt); iso(job.updatedAt);
143
+ if (!object(job.model) || !validId(job.model.provider) || !validId(job.model.model) || !Array.isArray(job.completedSegments) || !object(job.progress) || !Number.isSafeInteger(job.progress.completed) || !Number.isSafeInteger(job.progress.total) || job.progress.completed < 0 || job.progress.total < job.progress.completed || typeof job.progress.phase !== 'string') fail('Invalid job model or progress');
144
+ }
145
+ function requireNoOtherActive(sessionId, jobId) {
146
+ if (get("SELECT id FROM compaction_jobs WHERE session_id=? AND id<>? AND state IN ('queued','preparing','summarizing','validating','ready')", sessionId, jobId)) fail('Another context job is active', 'CTX_JOB_ACTIVE');
147
+ }
148
+ function sources(sessionId, values) {
149
+ if (!Array.isArray(values)) fail('Invalid source references', 'CTX_SOURCE_INVALID');
150
+ return values.map(source => {
151
+ if (!object(source) || !validId(source.recordId) || typeof source.quote !== 'string' || !source.quote) fail('Invalid source reference', 'CTX_SOURCE_INVALID');
152
+ const row = get('SELECT record_json FROM original_records WHERE session_id=? AND id=?', sessionId, source.recordId);
153
+ if (!row) fail('Source belongs to no original in this session', 'CTX_SOURCE_INVALID');
154
+ const text = textOf(JSON.parse(row.record_json).message);
155
+ const start = source.start === undefined ? text.indexOf(source.quote) : source.start;
156
+ if (!Number.isSafeInteger(start) || start < 0 || text.slice(start, start + source.quote.length) !== source.quote || (source.end !== undefined && source.end !== start + source.quote.length)) fail('Source quote does not match original', 'CTX_SOURCE_INVALID');
157
+ return { ...source, start, end: start + source.quote.length };
158
+ });
159
+ }
160
+ function writeVersion(sessionId, version) {
161
+ if (get('SELECT id FROM context_versions WHERE session_id=? AND id=?', sessionId, version.id)) fail('Version id already exists', 'CTX_VERSION_CONFLICT');
162
+ run('INSERT INTO context_versions(session_id,id,version_json) VALUES(?,?,?)', sessionId, version.id, JSON.stringify(version));
163
+ run('INSERT INTO summary_nodes(session_id,version_id,summary_json) VALUES(?,?,?)', sessionId, version.id, JSON.stringify(version.summary));
164
+ }
165
+ const usageBackfilled = new Set();
166
+ function enqueueUsage(sessionId, operationId, jobId, usage) {
167
+ const event = { schema: 'talos.context.event.v1', id: `usage-${hash(JSON.stringify([sessionId, operationId]))}`, sessionId,
168
+ ...(jobId ? { jobId } : {}), kind: 'context.usage.recorded', createdAt: new Date().toISOString(), payload: { operationId, usage } };
169
+ // The retained receipt includes acknowledged deliveries: retry cannot revive it.
170
+ run('INSERT INTO context_outbox(session_id,id,event_json) VALUES(?,?,?) ON CONFLICT(session_id,id) DO NOTHING', sessionId, event.id, JSON.stringify(event));
171
+ }
172
+ function activate(sessionId, version, jobId, kind = 'context.version.committed') {
173
+ run('UPDATE context_sessions SET active_version_id=?,revision=revision+1,state_revision=state_revision+1 WHERE session_id=?', version.id, sessionId);
174
+ const event = { schema: 'talos.context.event.v1', id: randomUUID(), sessionId, ...(jobId ? { jobId } : {}), versionId: version.id, kind, createdAt: version.createdAt, payload: { coveredThrough: version.coveredThrough } };
175
+ run('INSERT INTO context_outbox(session_id,id,event_json) VALUES(?,?,?)', sessionId, event.id, JSON.stringify(event));
176
+ }
177
+
178
+ const methods = {
179
+ initSession({ sessionId, settings: value, metadata = {} }) {
180
+ id(sessionId, 'sessionId'); settings(value); json(metadata);
181
+ if (!object(metadata)) fail('Session metadata must be a JSON object');
182
+ return tx(() => {
183
+ run('INSERT OR IGNORE INTO context_sessions(session_id,settings_json,metadata_json) VALUES(?,?,?)', sessionId, JSON.stringify(value), JSON.stringify(metadata));
184
+ return snapshot(sessionId);
185
+ });
186
+ },
187
+ readContextSnapshot({ sessionId }) { return tx(() => snapshot(sessionId)); },
188
+ /* 09/09 — l'ultima misura preparata: una per sessione, l'ultima vince. Non è una versione né un
189
+ evento: è un fatto sul presente che invecchia, e per questo porta la revisione e l'ora. */
190
+ recordMeasurement({ sessionId, revision: measuredRevision, measuredAt, measurement }) {
191
+ id(sessionId, 'sessionId'); json(measurement);
192
+ if (!Number.isSafeInteger(measuredRevision) || measuredRevision < 0) fail('Invalid measurement revision');
193
+ if (typeof measuredAt !== 'string' || Number.isNaN(Date.parse(measuredAt))) fail('Invalid measurement date');
194
+ return tx(() => {
195
+ session(sessionId);
196
+ run('INSERT INTO context_measurements(session_id,revision,measured_at,measurement_json) VALUES(?,?,?,?) ON CONFLICT(session_id) DO UPDATE SET revision=excluded.revision, measured_at=excluded.measured_at, measurement_json=excluded.measurement_json', sessionId, measuredRevision, measuredAt, JSON.stringify(measurement));
197
+ return snapshot(sessionId);
198
+ });
199
+ },
200
+ appendOriginalBatch({ sessionId, records, expectedRevision }) {
201
+ if (!Array.isArray(records)) fail('Original records must be an array');
202
+ return tx(() => {
203
+ const row = session(sessionId);
204
+ if (expectedRevision !== undefined) revision(row, expectedRevision);
205
+ let sequence = row.head_sequence;
206
+ const result = [];
207
+ for (const input of records) {
208
+ json(input);
209
+ if (!object(input)) fail('Invalid original record');
210
+ id(input.id); message(input.message); iso(input.createdAt);
211
+ const sha256 = hash(JSON.stringify(input.message));
212
+ if (input.sha256 !== undefined && input.sha256 !== sha256) fail('Original hash mismatch', 'CTX_HASH_MISMATCH');
213
+ if (input.sessionId !== undefined && input.sessionId !== sessionId) fail('Original session mismatch', 'CTX_SOURCE_INVALID');
214
+ const prior = get('SELECT sha256,record_json FROM original_records WHERE session_id=? AND id=?', sessionId, input.id);
215
+ if (prior) {
216
+ if (prior.sha256 !== sha256) fail('Original id already contains different bytes', 'CTX_RECORD_CONFLICT');
217
+ result.push(JSON.parse(prior.record_json));
218
+ continue;
219
+ }
220
+ if (input.assetRefs !== undefined) {
221
+ if (!Array.isArray(input.assetRefs)) fail('Invalid original asset references');
222
+ for (const ref of input.assetRefs) {
223
+ const assetId = typeof ref === 'string' ? ref : ref?.id;
224
+ if (!validId(assetId) || !get('SELECT id FROM record_assets WHERE session_id=? AND id=?', sessionId, assetId)) fail('Original references an absent session asset', 'CTX_SOURCE_INVALID');
225
+ }
226
+ }
227
+ const record = { ...input, schema: 'talos.context.record.v1', sessionId, sequence: ++sequence, sha256 };
228
+ run('INSERT INTO original_records(session_id,id,sequence,sha256,record_json) VALUES(?,?,?,?,?)', sessionId, record.id, sequence, sha256, JSON.stringify(record));
229
+ fault('append-after-record');
230
+ result.push(record);
231
+ }
232
+ if (sequence !== row.head_sequence) run('UPDATE context_sessions SET head_sequence=?,revision=revision+1 WHERE session_id=?', sequence, sessionId);
233
+ return { records: result, revision: row.revision + Number(sequence !== row.head_sequence), headSequence: sequence };
234
+ });
235
+ },
236
+ readOriginals({ sessionId, afterSequence = 0, throughSequence = Number.MAX_SAFE_INTEGER, ids, limit = 1000 }) {
237
+ id(sessionId, 'sessionId'); integer(afterSequence, 'afterSequence'); integer(throughSequence, 'throughSequence'); integer(limit, 'limit');
238
+ if (ids !== undefined && (!Array.isArray(ids) || ids.some(value => !validId(value)))) fail('Invalid record ids');
239
+ if (ids?.length === 0 || limit === 0) return [];
240
+ const selected = new Set(ids);
241
+ if (!ids) return parseRows('SELECT record_json FROM original_records WHERE session_id=? AND sequence>? AND sequence<=? ORDER BY sequence LIMIT ?', 'record_json', sessionId, afterSequence, throughSequence, limit);
242
+ return originals(sessionId, afterSequence, throughSequence).filter(record => selected.has(record.id)).slice(0, limit);
243
+ },
244
+ updateSessionSettings({ sessionId, settings: value, expectedRevision }) {
245
+ settings(value);
246
+ return tx(() => { revision(session(sessionId), expectedRevision); run('UPDATE context_sessions SET settings_json=?,revision=revision+1,state_revision=state_revision+1 WHERE session_id=?', JSON.stringify(value), sessionId); return snapshot(sessionId); });
247
+ },
248
+ listContextVersions({ sessionId }) {
249
+ id(sessionId, 'sessionId');
250
+ return parseRows('SELECT version_json FROM context_versions WHERE session_id=? ORDER BY ordinal DESC', 'version_json', sessionId);
251
+ },
252
+ commitContextVersion({ sessionId, expectedRevision, expectedStateRevision, jobId, version }) {
253
+ return tx(() => {
254
+ const row = session(sessionId);
255
+ revision(row, expectedRevision, integer(expectedStateRevision, 'expectedStateRevision'));
256
+ const job = jobRow(sessionId, jobId);
257
+ if (!job) fail('Context job does not exist', 'CTX_JOB_NOT_FOUND');
258
+ if (job.state === 'cancelled') fail('Cancelled job cannot publish', 'CTX_JOB_CANCELLED');
259
+ if (job.state !== 'ready') fail('Only a ready job can publish', 'CTX_JOB_NOT_READY');
260
+ if (job.baseStateRevision !== row.state_revision || job.coveredThrough !== version?.coveredThrough || job.baseRevision > row.revision) fail('Job context is stale', 'CTX_STALE_REVISION');
261
+ json(version);
262
+ if (!object(version) || version.sessionId !== sessionId || JSON.stringify(version.model) !== JSON.stringify(job.model)) fail('Version does not match job', 'CTX_SOURCE_INVALID');
263
+ // The archive verifier checks every immutable source/hash, summary and
264
+ // measurement before the transaction promotes any candidate bytes.
265
+ const preview = archive(sessionId);
266
+ const completed = { ...job, state: 'committed', versionId: version.id, updatedAt: version.createdAt };
267
+ preview.versions.unshift(version);
268
+ preview.jobs = preview.jobs.map(entry => entry.id === jobId ? completed : entry);
269
+ preview.session.jobs = preview.jobs;
270
+ preview.session.activeVersion = version;
271
+ const { manifest: unused, ...payload } = preview;
272
+ preview.manifest.payloadSha256 = hash(JSON.stringify(payload));
273
+ verifyContextArchive(preview);
274
+ writeVersion(sessionId, version);
275
+ fault('publish-after-version');
276
+ run('UPDATE compaction_jobs SET state=?,job_json=? WHERE session_id=? AND id=?', completed.state, JSON.stringify(completed), sessionId, jobId);
277
+ activate(sessionId, version, jobId);
278
+ fault('publish-after-outbox');
279
+ return version;
280
+ });
281
+ },
282
+ restoreContextVersion({ sessionId, versionId, expectedRevision, newVersionId, createdAt }) {
283
+ id(versionId); id(newVersionId); iso(createdAt);
284
+ return tx(() => {
285
+ revision(session(sessionId), expectedRevision);
286
+ const row = get('SELECT version_json FROM context_versions WHERE session_id=? AND id=?', sessionId, versionId);
287
+ if (!row) fail('Context version not found in this session', 'CTX_VERSION_NOT_FOUND');
288
+ const version = { ...JSON.parse(row.version_json), id: newVersionId, createdAt, restoredFrom: versionId };
289
+ writeVersion(sessionId, version);
290
+ fault('restore-after-version');
291
+ activate(sessionId, version, undefined, 'context.version.restored');
292
+ return version;
293
+ });
294
+ },
295
+ claimContextJob({ sessionId, job }) {
296
+ validateJob(job, sessionId);
297
+ return tx(() => {
298
+ const row = session(sessionId);
299
+ const byKey = get('SELECT job_json FROM compaction_jobs WHERE session_id=? AND idempotency_key=?', sessionId, job.idempotencyKey);
300
+ if (byKey) {
301
+ const prior = JSON.parse(byKey.job_json);
302
+ if (prior.requestFingerprint !== job.requestFingerprint) fail('Idempotency key already used for a different request', 'CTX_IDEMPOTENCY_CONFLICT');
303
+ if (prior.state === 'paused' && activeStates.includes(job.state)) {
304
+ requireNoOtherActive(sessionId, prior.id);
305
+ const resumed = { ...prior, state: job.state, updatedAt: job.updatedAt };
306
+ delete resumed.error;
307
+ run('UPDATE compaction_jobs SET state=?,job_json=? WHERE session_id=? AND id=?', resumed.state, JSON.stringify(resumed), sessionId, prior.id);
308
+ return resumed;
309
+ }
310
+ return prior;
311
+ }
312
+ if (jobRow(sessionId, job.id)) fail('Job id already exists', 'CTX_IDEMPOTENCY_CONFLICT');
313
+ if (!activeStates.includes(job.state) && job.state !== 'paused') fail('New job must be active or paused');
314
+ if (job.baseRevision !== row.revision || job.baseStateRevision !== row.state_revision || job.coveredThrough > row.head_sequence) fail('Job starts from a stale snapshot', 'CTX_STALE_REVISION');
315
+ if (activeStates.includes(job.state)) requireNoOtherActive(sessionId, job.id);
316
+ run('INSERT INTO compaction_jobs(session_id,id,idempotency_key,state,job_json) VALUES(?,?,?,?,?)', sessionId, job.id, job.idempotencyKey, job.state, JSON.stringify(job));
317
+ return job;
318
+ });
319
+ },
320
+ saveJobProgress({ sessionId, job }) {
321
+ validateJob(job, sessionId);
322
+ return tx(() => {
323
+ const prior = jobRow(sessionId, job.id);
324
+ if (!prior) fail('Context job does not exist', 'CTX_JOB_NOT_FOUND');
325
+ if (prior.state === 'cancelled' && JSON.stringify(prior) !== JSON.stringify(job)) fail('Cancelled job cannot be changed', 'CTX_JOB_CANCELLED');
326
+ if (['committed', 'failed'].includes(prior.state) && JSON.stringify(prior) !== JSON.stringify(job)) fail('Completed job cannot be changed', 'CTX_JOB_TERMINAL');
327
+ for (const key of ['schema', 'id', 'sessionId', 'idempotencyKey', 'requestFingerprint', 'kind', 'baseRevision', 'baseStateRevision', 'coveredThrough', 'model', 'createdAt']) if (JSON.stringify(prior[key]) !== JSON.stringify(job[key])) fail('Job identity and base snapshot are immutable', 'CTX_JOB_CONFLICT');
328
+ // Cancellation changes state, never replaces a concurrent progress write.
329
+ // Merge under the same SQLite transaction after validating immutable identity.
330
+ if (job.state === 'cancelled') job = { ...prior, state: 'cancelled', updatedAt: Date.parse(job.updatedAt) >= Date.parse(prior.updatedAt) ? job.updatedAt : prior.updatedAt, ...(job.error ? { error: job.error } : {}) };
331
+ if (job.state === 'committed' && prior.state !== 'committed') fail('A job may be committed only with a version', 'CTX_JOB_NOT_READY');
332
+ if (job.versionId !== prior.versionId) fail('Job version is assigned by publication', 'CTX_JOB_CONFLICT');
333
+ if (activeStates.includes(prior.state) && activeStates.includes(job.state) && activeStates.indexOf(job.state) < activeStates.indexOf(prior.state)) fail('Job phase cannot move backwards', 'CTX_JOB_CONFLICT');
334
+ if (job.progress.completed < prior.progress.completed || job.completedSegments.length < prior.completedSegments.length || prior.completedSegments.some((segment, index) => JSON.stringify(segment) !== JSON.stringify(job.completedSegments[index]))) fail('Verified job progress cannot be discarded', 'CTX_JOB_CONFLICT');
335
+ if (Date.parse(job.updatedAt) < Date.parse(prior.updatedAt)) fail('Job progress timestamp is stale', 'CTX_JOB_CONFLICT');
336
+ if (activeStates.includes(job.state)) requireNoOtherActive(sessionId, job.id);
337
+ run('UPDATE compaction_jobs SET state=?,job_json=? WHERE session_id=? AND id=?', job.state, JSON.stringify(job), sessionId, job.id);
338
+ return job;
339
+ });
340
+ },
341
+ readContextJob({ sessionId, jobId }) { return jobRow(sessionId, jobId); },
342
+ upsertProtectedFact({ sessionId, fact, expectedRevision }) {
343
+ json(fact);
344
+ if (!object(fact) || !validId(fact.id) || typeof fact.text !== 'string' || !fact.text.trim() || !['active', 'conflict', 'removed'].includes(fact.status)) fail('Invalid protected fact');
345
+ integer(fact.revision, 'fact revision');
346
+ return tx(() => {
347
+ revision(session(sessionId), expectedRevision);
348
+ const normalized = { ...fact, sources: sources(sessionId, fact.sources) };
349
+ if (fact.status === 'conflict') {
350
+ if (!object(fact.conflict) || typeof fact.conflict.proposedText !== 'string' || !fact.conflict.proposedText.trim()) fail('Invalid protected fact conflict');
351
+ normalized.conflict = { ...fact.conflict, sources: sources(sessionId, fact.conflict.sources) };
352
+ }
353
+ run('INSERT INTO protected_facts(session_id,id,fact_json) VALUES(?,?,?) ON CONFLICT(session_id,id) DO UPDATE SET fact_json=excluded.fact_json', sessionId, fact.id, JSON.stringify(normalized));
354
+ run('UPDATE context_sessions SET revision=revision+1,state_revision=state_revision+1 WHERE session_id=?', sessionId);
355
+ return normalized;
356
+ });
357
+ },
358
+ removeProtectedFact({ sessionId, factId, expectedRevision }) {
359
+ id(factId);
360
+ return tx(() => {
361
+ revision(session(sessionId), expectedRevision);
362
+ const row = get('SELECT fact_json FROM protected_facts WHERE session_id=? AND id=?', sessionId, factId);
363
+ if (!row) fail('Protected fact not found', 'CTX_FACT_NOT_FOUND');
364
+ const prior = JSON.parse(row.fact_json);
365
+ const fact = { ...prior, status: 'removed', revision: prior.revision + 1 };
366
+ delete fact.conflict;
367
+ run('UPDATE protected_facts SET fact_json=? WHERE session_id=? AND id=?', JSON.stringify(fact), sessionId, factId);
368
+ run('UPDATE context_sessions SET revision=revision+1,state_revision=state_revision+1 WHERE session_id=?', sessionId);
369
+ return fact;
370
+ });
371
+ },
372
+ readContextOutbox({ sessionId, limit = 100 }) {
373
+ id(sessionId, 'sessionId'); integer(limit, 'limit');
374
+ if (!usageBackfilled.has(sessionId) && session(sessionId, false)) {
375
+ tx(() => {
376
+ for (const row of all('SELECT operation_id,job_id,usage_json FROM usage_records WHERE session_id=? ORDER BY ordinal', sessionId)) {
377
+ enqueueUsage(sessionId, row.operation_id, row.job_id, JSON.parse(row.usage_json));
378
+ }
379
+ });
380
+ usageBackfilled.add(sessionId);
381
+ }
382
+ return parseRows('SELECT event_json FROM context_outbox WHERE session_id=? AND acknowledged=0 ORDER BY ordinal LIMIT ?', 'event_json', sessionId, limit);
383
+ },
384
+ ackContextEvent({ sessionId, eventId }) { id(sessionId, 'sessionId'); id(eventId); run('UPDATE context_outbox SET acknowledged=1 WHERE session_id=? AND id=?', sessionId, eventId); },
385
+ /*
386
+ * 25/09/2026 — ticket della CLI «riassunto rifiutato richiesto a ogni passo», decisione owner «pausa che cresce + segnale»:
387
+ * l'AVVISO che la compattazione automatica è in pausa dopo un rifiuto. Id deterministico (uno per lavoro fallito, scelto
388
+ * dal motore) e `ON CONFLICT DO NOTHING`: ogni passo del giro che lo ripete non ne aggiunge un secondo.
389
+ */
390
+ recordContextNotice({ sessionId, event }) {
391
+ id(sessionId, 'sessionId'); json(event);
392
+ if (!object(event) || event.sessionId !== sessionId) fail('A context notice belongs to its own session');
393
+ id(event.id);
394
+ run('INSERT INTO context_outbox(session_id,id,event_json) VALUES(?,?,?) ON CONFLICT(session_id,id) DO NOTHING', sessionId, event.id, JSON.stringify(event));
395
+ },
396
+ recordUsage({ sessionId, jobId = null, operationId, usage }) {
397
+ id(operationId); json(usage);
398
+ if (!object(usage)) fail('Usage must be a JSON object');
399
+ for (const [key, value] of Object.entries(usage)) {
400
+ if (['inputTokens', 'outputTokens', 'totalTokens', 'cachedTokens'].includes(key)) integer(value, key);
401
+ if (key === 'cost' && (typeof value !== 'number' || !Number.isFinite(value) || value < 0)) fail('Invalid usage cost');
402
+ if (['currency', 'requestId'].includes(key) && typeof value !== 'string') fail('Invalid usage metadata');
403
+ }
404
+ return tx(() => {
405
+ session(sessionId);
406
+ if (jobId !== null && !jobRow(sessionId, jobId)) fail('Usage job belongs to no session job', 'CTX_JOB_NOT_FOUND');
407
+ const prior = get('SELECT job_id,usage_json FROM usage_records WHERE session_id=? AND operation_id=?', sessionId, operationId);
408
+ if (prior) {
409
+ if (prior.job_id !== jobId || prior.usage_json !== JSON.stringify(usage)) fail('Usage operation contains different accounting', 'CTX_IDEMPOTENCY_CONFLICT');
410
+ enqueueUsage(sessionId, operationId, jobId, usage);
411
+ return;
412
+ }
413
+ run('INSERT INTO usage_records(session_id,operation_id,job_id,usage_json) VALUES(?,?,?,?)', sessionId, operationId, jobId, JSON.stringify(usage));
414
+ fault('usage-after-record');
415
+ enqueueUsage(sessionId, operationId, jobId, usage);
416
+ fault('usage-after-outbox');
417
+ });
418
+ },
419
+ readUsage({ sessionId }) {
420
+ id(sessionId, 'sessionId');
421
+ return all('SELECT operation_id,job_id,usage_json FROM usage_records WHERE session_id=? ORDER BY ordinal', sessionId).map(row => ({ sessionId, operationId: row.operation_id, jobId: row.job_id, usage: JSON.parse(row.usage_json) }));
422
+ },
423
+ readContextMutation({ sessionId, idempotencyKey, requestFingerprint }) {
424
+ id(sessionId); id(idempotencyKey);
425
+ if (typeof requestFingerprint !== 'string' || !/^[a-f0-9]{64}$/.test(requestFingerprint)) fail('Invalid request fingerprint');
426
+ const prior = get('SELECT request_fingerprint,result_json FROM context_mutations WHERE session_id=? AND idempotency_key=?', sessionId, idempotencyKey);
427
+ if (!prior) return null;
428
+ if (prior.request_fingerprint !== requestFingerprint) fail('Idempotency key identifies a different request', 'CTX_IDEMPOTENCY_CONFLICT');
429
+ return { result: JSON.parse(prior.result_json) };
430
+ },
431
+ putBlob({ sessionId, id: blobId, bytes, mimeType, sha256 }) {
432
+ id(blobId);
433
+ if (!(bytes instanceof Uint8Array) || typeof mimeType !== 'string' || !mimeType || mimeType.length > 256) fail('Invalid blob bytes or MIME');
434
+ const digest = hash(bytes);
435
+ if (sha256 !== undefined && digest !== sha256) fail('Blob hash mismatch', 'CTX_HASH_MISMATCH');
436
+ return tx(() => {
437
+ session(sessionId);
438
+ const prior = get('SELECT sha256,mime_type FROM record_assets WHERE session_id=? AND id=?', sessionId, blobId);
439
+ if (prior && (prior.sha256 !== digest || prior.mime_type !== mimeType)) fail('Asset id already contains different bytes or MIME', 'CTX_BLOB_CONFLICT');
440
+ run('INSERT OR IGNORE INTO content_blobs(sha256,bytes) VALUES(?,?)', digest, bytes);
441
+ run('INSERT OR IGNORE INTO record_assets(session_id,id,sha256,mime_type) VALUES(?,?,?,?)', sessionId, blobId, digest, mimeType);
442
+ return { id: blobId, sha256: digest, mimeType, byteLength: bytes.byteLength };
443
+ });
444
+ },
445
+ readBlob({ sessionId, id: blobId }) {
446
+ id(sessionId, 'sessionId'); id(blobId);
447
+ const row = get('SELECT a.id,a.sha256,a.mime_type,b.bytes FROM record_assets a JOIN content_blobs b ON a.sha256=b.sha256 WHERE a.session_id=? AND a.id=?', sessionId, blobId);
448
+ if (!row) return null;
449
+ if (hash(row.bytes) !== row.sha256) fail('Stored blob failed integrity verification', 'CTX_HASH_MISMATCH');
450
+ return { id: row.id, sha256: row.sha256, mimeType: row.mime_type, bytes: row.bytes };
451
+ },
452
+ replaceSearchChunks({ sessionId, chunks }) {
453
+ if (!Array.isArray(chunks)) fail('Chunks must be an array');
454
+ return tx(() => {
455
+ session(sessionId);
456
+ const seen = new Set();
457
+ let dimensions;
458
+ for (const chunk of chunks) {
459
+ json(chunk);
460
+ if (!object(chunk) || !validId(chunk.id) || seen.has(chunk.id) || !validId(chunk.recordId)) fail('Invalid or duplicate search chunk', 'CTX_SOURCE_INVALID');
461
+ seen.add(chunk.id);
462
+ const row = get('SELECT sequence,record_json FROM original_records WHERE session_id=? AND id=?', sessionId, chunk.recordId);
463
+ if (!row || row.sequence !== chunk.sequence || !Number.isSafeInteger(chunk.start) || chunk.start < 0 || !Number.isSafeInteger(chunk.end) || chunk.end <= chunk.start || typeof chunk.text !== 'string' || textOf(JSON.parse(row.record_json).message).slice(chunk.start, chunk.end) !== chunk.text || chunk.text.length !== chunk.end - chunk.start) fail('Chunk offsets do not match session original', 'CTX_SOURCE_INVALID');
464
+ if (chunk.embedding !== undefined) {
465
+ if (!Array.isArray(chunk.embedding) || !chunk.embedding.length || chunk.embedding.length > 65536 || chunk.embedding.some(value => typeof value !== 'number' || !Number.isFinite(value) || !Number.isFinite(Math.fround(value)))) fail('Invalid search embedding');
466
+ dimensions ??= chunk.embedding.length;
467
+ if (chunk.embedding.length !== dimensions) fail('Embedding dimensions differ');
468
+ }
469
+ }
470
+ run('DELETE FROM search_chunks WHERE session_id=?', sessionId);
471
+ for (const chunk of chunks) run('INSERT INTO search_chunks(session_id,id,record_id,sequence,text,start,end,embedding,dimensions) VALUES(?,?,?,?,?,?,?,?,?)', sessionId, chunk.id, chunk.recordId, chunk.sequence, chunk.text, chunk.start, chunk.end, chunk.embedding ? new Uint8Array(new Float32Array(chunk.embedding).buffer) : null, chunk.embedding?.length ?? null);
472
+ });
473
+ },
474
+ searchLexical({ sessionId, query, limit = 20 }) {
475
+ id(sessionId, 'sessionId'); integer(limit, 'limit');
476
+ if (typeof query !== 'string' || query.length > 8000) fail('Invalid lexical query');
477
+ const terms = query.match(/[\p{L}\p{N}_]+/gu)?.slice(0, 100) ?? [];
478
+ if (!terms.length || limit === 0) return [];
479
+ const escaped = terms.map(term => `"${term.replaceAll('"', '""')}"`).join(' OR ');
480
+ return all('SELECT c.id,c.record_id AS recordId,c.sequence,c.text,c.start,c.end,-bm25(search_chunks_fts) AS score FROM search_chunks_fts JOIN search_chunks c ON c.rowid=search_chunks_fts.rowid WHERE search_chunks_fts MATCH ? AND c.session_id=? ORDER BY score DESC,c.sequence,c.start,c.id LIMIT ?', escaped, sessionId, limit);
481
+ },
482
+ searchVector({ sessionId, embedding, limit = 20 }) {
483
+ id(sessionId, 'sessionId'); integer(limit, 'limit');
484
+ if (!vector) fail('Pinned sqlite-vec extension is unavailable', 'CTX_VECTOR_UNAVAILABLE');
485
+ if (!Array.isArray(embedding) || !embedding.length || embedding.some(value => typeof value !== 'number' || !Number.isFinite(value) || !Number.isFinite(Math.fround(value)))) fail('Invalid query embedding');
486
+ const dimensions = get('SELECT dimensions FROM search_chunks WHERE session_id=? AND embedding IS NOT NULL LIMIT 1', sessionId)?.dimensions;
487
+ if (dimensions !== undefined && dimensions !== embedding.length) fail('Query embedding dimensions differ');
488
+ return all('SELECT id,record_id AS recordId,sequence,text,start,end,1-vec_distance_cosine(embedding,?) AS score FROM search_chunks WHERE session_id=? AND embedding IS NOT NULL ORDER BY score DESC,sequence,start,id LIMIT ?', new Uint8Array(new Float32Array(embedding).buffer), sessionId, limit);
489
+ },
490
+ exportSession({ sessionId }) { return tx(() => verifyContextArchive(archive(sessionId))); },
491
+ importSession({ archive: input }) {
492
+ const verified = verifyContextArchive(input);
493
+ return tx(() => {
494
+ const { session: snap } = verified;
495
+ const sessionId = snap.sessionId;
496
+ if (session(sessionId, false)) {
497
+ const current = archive(sessionId);
498
+ if (!isDeepStrictEqual(comparableArchive(current), comparableArchive(verified))) fail('Existing context session diverges from import', 'CTX_IMPORT_CONFLICT');
499
+ return snapshot(sessionId);
500
+ }
501
+ run('INSERT INTO context_sessions(session_id,revision,state_revision,head_sequence,settings_json,metadata_json,active_version_id) VALUES(?,?,?,?,?,?,?)', sessionId, snap.revision, snap.stateRevision, snap.headSequence, JSON.stringify(snap.settings), JSON.stringify(snap.metadata), snap.activeVersion?.id ?? null);
502
+ for (const blob of verified.blobs) {
503
+ run('INSERT OR IGNORE INTO content_blobs(sha256,bytes) VALUES(?,?)', blob.sha256, Buffer.from(blob.base64, 'base64'));
504
+ run('INSERT INTO record_assets(session_id,id,sha256,mime_type) VALUES(?,?,?,?)', sessionId, blob.id, blob.sha256, blob.mimeType);
505
+ }
506
+ for (const record of verified.records) run('INSERT INTO original_records(session_id,id,sequence,sha256,record_json) VALUES(?,?,?,?,?)', sessionId, record.id, record.sequence, record.sha256, JSON.stringify(record));
507
+ fault('import-after-records');
508
+ for (const version of [...verified.versions].reverse()) writeVersion(sessionId, version);
509
+ for (const fact of verified.facts) run('INSERT INTO protected_facts(session_id,id,fact_json) VALUES(?,?,?)', sessionId, fact.id, JSON.stringify(fact));
510
+ for (const job of verified.jobs) run('INSERT INTO compaction_jobs(session_id,id,idempotency_key,state,job_json) VALUES(?,?,?,?,?)', sessionId, job.id, job.idempotencyKey, job.state, JSON.stringify(job));
511
+ for (const item of verified.usage) run('INSERT INTO usage_records(session_id,operation_id,job_id,usage_json) VALUES(?,?,?,?)', sessionId, item.operationId, item.jobId ?? null, JSON.stringify(item.usage));
512
+ for (const item of verified.mutations ?? []) run('INSERT INTO context_mutations(session_id,idempotency_key,request_fingerprint,result_json) VALUES(?,?,?,?)', sessionId, item.idempotencyKey, item.requestFingerprint, JSON.stringify(item.result));
513
+ return snapshot(sessionId);
514
+ });
515
+ },
516
+ async backup({ destinationPath }) {
517
+ if (typeof destinationPath !== 'string' || !destinationPath || destinationPath.includes('\0') || destinationPath === ':memory:') fail('Invalid backup destination');
518
+ const target = resolve(destinationPath);
519
+ if (databasePath !== ':memory:' && target.toLowerCase() === resolve(databasePath).toLowerCase()) fail('Backup destination must differ from database', 'CTX_BACKUP_CONFLICT');
520
+ if (existsSync(target)) fail('Backup destination already exists', 'CTX_BACKUP_CONFLICT');
521
+ mkdirSync(dirname(target), { recursive: true });
522
+ // Exclusive reservation prevents overwriting another backup or a raced file.
523
+ const fd = openSync(target, 'wx'); closeSync(fd);
524
+ try {
525
+ await backup(db, target);
526
+ const bytes = readFileSync(target);
527
+ return { schema: 'talos.context.backup.v1', sha256: hash(bytes), byteLength: bytes.length, createdAt: new Date().toISOString(), sqliteVersion: get('SELECT sqlite_version() AS version').version };
528
+ } catch (error) {
529
+ try { unlinkSync(target); } catch { /* Preserve original failure. */ }
530
+ throw new ContextStoreError('SQLite backup failed', 'CTX_BACKUP_FAILED', { cause: error });
531
+ }
532
+ },
533
+ health() { return { sqliteVersion: get('SELECT sqlite_version() AS version').version, fts5: !!get("SELECT name FROM sqlite_master WHERE name='search_chunks_fts'"), vector, integrity: get('PRAGMA integrity_check').integrity_check }; },
534
+ close() { db.close(); },
535
+ };
536
+
537
+ let queue = Promise.resolve();
538
+ const idempotentMethods = new Set(['updateSessionSettings', 'upsertProtectedFact', 'removeProtectedFact', 'restoreContextVersion', 'saveJobProgress']);
539
+ parentPort.on('message', request => {
540
+ queue = queue.then(async () => {
541
+ try {
542
+ if (!object(request) || !Object.hasOwn(methods, request.method) || !object(request.args)) fail('Unknown context worker operation');
543
+ const result = idempotentMethods.has(request.method) && request.args.idempotencyKey !== undefined
544
+ ? tx(() => {
545
+ const prior = methods.readContextMutation(request.args);
546
+ if (prior) return prior.result;
547
+ const output = methods[request.method](request.args);
548
+ json(output);
549
+ run('INSERT INTO context_mutations(session_id,idempotency_key,request_fingerprint,result_json) VALUES(?,?,?,?)', request.args.sessionId, request.args.idempotencyKey, request.args.requestFingerprint, JSON.stringify(output));
550
+ return output;
551
+ })
552
+ : await methods[request.method](request.args);
553
+ parentPort.postMessage({ id: request.id, result });
554
+ if (request.method === 'close') parentPort.close();
555
+ } catch (error) {
556
+ parentPort.postMessage({ id: request.id, error: { code: error?.code?.startsWith('CTX_') ? error.code : 'CTX_PERSISTENCE_FAILED', message: error?.code?.startsWith('CTX_') ? error.message : 'Context persistence operation failed' } });
557
+ }
558
+ });
559
+ });
@@ -0,0 +1,10 @@
1
+ import { ContextEngineError } from './contracts.mjs';
2
+
3
+ export function validateContextProfile(profile) {
4
+ const { windowTokens, responseReserve, method = 'heuristic' } = profile ?? {};
5
+ if (!Number.isInteger(windowTokens) || windowTokens <= 0 || !Number.isInteger(responseReserve) || responseReserve < 0 || !['runtime', 'provider', 'heuristic'].includes(method)) throw new ContextEngineError('Finestra o riserva del modello non disponibili.', 'CTX_INVALID_PROFILE');
6
+ const safetyMarginTokens = profile.marginTokens ?? Math.max(method === 'heuristic' ? 1024 : 256, Math.ceil(windowTokens * (method === 'heuristic' ? 0.15 : 0.05)));
7
+ if (!Number.isInteger(safetyMarginTokens) || safetyMarginTokens < 0) throw new ContextEngineError('Margine del contesto non valido.', 'CTX_INVALID_PROFILE');
8
+ if (windowTokens - responseReserve - safetyMarginTokens <= 0) throw new ContextEngineError('La riserva della risposta occupa la finestra disponibile.', 'CTX_CONTEXT_TOO_SMALL');
9
+ return { ...profile, windowTokens, responseReserve, method, safetyMarginTokens };
10
+ }