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,339 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
4
+
5
+ export const LOCAL_MODEL_STATES = ['incomplete', 'ready', 'failed'];
6
+
7
+ export class LocalModelStoreError extends Error {
8
+ constructor(message, code = 'MODEL_STORE_FAILED') {
9
+ super(message);
10
+ this.name = 'LocalModelStoreError';
11
+ this.code = code;
12
+ }
13
+ }
14
+
15
+ const MANIFEST_KEYS = ['id', 'repo', 'revision', 'files', 'bytes', 'sha256', 'license', 'path', 'state', 'updatedAt'];
16
+ const FILE_KEYS = ['path', 'bytes', 'sha256'];
17
+ const SHA256 = /^[a-f0-9]{64}$/i;
18
+ const REVISION = /^[a-f0-9]{40,64}$/i;
19
+ const ID = /^[a-z0-9][a-z0-9._-]{0,127}$/i;
20
+
21
+ function invalid(message) {
22
+ return new LocalModelStoreError(message, 'MODEL_INVALID');
23
+ }
24
+
25
+ function exactKeys(value, keys) {
26
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
27
+ }
28
+
29
+ function validateRelativePath(value, label) {
30
+ if (typeof value !== 'string' || value.trim() === '' || isAbsolute(value)) throw invalid(`${label} must be relative`);
31
+ const segments = value.replaceAll('\\', '/').split('/');
32
+ if (segments.some((segment) => segment === '' || segment === '.' || segment === '..')) throw invalid(`${label} contains traversal`);
33
+ }
34
+
35
+ function validateManifest(value, rootDir) {
36
+ if (!value || typeof value !== 'object' || Array.isArray(value) || !exactKeys(value, MANIFEST_KEYS)) throw invalid('manifest shape is invalid');
37
+ if (typeof value.id !== 'string' || !ID.test(value.id)) throw invalid('id is invalid');
38
+ if (typeof value.repo !== 'string' || value.repo.trim() === '') throw invalid('repo is required');
39
+ if (typeof value.revision !== 'string' || !REVISION.test(value.revision)) throw invalid('revision must be a full commit hash');
40
+ if (!Array.isArray(value.files) || value.files.length === 0) throw invalid('files are required');
41
+ let sum = 0;
42
+ for (const [index, file] of value.files.entries()) {
43
+ if (!file || typeof file !== 'object' || Array.isArray(file) || !exactKeys(file, FILE_KEYS)) throw invalid(`files[${index}] is invalid`);
44
+ validateRelativePath(file.path, `files[${index}].path`);
45
+ if (!Number.isSafeInteger(file.bytes) || file.bytes <= 0) throw invalid(`files[${index}].bytes is invalid`);
46
+ if (typeof file.sha256 !== 'string' || !SHA256.test(file.sha256)) throw invalid(`files[${index}].sha256 is invalid`);
47
+ sum += file.bytes;
48
+ }
49
+ if (!Number.isSafeInteger(value.bytes) || value.bytes <= 0 || value.bytes !== sum) throw invalid('bytes do not match file sizes');
50
+ if (typeof value.sha256 !== 'string' || !SHA256.test(value.sha256)) throw invalid('sha256 is invalid');
51
+ if (typeof value.license !== 'string' || value.license.trim() === '') throw invalid('license is required');
52
+ validateRelativePath(value.path, 'path');
53
+ const absoluteModelPath = resolve(rootDir, value.path);
54
+ const difference = relative(resolve(rootDir), absoluteModelPath);
55
+ if (difference === '..' || difference.startsWith(`..${'\\'}`) || isAbsolute(difference)) throw invalid('path escapes model root');
56
+ if (!LOCAL_MODEL_STATES.includes(value.state)) throw invalid('state is invalid');
57
+ if (typeof value.updatedAt !== 'string' || !Number.isFinite(Date.parse(value.updatedAt))) throw invalid('updatedAt is invalid');
58
+ return structuredClone(value);
59
+ }
60
+
61
+ /**
62
+ * ⭐⭐⭐ BC-13 (11/09/2026) — quante letture in volo insieme quando l'elenco è FREDDO.
63
+ *
64
+ * MISURATO su questa macchina (scratchpad/misura-concorrenza.mjs, 10 giri per riga,
65
+ * mediane), leggendo manifest + eventuale `.name.json`:
66
+ *
67
+ * 200 manifest → 1: 119,02 ms · 8: 24,20 · 16: 22,72 · **32: 22,38** · 64: 22,78 · ∞: 25,04
68
+ * 1000 manifest → 1: 626,73 ms · 8: 134,52 · 16: 129,29 · **32: 126,65** · 64: 122,62 · ∞: 126,67
69
+ *
70
+ * ⇒ L'altopiano arriva già a 16: oltre non si guadagna più niente, e `Infinity` è
71
+ * perfino PEGGIO di 32 (25,04 contro 22,38 su 200) perché il disco non va più
72
+ * veloce e la coda interna di libuv si allunga soltanto.
73
+ * ⛔ E `Promise.all` senza limite su una cartella grande è il modo classico di
74
+ * arrivare a `EMFILE: too many open files` — il consiglio unanime è una coda a
75
+ * concorrenza limitata invece di alzare il tetto dei descrittori
76
+ * (oneuptime.com «How to Fix Error: EMFILE: too many open files in Node.js»,
77
+ * 22/01/2026, e latchkey.dev «Node.js EMFILE — Fix the File-Descriptor Limit»,
78
+ * letti l'11/09/2026). Qui i file sono due per modello: 32 in volo = 32
79
+ * descrittori, un tetto che nessuna libreria esterna deve garantire.
80
+ */
81
+ const LETTURE_IN_PARALLELO = 32;
82
+
83
+ /**
84
+ * Una pozza di `limite` operai che si passano la lista: nessuna dipendenza
85
+ * esterna (`p-limit` farebbe esattamente questo), ordine del risultato
86
+ * conservato perché ogni operaio scrive nella SUA casella, non in coda.
87
+ * ⛔ Un `catch` qui non ci va: `list()` deve continuare a fallire come prima se
88
+ * un manifest è corrotto — la concorrenza cambia il COME si legge, mai il COSA
89
+ * si risponde.
90
+ */
91
+ async function mappaConLimite(elementi, limite, fn) {
92
+ const esiti = new Array(elementi.length);
93
+ let prossimo = 0;
94
+ const operai = Array.from({ length: Math.min(limite, elementi.length) }, async () => {
95
+ while (prossimo < elementi.length) {
96
+ const indice = prossimo;
97
+ prossimo += 1;
98
+ esiti[indice] = await fn(elementi[indice]);
99
+ }
100
+ });
101
+ await Promise.all(operai);
102
+ return esiti;
103
+ }
104
+
105
+ export function createLocalModelStore({ rootDir, fsImpl = {}, now = () => new Date() } = {}) {
106
+ if (typeof rootDir !== 'string' || !isAbsolute(rootDir)) throw new LocalModelStoreError('rootDir must be absolute', 'MODEL_STORE_MISCONFIGURED');
107
+ const fs = { mkdir, readFile, readdir, rename, rm, stat, writeFile, ...fsImpl };
108
+ const manifestDir = join(rootDir, 'manifests');
109
+ const locks = new Set();
110
+ const manifestPath = (id) => join(manifestDir, `${id}.json`);
111
+ const namePath = (id) => join(manifestDir, `${id}.name.json`);
112
+
113
+ /*
114
+ * ⭐⭐⭐ BC-13 — «i modelli locali devono comparire ISTANTANEI», owner 11/09/2026.
115
+ *
116
+ * ## Da dove viene questa forma: il mobile, che ci ha lavorato oggi
117
+ *
118
+ * `AVM/mobile/src/lib/models/localCatalogueSignal.ts:1-89` dice la regola per
119
+ * esteso: «un elenco che non si aggiorna da solo è un elenco che mente finché
120
+ * qualcuno non lo interroga», e «il disco è l'unica fonte che non può essere in
121
+ * ritardo». ⇒ Là chi CAMBIA il disco lo annuncia (`transfer-finished`,
122
+ * `imported`, `deleted`) e chi mostra l'elenco rilegge — senza pulsante.
123
+ *
124
+ * Qui il desktop non ha bisogno di un bus: chi cambia il disco e chi lo legge
125
+ * sono lo STESSO oggetto. Quindi l'annuncio è un contatore interno — `revisione`
126
+ * — che ogni scrittura di questo store alza. È la stessa idea, con una riga
127
+ * invece di un modulo, perché il confine fra i due lati qui non esiste.
128
+ *
129
+ * ## Perché serve ANCHE il mtime della cartella, e non basta il contatore
130
+ *
131
+ * Il contatore vede solo ciò che passa da qui. I manifest possono cambiare da
132
+ * fuori (un'altra istanza, l'owner che cancella una cartella a mano). Il mtime
133
+ * della cartella copre quel caso.
134
+ *
135
+ * ⛔ MISURATO l'11/09/2026 su questo NTFS (scratchpad/misura-varianti.mjs), non
136
+ * dedotto dalla documentazione — Microsoft Learn «File Times» descrive i tempi
137
+ * del FILE, non quelli della cartella che lo contiene:
138
+ * · aggiunta di una voce → il mtime della cartella CAMBIA ✔
139
+ * · rename di una voce dentro → CAMBIA ✔
140
+ * · riscrittura IN LOCO → NON cambia ✘
141
+ * Il terzo caso non ci riguarda: `persist()` e `renameModel()` scrivono SEMPRE
142
+ * un temporaneo e poi `rename` (è la scrittura atomica che questo file già
143
+ * faceva), quindi ogni scrittura vera passa dal caso che si vede. E se un
144
+ * giorno qualcuno riscrivesse in loco da dentro, il contatore lo prende
145
+ * comunque: le due guardie coprono buchi diversi, per questo ci sono entrambe.
146
+ *
147
+ * ## Cosa costa la guardia, e cosa risparmia (misurato, mediane)
148
+ *
149
+ * `stat` della sola cartella .............. 0,03 ms
150
+ * `list()` in serie, come prima ........... 2 modelli 1,50 ms · 50: 32,04 · 200: 129,30
151
+ * `list()` in parallelo (32) .............. 2 modelli 1,10 ms · 50: 6,75 · 200: 22,38
152
+ * `structuredClone` dell'elenco in cache .. 50: 0,077 ms · 200: 0,297 · 1000: 1,678
153
+ * ⇒ a caldo su 200 modelli: 0,03 + 0,30 ≈ 0,33 ms contro 129,30 → ~390×.
154
+ *
155
+ * ⛔ Si restituisce un `structuredClone`, non l'array in cache: prima di oggi
156
+ * ogni `list()` consegnava oggetti freschi, e chi chiama ha il diritto di
157
+ * maneggiarli. Congelarli sarebbe costato meno (0,3 ms su 200 non sono
158
+ * niente) ma avrebbe cambiato un contratto in silenzio, che è esattamente il
159
+ * genere di cura che poi rompe qualcun altro.
160
+ */
161
+ let revisione = 0;
162
+ /** @type {{revisione: number, firma: number, modelli: Array}|null} */
163
+ let elencoInCache = null;
164
+
165
+ /**
166
+ * La firma della cartella, o `null` se non si può leggere. `null` NON è una
167
+ * firma: non si confronta mai con niente, quindi una cartella che non esiste
168
+ * (o un `fsImpl` di prova senza `stat`) fa semplicemente decadere la cache
169
+ * invece di congelare una risposta sbagliata.
170
+ */
171
+ async function firmaDellaCartella() {
172
+ try {
173
+ const informazioni = await fs.stat(manifestDir);
174
+ const valore = Number(informazioni?.mtimeMs);
175
+ return Number.isFinite(valore) ? valore : null;
176
+ } catch {
177
+ return null;
178
+ }
179
+ }
180
+
181
+ async function persist(manifest) {
182
+ await fs.mkdir(manifestDir, { recursive: true });
183
+ const target = manifestPath(manifest.id);
184
+ const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
185
+ try {
186
+ await fs.writeFile(temporary, `${JSON.stringify(manifest, null, 2)}\n`, { encoding: 'utf8', flag: 'wx' });
187
+ await fs.rename(temporary, target);
188
+ revisione += 1; // BC-13: l'annuncio del mobile, ridotto a un contatore perché qui chi scrive e chi legge sono lo stesso oggetto
189
+ } catch (error) {
190
+ await fs.rm(temporary, { force: true }).catch(() => {});
191
+ if (error?.code === 'EEXIST') throw new LocalModelStoreError(`model ${manifest.id} already exists`, 'MODEL_EXISTS');
192
+ throw new LocalModelStoreError(`cannot persist model ${manifest.id}: ${error.message}`, 'MODEL_WRITE_FAILED');
193
+ }
194
+ }
195
+
196
+ async function inspect(id) {
197
+ if (typeof id !== 'string' || !ID.test(id)) throw invalid('id is invalid');
198
+ let contents;
199
+ try {
200
+ contents = await fs.readFile(manifestPath(id), 'utf8');
201
+ } catch (error) {
202
+ if (error?.code === 'ENOENT') return null;
203
+ throw new LocalModelStoreError(`cannot read model ${id}: ${error.message}`, 'MODEL_READ_FAILED');
204
+ }
205
+ try {
206
+ const manifest = validateManifest(JSON.parse(contents), rootDir);
207
+ try {
208
+ const name = await fs.readFile(namePath(id), 'utf8');
209
+ if (typeof name === 'string' && name.trim()) manifest.name = name.trim();
210
+ } catch (error) { if (error?.code !== 'ENOENT') throw new LocalModelStoreError(`cannot read model ${id} name: ${error.message}`, 'MODEL_READ_FAILED'); }
211
+ return manifest;
212
+ } catch (error) {
213
+ if (error instanceof LocalModelStoreError) throw new LocalModelStoreError(`manifest ${id} is corrupt: ${error.message}`, 'MODEL_CORRUPT');
214
+ throw new LocalModelStoreError(`manifest ${id} is not JSON`, 'MODEL_CORRUPT');
215
+ }
216
+ }
217
+
218
+ async function register(value) {
219
+ const manifest = validateManifest({ ...value, updatedAt: value?.updatedAt ?? now().toISOString() }, rootDir);
220
+ if (await inspect(manifest.id)) throw new LocalModelStoreError(`model ${manifest.id} already exists`, 'MODEL_EXISTS');
221
+ await persist(manifest);
222
+ return manifest;
223
+ }
224
+
225
+ async function setState(id, state) {
226
+ if (!LOCAL_MODEL_STATES.includes(state)) throw invalid('state is invalid');
227
+ const current = await inspect(id);
228
+ if (!current) throw new LocalModelStoreError(`model ${id} not found`, 'MODEL_NOT_FOUND');
229
+ const manifest = validateManifest({ ...current, state, updatedAt: now().toISOString() }, rootDir);
230
+ await persist(manifest);
231
+ return manifest;
232
+ }
233
+
234
+ /**
235
+ * BC-13 — `{ fresco: true }` salta la cache e rilegge il disco.
236
+ *
237
+ * ⛔ Non è un ripiego «in caso non funzioni»: è la porta per chi ha una ragione
238
+ * di sospettare il disco che questo store non può conoscere (una verifica
239
+ * dell'owner, una diagnosi). Chi non ne ha una, non la usa — altrimenti la
240
+ * cura vale zero.
241
+ */
242
+ async function list({ fresco = false } = {}) {
243
+ const firma = await firmaDellaCartella();
244
+ if (!fresco && elencoInCache && firma !== null
245
+ && elencoInCache.firma === firma && elencoInCache.revisione === revisione) {
246
+ return structuredClone(elencoInCache.modelli);
247
+ }
248
+
249
+ let entries;
250
+ try { entries = await fs.readdir(manifestDir, { withFileTypes: true }); }
251
+ catch (error) { if (error?.code === 'ENOENT') return []; throw new LocalModelStoreError(`cannot list models: ${error.message}`, 'MODEL_READ_FAILED'); }
252
+ const identificativi = [];
253
+ for (const entry of entries) {
254
+ const name = typeof entry === 'string' ? entry : entry.name;
255
+ if (!name.endsWith('.json') || name.endsWith('.name.json')) continue;
256
+ identificativi.push(name.slice(0, -5));
257
+ }
258
+ /*
259
+ * La revisione si legge PRIMA delle letture: se una scrittura arriva mentre
260
+ * stiamo leggendo, il confronto qui sotto fallisce e l'elenco non finisce in
261
+ * cache. Meglio una cache che non si popola che una cache che congela una
262
+ * fotografia scattata a metà di una scrittura.
263
+ */
264
+ const revisionePrimaDiLeggere = revisione;
265
+ const letti = await mappaConLimite(identificativi, LETTURE_IN_PARALLELO, (id) => inspect(id));
266
+ const models = letti.filter((model) => model).sort((a, b) => a.id.localeCompare(b.id));
267
+
268
+ const firmaDopo = await firmaDellaCartella();
269
+ if (firmaDopo !== null && firmaDopo === firma && revisione === revisionePrimaDiLeggere) {
270
+ elencoInCache = { revisione, firma: firmaDopo, modelli: structuredClone(models) };
271
+ } else {
272
+ elencoInCache = null;
273
+ }
274
+ return models;
275
+ }
276
+
277
+ async function lock(id) {
278
+ if (!await inspect(id)) throw new LocalModelStoreError(`model ${id} not found`, 'MODEL_NOT_FOUND');
279
+ if (locks.has(id)) throw new LocalModelStoreError(`model ${id} is already locked`, 'MODEL_LOCKED');
280
+ locks.add(id);
281
+ return true;
282
+ }
283
+
284
+ async function unlock(id) {
285
+ return locks.delete(id);
286
+ }
287
+
288
+ /**
289
+ * 06/09 (richiesta INST-DELETE-FILE): «Elimina» toglieva solo i manifest e
290
+ * lasciava i pesi sul disco. Ora cancella anche la cartella del modello, ma SOLO se sta dentro
291
+ * `rootDir` (il manifest lo garantisce già; qui si riverifica prima di un `rm` ricorsivo:
292
+ * risolvi, poi `relative()` che non inizi con `..` e non sia assoluto — openreplay «Preventing
293
+ * Path Traversal in Node.js», googleapis/nodejs-storage #2654, letti il 06/09/2026). Mai la
294
+ * radice stessa, mai un percorso fuori: in quei casi restano i manifest via e i pesi intatti.
295
+ */
296
+ async function remove(id) {
297
+ if (locks.has(id)) throw new LocalModelStoreError(`model ${id} is locked`, 'MODEL_LOCKED');
298
+ const current = await inspect(id).catch(() => null);
299
+ if (current?.path) {
300
+ // `path` è la CARTELLA del modello (download HF: `org-model/`) o il FILE principale
301
+ // (import locale: `<id>/<peso>.gguf`): si guarda sul disco, non si indovina.
302
+ try {
303
+ const radice = resolve(rootDir);
304
+ const dentro = (assoluto) => { const d = relative(radice, assoluto); return d !== '' && d !== '..' && !d.startsWith(`..${sep}`) && !d.startsWith('../') && !isAbsolute(d); };
305
+ const bersaglio = resolve(radice, current.path);
306
+ const eCartella = await fs.stat(bersaglio).then((s) => s.isDirectory()).catch(() => false);
307
+ const cartella = eCartella ? bersaglio : dirname(bersaglio);
308
+ if (!eCartella) for (const b of [bersaglio, ...(current.files || []).map((f) => resolve(cartella, f.path))]) if (dentro(b)) await fs.rm(b, { force: true });
309
+ // la cartella del modello è sua per costruzione: via anche quella, mai la radice
310
+ if (cartella !== radice && dentro(cartella)) await fs.rm(cartella, { recursive: true, force: true });
311
+ } catch {
312
+ // i pesi non si sono lasciati cancellare: i manifest vanno via lo stesso (comportamento di prima)
313
+ }
314
+ }
315
+ await fs.rm(manifestPath(id), { force: true });
316
+ await fs.rm(namePath(id), { force: true });
317
+ revisione += 1; // BC-13: un modello in meno è una notizia per l'elenco, esattamente come uno in più
318
+ return true;
319
+ }
320
+
321
+ async function renameModel(id, name) {
322
+ if (typeof name !== 'string' || name.trim().length === 0 || name.trim().length > 160) throw invalid('name is invalid');
323
+ const current = await inspect(id);
324
+ if (!current) throw new LocalModelStoreError(`model ${id} not found`, 'MODEL_NOT_FOUND');
325
+ const temporary = `${namePath(id)}.tmp-${process.pid}-${randomUUID()}`;
326
+ try {
327
+ await fs.mkdir(manifestDir, { recursive: true });
328
+ await fs.writeFile(temporary, `${name.trim()}\n`, { encoding: 'utf8', flag: 'wx' });
329
+ await fs.rename(temporary, namePath(id));
330
+ revisione += 1; // BC-13: `list()` porta anche il `name`, quindi un nome nuovo è un elenco nuovo
331
+ } catch (error) {
332
+ await fs.rm(temporary, { force: true }).catch(() => {});
333
+ throw new LocalModelStoreError(`cannot rename model ${id}: ${error.message}`, 'MODEL_WRITE_FAILED');
334
+ }
335
+ return { ...current, name: name.trim() };
336
+ }
337
+
338
+ return Object.freeze({ inspect, list, register, setState, lock, unlock, remove, rename: renameModel });
339
+ }
@@ -0,0 +1,66 @@
1
+ export const LOCAL_RUNTIME_STATES = ['unavailable', 'detected', 'loading', 'ready', 'stopping', 'failed'];
2
+ const EVENT_TYPES = ['text', 'reasoning', 'tool_call', 'status', 'error', 'done'];
3
+
4
+ function invalid(message) {
5
+ const error = new Error(message);
6
+ error.code = 'LOCAL_RUNTIME_INVALID';
7
+ return error;
8
+ }
9
+
10
+ function exactKeys(value, keys) {
11
+ return Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
12
+ }
13
+
14
+ function nonEmptyString(value, name) {
15
+ if (typeof value !== 'string' || value.trim() === '') throw invalid(`${name} is required`);
16
+ }
17
+
18
+ function validDate(value) {
19
+ return typeof value === 'string' && Number.isFinite(Date.parse(value));
20
+ }
21
+
22
+ function safeModelId(value) {
23
+ return typeof value === 'string' && value.trim() !== '' && !/^(?:[a-z]:[\\/]|[\\/]{2}|[\\/])/i.test(value);
24
+ }
25
+
26
+ export function parseLocalRuntimeSnapshot(value) {
27
+ const keys = ['runtimeId', 'version', 'backend', 'model', 'contextTokens', 'capabilities', 'state', 'observedAt'];
28
+ if (!value || typeof value !== 'object' || Array.isArray(value) || !exactKeys(value, keys)) throw invalid('runtime snapshot shape is invalid');
29
+ nonEmptyString(value.runtimeId, 'runtimeId');
30
+ nonEmptyString(value.version, 'version');
31
+ if (!value.backend || typeof value.backend !== 'object' || Array.isArray(value.backend) || !exactKeys(value.backend, ['id', 'device'])) throw invalid('backend is invalid');
32
+ nonEmptyString(value.backend.id, 'backend.id');
33
+ if (value.backend.device !== null) nonEmptyString(value.backend.device, 'backend.device');
34
+ if (value.model !== null) {
35
+ if (typeof value.model !== 'object' || Array.isArray(value.model) || !exactKeys(value.model, ['id', 'label']) || !safeModelId(value.model.id)) throw invalid('model is invalid');
36
+ if (value.model.label !== null) nonEmptyString(value.model.label, 'model.label');
37
+ }
38
+ if (value.contextTokens !== null && (!Number.isInteger(value.contextTokens) || value.contextTokens <= 0)) throw invalid('contextTokens is invalid');
39
+ const capabilityKeys = ['streaming', 'reasoning', 'toolCalls', 'multimodal'];
40
+ if (!value.capabilities || typeof value.capabilities !== 'object' || Array.isArray(value.capabilities) || !exactKeys(value.capabilities, capabilityKeys) || capabilityKeys.some((key) => typeof value.capabilities[key] !== 'boolean')) throw invalid('capabilities are invalid');
41
+ if (!LOCAL_RUNTIME_STATES.includes(value.state) || !validDate(value.observedAt)) throw invalid('state or observedAt is invalid');
42
+ if (value.state === 'ready' && (!value.model || value.contextTokens === null)) throw invalid('ready runtime must have a model and context');
43
+ return structuredClone(value);
44
+ }
45
+
46
+ export function parseLocalRuntimeEvent(value) {
47
+ if (!value || typeof value !== 'object' || Array.isArray(value) || typeof value.type !== 'string' || !EVENT_TYPES.includes(value.type)) throw invalid('runtime event type is invalid');
48
+ if (!Number.isInteger(value.seq) || value.seq < 0) throw invalid('event sequence is invalid');
49
+ const common = ['type', 'seq'];
50
+ if (value.type === 'text' || value.type === 'reasoning') {
51
+ if (!exactKeys(value, [...common, 'value']) || typeof value.value !== 'string') throw invalid(`${value.type} event is invalid`);
52
+ } else if (value.type === 'tool_call') {
53
+ if (!exactKeys(value, [...common, 'id', 'name', 'arguments'])) throw invalid('tool_call event shape is invalid');
54
+ nonEmptyString(value.id, 'tool_call.id');
55
+ nonEmptyString(value.name, 'tool_call.name');
56
+ if (typeof value.arguments !== 'string') throw invalid('tool_call.arguments is invalid');
57
+ try { JSON.parse(value.arguments); } catch { throw invalid('tool_call.arguments must be JSON'); }
58
+ } else if (value.type === 'status') {
59
+ if (!exactKeys(value, [...common, 'state']) || !LOCAL_RUNTIME_STATES.includes(value.state)) throw invalid('status event is invalid');
60
+ } else if (value.type === 'error') {
61
+ if (!exactKeys(value, [...common, 'code', 'message', 'retryable']) || typeof value.code !== 'string' || typeof value.message !== 'string' || typeof value.retryable !== 'boolean') throw invalid('error event is invalid');
62
+ } else if (!exactKeys(value, common)) {
63
+ throw invalid('done event is invalid');
64
+ }
65
+ return value.type;
66
+ }
@@ -0,0 +1,44 @@
1
+ import { parseLocalRuntimeEvent } from './local-runtime-contract.mjs';
2
+
3
+ const ENVELOPE_KEYS = ['runId', 'turnId', 'runtimeId', 'seq', 'at', 'type'];
4
+
5
+ function invalid(message) {
6
+ const error = new Error(message);
7
+ error.code = 'LOCAL_RUNTIME_INVALID';
8
+ return error;
9
+ }
10
+
11
+ function nonEmptyString(value) {
12
+ return typeof value === 'string' && value.trim() !== '';
13
+ }
14
+
15
+ export function parseRuntimeEventEnvelope(value) {
16
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw invalid('event envelope shape is invalid');
17
+ if (!ENVELOPE_KEYS.every((key) => Object.hasOwn(value, key)) || Object.keys(value).some((key) => !ENVELOPE_KEYS.includes(key) && !['value', 'id', 'name', 'arguments', 'state', 'code', 'message', 'retryable'].includes(key))) throw invalid('event envelope fields are invalid');
18
+ if (!nonEmptyString(value.runId) || !nonEmptyString(value.turnId) || !nonEmptyString(value.runtimeId) || !nonEmptyString(value.at)) throw invalid('event envelope identity is invalid');
19
+ if (!Number.isInteger(value.seq) || value.seq < 0) throw invalid('event envelope sequence is invalid');
20
+ const payload = { ...value };
21
+ delete payload.runId; delete payload.turnId; delete payload.runtimeId; delete payload.at;
22
+ parseLocalRuntimeEvent(payload);
23
+ if (!Number.isFinite(Date.parse(value.at))) throw invalid('event envelope timestamp is invalid');
24
+ return structuredClone(value);
25
+ }
26
+
27
+ export function createRuntimeEventLedger() {
28
+ const events = new Map();
29
+ return {
30
+ push(value) {
31
+ const event = parseRuntimeEventEnvelope(value);
32
+ const current = events.get(event.seq);
33
+ if (current) {
34
+ if (JSON.stringify(current) !== JSON.stringify(event)) throw invalid('event sequence conflict');
35
+ return false;
36
+ }
37
+ events.set(event.seq, event);
38
+ return true;
39
+ },
40
+ replay() {
41
+ return [...events.values()].sort((a, b) => a.seq - b.seq).map((event) => structuredClone(event));
42
+ },
43
+ };
44
+ }