talos-code 0.0.1 → 0.1.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 (401) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +111 -3
  3. package/THIRD_PARTY_NOTICES.md +40 -0
  4. package/dist/archive/zip.js +73 -0
  5. package/dist/args.js +97 -0
  6. package/dist/automations/history-store.js +34 -0
  7. package/dist/automations/policy-store.js +43 -0
  8. package/dist/automations/runner.js +107 -0
  9. package/dist/automations/schedule.js +85 -0
  10. package/dist/automations/windows-task.js +53 -0
  11. package/dist/commands/advanced-cli.js +233 -0
  12. package/dist/commands/checkpoint-cli.js +45 -0
  13. package/dist/commands/config-cli.js +107 -0
  14. package/dist/commands/context.js +17 -0
  15. package/dist/commands/extensions-cli.js +207 -0
  16. package/dist/commands/project-cli.js +74 -0
  17. package/dist/commands/project-commands.js +143 -0
  18. package/dist/commands/provider-cli.js +185 -0
  19. package/dist/commands/services-cli.js +248 -0
  20. package/dist/commands/session-cli.js +171 -0
  21. package/dist/commands/system-cli.js +405 -0
  22. package/dist/config/commands.js +60 -0
  23. package/dist/config/load.js +382 -0
  24. package/dist/config/migrations.js +48 -0
  25. package/dist/config/types.js +11 -0
  26. package/dist/diagnostics/development-log.js +154 -0
  27. package/dist/diagnostics/doctor.js +104 -0
  28. package/dist/diagnostics/redact.js +80 -0
  29. package/dist/diagnostics/zip.js +52 -0
  30. package/dist/errors.js +156 -0
  31. package/dist/events/bridge.js +130 -0
  32. package/dist/extensions/installer.js +178 -0
  33. package/dist/extensions/package-schema.js +24 -0
  34. package/dist/headless/result.js +84 -0
  35. package/dist/headless/run.js +230 -0
  36. package/dist/i18n/en/approval.js +43 -0
  37. package/dist/i18n/en/common.js +9 -0
  38. package/dist/i18n/en/credentials.js +82 -0
  39. package/dist/i18n/en/errors.js +246 -0
  40. package/dist/i18n/en/firstrun.js +89 -0
  41. package/dist/i18n/en/providers.js +37 -0
  42. package/dist/i18n/en/screen.js +736 -0
  43. package/dist/i18n/en/tools.js +127 -0
  44. package/dist/i18n/en.js +14 -0
  45. package/dist/i18n/error-view.js +307 -0
  46. package/dist/i18n/index.js +19 -0
  47. package/dist/i18n/kernel-map.js +139 -0
  48. package/dist/io.js +45 -0
  49. package/dist/main.js +330 -0
  50. package/dist/paths.js +41 -0
  51. package/dist/protocol/v2/codec.js +9 -0
  52. package/dist/protocol/v2/events.js +555 -0
  53. package/dist/protocol/v2/index.js +4 -0
  54. package/dist/protocol/v2/replay.js +28 -0
  55. package/dist/protocol/v2/types.js +1 -0
  56. package/dist/provider/control-plane.js +135 -0
  57. package/dist/provider/environment-keys.js +275 -0
  58. package/dist/provider/health.js +110 -0
  59. package/dist/provider/missing-key.js +48 -0
  60. package/dist/provider/model-catalog.js +168 -0
  61. package/dist/provider/provider-text.js +13 -0
  62. package/dist/provider/store.js +95 -0
  63. package/dist/provider/system-keyring.js +214 -0
  64. package/dist/runtime/active-run.js +63 -0
  65. package/dist/runtime/agent-tree.js +74 -0
  66. package/dist/runtime/attachments.js +84 -0
  67. package/dist/runtime/brokered-executor.js +395 -0
  68. package/dist/runtime/context-status.js +76 -0
  69. package/dist/runtime/create-runtime.js +111 -0
  70. package/dist/runtime/model-profile.js +361 -0
  71. package/dist/runtime/output-store.js +137 -0
  72. package/dist/runtime/provider-attempts.js +185 -0
  73. package/dist/runtime/replay-buffer.js +153 -0
  74. package/dist/runtime/repo.js +95 -0
  75. package/dist/runtime/session-facade.js +135 -0
  76. package/dist/runtime/session-summary.js +125 -0
  77. package/dist/runtime/supervisor.js +262 -0
  78. package/dist/runtime/talos-composition.js +1006 -0
  79. package/dist/runtime/types.js +5 -0
  80. package/dist/runtime/usage-snapshot.js +82 -0
  81. package/dist/security/auto-classifier.js +38 -0
  82. package/dist/security/credential-free-environment.js +54 -0
  83. package/dist/security/evaluate.js +243 -0
  84. package/dist/security/execution-backends.js +236 -0
  85. package/dist/security/execution-broker.js +102 -0
  86. package/dist/security/forge-scan.js +74 -0
  87. package/dist/security/from-approval.js +39 -0
  88. package/dist/security/mxc-execution-backend.js +281 -0
  89. package/dist/security/permission-engine.js +138 -0
  90. package/dist/security/permission-explanation.js +54 -0
  91. package/dist/security/persist.js +179 -0
  92. package/dist/security/plugin-guard.js +230 -0
  93. package/dist/security/process-tree-evidence-store.js +74 -0
  94. package/dist/security/process-tree-probe.js +554 -0
  95. package/dist/security/project-resource-inventory.js +186 -0
  96. package/dist/security/project-trust-gate.js +38 -0
  97. package/dist/security/project-trust.js +367 -0
  98. package/dist/security/rule-parser.js +201 -0
  99. package/dist/security/shell-segmentation.js +68 -0
  100. package/dist/security/trust-authority.js +324 -0
  101. package/dist/security/types.js +1 -0
  102. package/dist/security/workspace-identity.js +102 -0
  103. package/dist/services/automation-facade.js +59 -0
  104. package/dist/services/forge-facade.js +77 -0
  105. package/dist/services/hook-facade.js +240 -0
  106. package/dist/services/index.js +16 -0
  107. package/dist/services/library-facade.js +174 -0
  108. package/dist/services/mcp-facade.js +348 -0
  109. package/dist/services/memory-facade.js +126 -0
  110. package/dist/services/notes-facade.js +157 -0
  111. package/dist/services/plugin-facade.js +308 -0
  112. package/dist/services/research-facade.js +110 -0
  113. package/dist/services/task-board-facade.js +236 -0
  114. package/dist/services/workflow-catalog.js +246 -0
  115. package/dist/sessions/export-format.js +98 -0
  116. package/dist/sessions/metadata-store.js +160 -0
  117. package/dist/sessions/share.js +122 -0
  118. package/dist/sessions/transfer.js +16 -0
  119. package/dist/subcommands.js +62 -0
  120. package/dist/tui/agent-roster.js +36 -0
  121. package/dist/tui/app.js +3761 -0
  122. package/dist/tui/approval.js +36 -0
  123. package/dist/tui/boot/boot-sequence.js +76 -0
  124. package/dist/tui/boot/cinematic.js +243 -0
  125. package/dist/tui/boot/desktop-logo.js +82 -0
  126. package/dist/tui/boot.js +3 -0
  127. package/dist/tui/busy-input.js +58 -0
  128. package/dist/tui/catalog-service.js +559 -0
  129. package/dist/tui/components/assistant-stream.js +1 -0
  130. package/dist/tui/components/command-menu.js +68 -0
  131. package/dist/tui/components/composer.js +219 -0
  132. package/dist/tui/components/diff.js +35 -0
  133. package/dist/tui/components/footer.js +48 -0
  134. package/dist/tui/components/header.js +6 -0
  135. package/dist/tui/components/markdown.js +305 -0
  136. package/dist/tui/components/status-indicator.js +32 -0
  137. package/dist/tui/components/terminal-shell.js +205 -0
  138. package/dist/tui/components/thinking-row.js +19 -0
  139. package/dist/tui/components/tool-row.js +97 -0
  140. package/dist/tui/components/transcript-virtualizer.js +165 -0
  141. package/dist/tui/components/transcript.js +43 -0
  142. package/dist/tui/diff-model.js +77 -0
  143. package/dist/tui/editor-history.js +21 -0
  144. package/dist/tui/editor.js +210 -0
  145. package/dist/tui/event-adapter.js +436 -0
  146. package/dist/tui/exit-output.js +58 -0
  147. package/dist/tui/external-editor.js +53 -0
  148. package/dist/tui/file-completion.js +89 -0
  149. package/dist/tui/focus-manager.js +5 -0
  150. package/dist/tui/highlight.js +30 -0
  151. package/dist/tui/input-router.js +18 -0
  152. package/dist/tui/interrupt.js +99 -0
  153. package/dist/tui/keybindings.js +194 -0
  154. package/dist/tui/keymap-resolver.js +91 -0
  155. package/dist/tui/launch-state.js +139 -0
  156. package/dist/tui/line-diff.js +57 -0
  157. package/dist/tui/live-activity.js +45 -0
  158. package/dist/tui/metrics.js +123 -0
  159. package/dist/tui/onboarding.js +36 -0
  160. package/dist/tui/overlays/agent-tree.js +39 -0
  161. package/dist/tui/overlays/approval-dialog.js +640 -0
  162. package/dist/tui/overlays/automation-center.js +51 -0
  163. package/dist/tui/overlays/checkpoint-picker.js +47 -0
  164. package/dist/tui/overlays/context-inspector.js +36 -0
  165. package/dist/tui/overlays/effort-line.js +110 -0
  166. package/dist/tui/overlays/forge-center.js +46 -0
  167. package/dist/tui/overlays/help-dialog.js +16 -0
  168. package/dist/tui/overlays/history-picker.js +37 -0
  169. package/dist/tui/overlays/hook-center.js +70 -0
  170. package/dist/tui/overlays/library-center.js +55 -0
  171. package/dist/tui/overlays/mcp-center.js +60 -0
  172. package/dist/tui/overlays/memory-center.js +63 -0
  173. package/dist/tui/overlays/model-picker.js +72 -0
  174. package/dist/tui/overlays/notes-center.js +53 -0
  175. package/dist/tui/overlays/overlay-host.js +8 -0
  176. package/dist/tui/overlays/plugin-center.js +76 -0
  177. package/dist/tui/overlays/provider-picker.js +145 -0
  178. package/dist/tui/overlays/queue-editor.js +32 -0
  179. package/dist/tui/overlays/research-center.js +59 -0
  180. package/dist/tui/overlays/scroll-window.js +234 -0
  181. package/dist/tui/overlays/session-picker.js +115 -0
  182. package/dist/tui/overlays/tasks-center.js +83 -0
  183. package/dist/tui/overlays/theme-picker.js +21 -0
  184. package/dist/tui/overlays/transcript-search.js +58 -0
  185. package/dist/tui/overlays/trust-center.js +29 -0
  186. package/dist/tui/overlays/workflow-center.js +46 -0
  187. package/dist/tui/project-file-index.js +214 -0
  188. package/dist/tui/project-references.js +85 -0
  189. package/dist/tui/project-trust-prompt.js +287 -0
  190. package/dist/tui/prompt-history-store.js +116 -0
  191. package/dist/tui/queue-store.js +110 -0
  192. package/dist/tui/regions/budget.js +59 -0
  193. package/dist/tui/regions/views.js +96 -0
  194. package/dist/tui/render-coordinator.js +148 -0
  195. package/dist/tui/render-scheduler.js +4 -0
  196. package/dist/tui/run.js +69 -0
  197. package/dist/tui/selection-list.js +29 -0
  198. package/dist/tui/session-controller.js +778 -0
  199. package/dist/tui/session-export.js +54 -0
  200. package/dist/tui/shell-input.js +35 -0
  201. package/dist/tui/shell-layout.js +48 -0
  202. package/dist/tui/shell-model.js +98 -0
  203. package/dist/tui/slash-commands.js +80 -0
  204. package/dist/tui/state.js +151 -0
  205. package/dist/tui/status-bar.js +125 -0
  206. package/dist/tui/status-view.js +40 -0
  207. package/dist/tui/terminal-capabilities.js +9 -0
  208. package/dist/tui/terminal-session.js +11 -0
  209. package/dist/tui/text-width.js +66 -0
  210. package/dist/tui/theme-catalog.js +25 -0
  211. package/dist/tui/theme-store.js +23 -0
  212. package/dist/tui/theme.js +23 -0
  213. package/dist/tui/tool-display.js +135 -0
  214. package/dist/tui/tool-facts.js +1 -0
  215. package/dist/tui/tools/bash-renderer.js +32 -0
  216. package/dist/tui/tools/change.js +61 -0
  217. package/dist/tui/tools/edit-renderer.js +17 -0
  218. package/dist/tui/tools/generic-renderer.js +13 -0
  219. package/dist/tui/tools/list-renderer.js +14 -0
  220. package/dist/tui/tools/read-renderer.js +13 -0
  221. package/dist/tui/tools/registry.js +20 -0
  222. package/dist/tui/tools/row-format.js +187 -0
  223. package/dist/tui/tools/search-renderer.js +38 -0
  224. package/dist/tui/tools/shared.js +97 -0
  225. package/dist/tui/tools/write-renderer.js +15 -0
  226. package/dist/tui/transcript-model.js +441 -0
  227. package/dist/tui/ui-preferences.js +79 -0
  228. package/dist/tui/usage-view.js +77 -0
  229. package/dist/tui/vim-mode.js +99 -0
  230. package/dist/update/check.js +15 -0
  231. package/dist/update/npm.js +51 -0
  232. package/dist/update/run.js +129 -0
  233. package/dist/version.js +2 -0
  234. package/dist/workspace/checkpoint-store.js +210 -0
  235. package/dist/workspace/checkpoint.js +413 -0
  236. package/dist/workspace/restore.js +232 -0
  237. package/package.json +63 -5
  238. package/vendor/context-engine/package.json +11 -0
  239. package/vendor/context-engine/src/compaction-planner.mjs +57 -0
  240. package/vendor/context-engine/src/contracts.mjs +48 -0
  241. package/vendor/context-engine/src/engine.mjs +445 -0
  242. package/vendor/context-engine/src/node/context-export.mjs +164 -0
  243. package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
  244. package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
  245. package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
  246. package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
  247. package/vendor/context-engine/src/profiles.mjs +10 -0
  248. package/vendor/context-engine/src/retrieval.mjs +104 -0
  249. package/vendor/context-engine/src/summary.mjs +97 -0
  250. package/vendor/context-engine/src/usage.mjs +34 -0
  251. package/vendor/harness-ui/package.json +38 -0
  252. package/vendor/harness-ui/src/acp-agent.mjs +350 -0
  253. package/vendor/harness-ui/src/agent-service.mjs +2188 -0
  254. package/vendor/harness-ui/src/agui-events.mjs +452 -0
  255. package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
  256. package/vendor/harness-ui/src/artifact-store.mjs +39 -0
  257. package/vendor/harness-ui/src/assistenza.mjs +123 -0
  258. package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
  259. package/vendor/harness-ui/src/automation-store.mjs +145 -0
  260. package/vendor/harness-ui/src/browser-annota.mjs +639 -0
  261. package/vendor/harness-ui/src/browser-frame.mjs +211 -0
  262. package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
  263. package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
  264. package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
  265. package/vendor/harness-ui/src/browser-stream.mjs +445 -0
  266. package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
  267. package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
  268. package/vendor/harness-ui/src/config.mjs +697 -0
  269. package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
  270. package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
  271. package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
  272. package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
  273. package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
  274. package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
  275. package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
  276. package/vendor/harness-ui/src/context-runtime.mjs +118 -0
  277. package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
  278. package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
  279. package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
  280. package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
  281. package/vendor/harness-ui/src/custom-task.mjs +171 -0
  282. package/vendor/harness-ui/src/doctor.mjs +142 -0
  283. package/vendor/harness-ui/src/document-filename.mjs +97 -0
  284. package/vendor/harness-ui/src/document-generator.mjs +493 -0
  285. package/vendor/harness-ui/src/document-report.mjs +331 -0
  286. package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
  287. package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
  288. package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
  289. package/vendor/harness-ui/src/forge-contract.mjs +221 -0
  290. package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
  291. package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
  292. package/vendor/harness-ui/src/generation-idle.mjs +332 -0
  293. package/vendor/harness-ui/src/gguf-header.mjs +207 -0
  294. package/vendor/harness-ui/src/git-service.mjs +626 -0
  295. package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
  296. package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
  297. package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
  298. package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
  299. package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
  300. package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
  301. package/vendor/harness-ui/src/hook-registry.mjs +186 -0
  302. package/vendor/harness-ui/src/http-app.mjs +6259 -0
  303. package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
  304. package/vendor/harness-ui/src/id-archivio.mjs +27 -0
  305. package/vendor/harness-ui/src/image-generator.mjs +143 -0
  306. package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
  307. package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
  308. package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
  309. package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
  310. package/vendor/harness-ui/src/library-store.mjs +652 -0
  311. package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
  312. package/vendor/harness-ui/src/local-model-store.mjs +339 -0
  313. package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
  314. package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
  315. package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
  316. package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
  317. package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
  318. package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
  319. package/vendor/harness-ui/src/mcp-client.mjs +98 -0
  320. package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
  321. package/vendor/harness-ui/src/mcp-session.mjs +177 -0
  322. package/vendor/harness-ui/src/memory-store.mjs +227 -0
  323. package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
  324. package/vendor/harness-ui/src/model-catalog.mjs +129 -0
  325. package/vendor/harness-ui/src/model-destination.mjs +189 -0
  326. package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
  327. package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
  328. package/vendor/harness-ui/src/notes-store.mjs +250 -0
  329. package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
  330. package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
  331. package/vendor/harness-ui/src/path-policy.mjs +442 -0
  332. package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
  333. package/vendor/harness-ui/src/plugin-session.mjs +180 -0
  334. package/vendor/harness-ui/src/process-policy.mjs +345 -0
  335. package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
  336. package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
  337. package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
  338. package/vendor/harness-ui/src/provider-probe.mjs +582 -0
  339. package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
  340. package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
  341. package/vendor/harness-ui/src/public-problem.mjs +109 -0
  342. package/vendor/harness-ui/src/research/card.mjs +235 -0
  343. package/vendor/harness-ui/src/research/citations.mjs +142 -0
  344. package/vendor/harness-ui/src/research/collector.mjs +275 -0
  345. package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
  346. package/vendor/harness-ui/src/research/dossier.mjs +114 -0
  347. package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
  348. package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
  349. package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
  350. package/vendor/harness-ui/src/research/independence.mjs +159 -0
  351. package/vendor/harness-ui/src/research/ledger.mjs +166 -0
  352. package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
  353. package/vendor/harness-ui/src/research/narration.mjs +181 -0
  354. package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
  355. package/vendor/harness-ui/src/research/opposing.mjs +305 -0
  356. package/vendor/harness-ui/src/research/outline.mjs +111 -0
  357. package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
  358. package/vendor/harness-ui/src/research/pdf.mjs +291 -0
  359. package/vendor/harness-ui/src/research/plan.mjs +301 -0
  360. package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
  361. package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
  362. package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
  363. package/vendor/harness-ui/src/research/recheck.mjs +194 -0
  364. package/vendor/harness-ui/src/research/report.mjs +203 -0
  365. package/vendor/harness-ui/src/research/run.mjs +527 -0
  366. package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
  367. package/vendor/harness-ui/src/research/verification.mjs +572 -0
  368. package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
  369. package/vendor/harness-ui/src/research-store.mjs +1133 -0
  370. package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
  371. package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
  372. package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
  373. package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
  374. package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
  375. package/vendor/harness-ui/src/search-source-store.mjs +172 -0
  376. package/vendor/harness-ui/src/session-registry.mjs +6095 -0
  377. package/vendor/harness-ui/src/session-store.mjs +220 -0
  378. package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
  379. package/vendor/harness-ui/src/setup-stato.mjs +31 -0
  380. package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
  381. package/vendor/harness-ui/src/skill-registry.mjs +120 -0
  382. package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
  383. package/vendor/harness-ui/src/static-files.mjs +96 -0
  384. package/vendor/harness-ui/src/stream-partition.mjs +123 -0
  385. package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
  386. package/vendor/harness-ui/src/task-catalog.mjs +65 -0
  387. package/vendor/harness-ui/src/tasks-store.mjs +220 -0
  388. package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
  389. package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
  390. package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
  391. package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
  392. package/vendor/harness-ui/src/usage-cache.mjs +315 -0
  393. package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
  394. package/vendor/harness-ui/src/workspace-context.mjs +124 -0
  395. package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
  396. package/vendor/harness-ui/src/workspace-files.mjs +589 -0
  397. package/vendor/harness-ui/src/workspace-info.mjs +189 -0
  398. package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
  399. package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
  400. package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
  401. package/vendor/manifest.json +170 -0
@@ -0,0 +1,65 @@
1
+ /**
2
+ * task-catalog.mjs — quali task sono LANCIABILI da Harness UI, e come
3
+ * ottenere una cartella vera per farli girare. Piano
4
+ * `elegant-spinning-dongarra.md`, FASE 1, §1.5 (Opzione A).
5
+ *
6
+ * ⛔⛔ ALLOWLIST STRETTA, stesso principio di `path-policy.mjs`: un id che non
7
+ * è testualmente uno dei task del corpus non produce MAI una cartella. Prima
8
+ * di questo file, Harness UI era read-only per costruzione (`http-app.mjs`
9
+ * rifiuta ogni metodo che non sia GET/HEAD) — questo è il PRIMO punto dove
10
+ * un browser può far succedere qualcosa di reale sul disco, quindi l'elenco
11
+ * ammesso non è "qualunque cartella passi", è SOLO ciò che è già nel corpus.
12
+ *
13
+ * Il catalogo non possiede più un corpus implicito: riceve un provider
14
+ * esplicito dal runtime configurato. Senza provider il risultato è
15
+ * `TASK_CATALOG_UNAVAILABLE`, non una fixture o un import da un checkout
16
+ * fratello. Questo mantiene la promessa di portabilità del desktop.
17
+ */
18
+ export class TaskCatalogError extends Error {
19
+ constructor(message, code = 'TASK_NOT_ALLOWED') {
20
+ super(message);
21
+ this.name = 'TaskCatalogError';
22
+ this.code = code;
23
+ }
24
+ }
25
+
26
+ function providerRequired(provider) {
27
+ if (!provider || typeof provider.list !== 'function' || typeof provider.prepare !== 'function') {
28
+ throw new TaskCatalogError('Il catalogo task non è disponibile in questa installazione. Configura il runtime proprietario.', 'TASK_CATALOG_UNAVAILABLE');
29
+ }
30
+ return provider;
31
+ }
32
+
33
+ /**
34
+ * Un elenco LEGGERO — mai il corpo intero della consegna, che può essere
35
+ * lungo (i task storia, quando arriveranno, elencano casi di test interi).
36
+ * Chi avvia una sessione riceve la consegna vera da `preparaEsecuzione`, non
37
+ * da qui: questa funzione è per un menu, non per lanciare niente.
38
+ */
39
+ export function listaTaskDisponibili(provider) {
40
+ return providerRequired(provider).list().map((task) => ({
41
+ id: task.id,
42
+ progetto: task.progetto,
43
+ difficolta: task.difficolta,
44
+ consegnaCorta: task.consegnaCorta,
45
+ }));
46
+ }
47
+
48
+ /**
49
+ * Da un id ammesso a una cartella VERA e usa-e-getta, pronta per
50
+ * `talosLavora`. Lancia `TaskCatalogError` su qualunque id che non sia
51
+ * ESATTAMENTE uno dei task del corpus — nessuna normalizzazione, nessun
52
+ * fallback: lo stesso stile di `assertSafeSegment` in `path-policy.mjs`.
53
+ *
54
+ * @returns {{cartella:string, comandoProva:string, task:object, pulisci:()=>void}}
55
+ */
56
+ export function preparaEsecuzione(taskId, provider) {
57
+ if (typeof taskId !== 'string' || taskId.length === 0) {
58
+ throw new TaskCatalogError('Id task non valido', 'QUERY_INVALID');
59
+ }
60
+ const risultato = providerRequired(provider).prepare(taskId);
61
+ if (!risultato || typeof risultato !== 'object' || typeof risultato.cartella !== 'string' || !risultato.task) {
62
+ throw new TaskCatalogError('Il catalogo task ha restituito una sessione non valida.', 'TASK_CATALOG_INVALID');
63
+ }
64
+ return risultato;
65
+ }
@@ -0,0 +1,220 @@
1
+ /**
2
+ * tasks-store.mjs — FASE N, quinto sistema (30/8), piano
3
+ * `elegant-spinning-dongarra.md`. Owner: "TUTTI i 5 sistemi rimasti, uno
4
+ * dopo l'altro".
5
+ *
6
+ * ⛔⛔⛔ GLOBALE, non per-progetto — stesso principio di notes-store.mjs
7
+ * (letto alla fonte: `mobile/src/lib/tools/tasksWriteTools.ts`+
8
+ * `readTools.ts`, `mobile/src/persistence/chatDatabaseSchema.ts`).
9
+ * "Ricordami di chiamare l'idraulico" non riguarda nessun progetto di
10
+ * codice — un desktop che rendesse le attività per-progetto le
11
+ * renderebbe invisibili da qualunque altra sessione. Storage accanto a
12
+ * `server.mjs`, stesso pattern REALE di `.automations/`/`.notes-store/`.
13
+ *
14
+ * ⛔ `schedule_json`/`instruction`/`last_run_at` (schema mobile
15
+ * `talos_tasks`) NON portati: colonne presenti nello schema mobile ma
16
+ * NON esposte da NESSUN tool mobile oggi ("pianificare aggiungerà un
17
+ * campo, non un'altra entità" — mai arrivato, verificato leggendo
18
+ * `TalosTasksWriteSources` per intero, zero riferimento a schedule).
19
+ * Harness Desktop ha già un proprio sistema di scheduling (Automazioni,
20
+ * `automation-store.mjs`, chiuso 27/8) — quando/se un task deve
21
+ * ripartire da solo, quella è la casa giusta, non una seconda qui.
22
+ *
23
+ * ⛔ Stessa CONVENZIONE DI CHIAMATA di `notes-store.mjs`/`library-store.mjs`
24
+ * (`funzione({cartella, ...}, deps={})`), per lo stesso motivo: va
25
+ * agganciato negli stessi punti di contatto I/O iniettabili di
26
+ * `agent-service.mjs`.
27
+ *
28
+ * ⛔ Un file JSON per attività (non un unico array) — stessa classe di
29
+ * bug (riscrittura invece di accodamento) già evitata altrove in
30
+ * questo progetto (session-store.mjs).
31
+ */
32
+ import { randomUUID } from 'node:crypto';
33
+ import { promises as fsp } from 'node:fs';
34
+ import { join } from 'node:path';
35
+ import { idArchivioValido } from './id-archivio.mjs';
36
+
37
+ const TITOLO_MASSIMO = 200;
38
+ const DESCRIZIONE_MASSIMA = 2_000;
39
+ const PRIORITA = Object.freeze(['low', 'normal', 'high']);
40
+ const STATI = Object.freeze(['todo', 'doing', 'done']);
41
+
42
+ /** ⭐ Stesso pattern REALE di `.automations/`/`.notes-store/`: il nome che `server.mjs` userà per il percorso di default, esportato per non duplicarlo lì. */
43
+ export const CARTELLA_ATTIVITA = '.tasks-store';
44
+
45
+ export class TaskStoreError extends Error {
46
+ constructor(message, code = 'TASK_INVALID') {
47
+ super(message);
48
+ this.name = 'TaskStoreError';
49
+ this.code = code;
50
+ }
51
+ }
52
+
53
+ function percorsoDi(cartella, id) {
54
+ // ⛔ 14/09: l'id arriva da fuori (indirizzo HTTP, attrezzo del modello) — un id fuori grammatica non nomina nessun file e
55
+ // torna `null`, mai un `join` che con `..` uscirebbe dalla cartella. Vedi id-archivio.mjs.
56
+ if (!idArchivioValido(id)) return null;
57
+ return join(cartella, `${id}.json`);
58
+ }
59
+
60
+ function validaTitolo(title) {
61
+ if (typeof title !== 'string' || title.trim().length === 0 || title.length > TITOLO_MASSIMO) {
62
+ throw new TaskStoreError(`title deve avere 1-${TITOLO_MASSIMO} caratteri`, 'TASK_INVALID');
63
+ }
64
+ return title.trim();
65
+ }
66
+
67
+ /** `null`/assente sono la stessa cosa (nessun dettaglio) — una descrizione fatta di soli spazi diventa `null`, stesso principio del tool mobile. */
68
+ function validaDescrizione(description) {
69
+ if (description === undefined || description === null) return null;
70
+ if (typeof description !== 'string' || description.length > DESCRIZIONE_MASSIMA) {
71
+ throw new TaskStoreError(`description deve avere al massimo ${DESCRIZIONE_MASSIMA} caratteri`, 'TASK_INVALID');
72
+ }
73
+ return description.trim() || null;
74
+ }
75
+
76
+ function validaPriorita(priority) {
77
+ if (!PRIORITA.includes(priority)) {
78
+ throw new TaskStoreError(`priority deve essere una fra ${PRIORITA.join('/')}`, 'TASK_INVALID');
79
+ }
80
+ return priority;
81
+ }
82
+
83
+ function validaStato(status) {
84
+ if (!STATI.includes(status)) {
85
+ throw new TaskStoreError(`status deve essere uno fra ${STATI.join('/')}`, 'TASK_INVALID');
86
+ }
87
+ return status;
88
+ }
89
+
90
+ /*
91
+ * ⛔ 11/09 — CHI ha scritto questa attività, stesso identico principio (e stesso default) di
92
+ * `ORIGINI` in notes-store.mjs: due porte sole — gli attrezzi `tasks_*` del modello e le
93
+ * rotte HTTP della persona — e ciò che è già sul disco viene tutto dal modello, perché fino
94
+ * a oggi la persona non aveva una porta per scrivere.
95
+ */
96
+ const ORIGINI = Object.freeze(['persona', 'modello']);
97
+
98
+ function validaOrigine(origine) {
99
+ if (!ORIGINI.includes(origine)) {
100
+ throw new TaskStoreError(`origine deve essere una fra ${ORIGINI.join('/')}`, 'TASK_INVALID');
101
+ }
102
+ return origine;
103
+ }
104
+
105
+ /**
106
+ * La forma PUBBLICA di un'attività — l'unica che esce da questo prodotto.
107
+ * ⛔ `fatta` è CALCOLATA da `stato`, mai scritta sul disco: due campi che dicono la stessa cosa
108
+ * e possono divergere sono un difetto, non una comodità. Esce dalla porta perché un pannello
109
+ * ha bisogno di una casella da spuntare, ma la verità resta una sola (`stato`).
110
+ * ⛔ Nessuna `scadenza`: non esiste in questo magazzino e non esiste in nessun attrezzo del
111
+ * modello (vedi l'intestazione di questo file sulle colonne mobile NON portate). Inventarla
112
+ * qui vorrebbe dire un campo che solo la persona può scrivere e che il modello non vedrà mai
113
+ * — la asimmetria che questo porto esiste per evitare.
114
+ */
115
+ export function formaPubblicaAttivita(voce) {
116
+ return {
117
+ id: voce.id,
118
+ titolo: voce.titolo,
119
+ descrizione: voce.descrizione ?? null,
120
+ stato: voce.stato,
121
+ fatta: voce.stato === 'done',
122
+ priorita: voce.priorita,
123
+ creataAlle: voce.creataAlle ?? null,
124
+ aggiornataAlle: voce.aggiornataAlle ?? null,
125
+ origine: ORIGINI.includes(voce.origine) ? voce.origine : 'modello',
126
+ };
127
+ }
128
+
129
+ /** Più recentemente aggiornate per prime — stesso ordine di `tasks_list` mobile. Cartella assente ⇒ `[]`, mai un errore. */
130
+ export async function elencaAttivita({ cartella }, deps = {}) {
131
+ const readdirFn = deps.readdirFn ?? fsp.readdir;
132
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
133
+ let nomi;
134
+ try {
135
+ nomi = await readdirFn(cartella);
136
+ } catch {
137
+ return [];
138
+ }
139
+ const voci = [];
140
+ for (const nome of nomi) {
141
+ if (!nome.endsWith('.json')) continue;
142
+ try {
143
+ voci.push(JSON.parse(await readFileFn(join(cartella, nome), 'utf8')));
144
+ } catch { /* una voce corrotta non deve nascondere le altre */ }
145
+ }
146
+ return voci.sort((a, b) => b.aggiornataAlle.localeCompare(a.aggiornataAlle) || a.id.localeCompare(b.id));
147
+ }
148
+
149
+ /** Un id assente torna `null`, mai un'eccezione. */
150
+ export async function leggiAttivita({ cartella, id }, deps = {}) {
151
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
152
+ const percorso = percorsoDi(cartella, id);
153
+ if (!percorso) return null; // ⛔ 14/09: id fuori grammatica = «non c'è», mai una lettura fuori dalla cartella
154
+ try {
155
+ return JSON.parse(await readFileFn(percorso, 'utf8'));
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
160
+
161
+ /** @returns l'attività creata: `{id, titolo, descrizione, priorita, stato:'todo', creataAlle, aggiornataAlle}`. @throws {TaskStoreError} TASK_INVALID PRIMA di ogni I/O. */
162
+ export async function creaAttivita({ cartella, title, description, priority = 'normal', origine = 'modello' }, deps = {}) {
163
+ const titolo = validaTitolo(title);
164
+ const descrizione = validaDescrizione(description);
165
+ const priorita = validaPriorita(priority);
166
+ const daChi = validaOrigine(origine);
167
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
168
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
169
+ const clockFn = deps.clockFn ?? (() => new Date());
170
+ const ora = clockFn().toISOString();
171
+ const voce = { id: randomUUID(), titolo, descrizione, priorita, stato: 'todo', origine: daChi, creataAlle: ora, aggiornataAlle: ora };
172
+ await mkdirFn(cartella, { recursive: true });
173
+ await writeFileFn(percorsoDi(cartella, voce.id), JSON.stringify(voce, null, 2), 'utf8');
174
+ return voce;
175
+ }
176
+
177
+ /**
178
+ * Solo title/description/priority — MAI lo stato (quello è
179
+ * `completaAttivita`, stesso confine del tool mobile: "Do NOT use this
180
+ * to mark something done"). Solo i campi passati cambiano.
181
+ * @throws {TaskStoreError} TASK_NOT_FOUND se l'id non esiste.
182
+ */
183
+ export async function aggiornaAttivita({ cartella, id, title, description, priority }, deps = {}) {
184
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
185
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
186
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
187
+ const clockFn = deps.clockFn ?? (() => new Date());
188
+ const voce = await leggiAttivita({ cartella, id }, { readFileFn });
189
+ if (!voce) throw new TaskStoreError(`nessuna attività con id ${id}`, 'TASK_NOT_FOUND');
190
+ if (title !== undefined) voce.titolo = validaTitolo(title);
191
+ if (description !== undefined) voce.descrizione = validaDescrizione(description);
192
+ if (priority !== undefined) voce.priorita = validaPriorita(priority);
193
+ voce.aggiornataAlle = clockFn().toISOString();
194
+ await mkdirFn(cartella, { recursive: true });
195
+ await writeFileFn(percorsoDi(cartella, id), JSON.stringify(voce, null, 2), 'utf8');
196
+ return voce;
197
+ }
198
+
199
+ /** Cambia SOLO lo stato — tool a parte sul mobile perché "è la cosa che si fa più spesso, e di gran lunga". @throws {TaskStoreError} TASK_NOT_FOUND se l'id non esiste. */
200
+ export async function completaAttivita({ cartella, id, status = 'done' }, deps = {}) {
201
+ const readFileFn = deps.readFileFn ?? fsp.readFile;
202
+ const mkdirFn = deps.mkdirFn ?? fsp.mkdir;
203
+ const writeFileFn = deps.writeFileFn ?? fsp.writeFile;
204
+ const clockFn = deps.clockFn ?? (() => new Date());
205
+ const voce = await leggiAttivita({ cartella, id }, { readFileFn });
206
+ if (!voce) throw new TaskStoreError(`nessuna attività con id ${id}`, 'TASK_NOT_FOUND');
207
+ voce.stato = validaStato(status);
208
+ voce.aggiornataAlle = clockFn().toISOString();
209
+ await mkdirFn(cartella, { recursive: true });
210
+ await writeFileFn(percorsoDi(cartella, id), JSON.stringify(voce, null, 2), 'utf8');
211
+ return voce;
212
+ }
213
+
214
+ /** Idempotente — un id già assente non è un errore, è l'esito voluto ottenuto da qualcun altro. */
215
+ export async function eliminaAttivita({ cartella, id }, deps = {}) {
216
+ const rmFn = deps.rmFn ?? fsp.rm;
217
+ const percorso = percorsoDi(cartella, id);
218
+ if (!percorso) return; // ⛔ 14/09: un id che non può nominare un file è già «assente» — no-op idempotente
219
+ await rmFn(percorso, { force: true });
220
+ }
@@ -0,0 +1,312 @@
1
+ /**
2
+ * terminal-registry.mjs — W1-01 (05/09). Il registro che decide **CHI può
3
+ * attaccarsi a quale PTY**. Ledger: `.claude/LEDGER-W1-01-SCHEDE-TERMINALE-2026-09-05.md`.
4
+ *
5
+ * ⛔⛔⛔ Perché è un modulo A PARTE da `pty-terminal.mjs`: quello è il ciclo
6
+ * di vita di una PTY (spawn, backlog, reap); questo è **autorizzazione**.
7
+ * Fino al 04/9 le due cose erano la stessa cosa perché l'id della PTY *era*
8
+ * il `sessionId`, quindi non c'era niente da autorizzare. Nel momento in cui
9
+ * una sessione può avere PIÙ schede, il `terminalId` diventa un identificativo
10
+ * scelto dal client — ed è esattamente la forma di difetto che ha prodotto
11
+ * **CVE-2026-59224** (Open WebUI, 2026 —
12
+ * github.com/advisories/GHSA-j657-m4c4-24jq, letto il 05/09/2026): il proxy
13
+ * WebSocket del terminale costruiva l'URL a monte concatenando il `session_id`
14
+ * ricevuto dal client senza validarlo, e con un id vivo ci si **agganciava
15
+ * alla PTY di un'altra persona**. L'identità inoltrata era una pretesa
16
+ * «bearer», senza legame di integrità.
17
+ *
18
+ * ⇒ Il principio NON NEGOZIABILE di questo file: **il `terminalId` non decide
19
+ * MAI la cartella**. La cartella si risolve UNA volta, alla creazione, dalla
20
+ * sessione vera (`cartellaDiSessione`), e da lì resta congelata nella voce.
21
+ * Il client può solo NOMINARE un terminale che il server gli ha già dato.
22
+ * È la cura standard per IDOR/BOLA — OWASP API1:2023 Broken Object Level
23
+ * Authorization, oggi #1: «l'API si fida di un identificativo fornito dal
24
+ * client senza verificare che il chiamante possa accedere a QUELL'oggetto»
25
+ * (ricerca 05/09/2026).
26
+ *
27
+ * ⛔ Il difetto misurato che questo file chiude, `server.mjs:589` prima di oggi:
28
+ *
29
+ * risolviCartella: (id) => sessionRegistry.cartellaDi(id)
30
+ * ?? config.cartelleProgetto[0]?.percorso ?? process.cwd()
31
+ *
32
+ * Un id **sconosciuto** non veniva rifiutato: cadeva sul primo progetto
33
+ * configurato. Con l'id legato al `sessionId` il danno era contenuto; con un
34
+ * `terminalId` libero **qualunque stringa apriva una shell**. Qui quel `??`
35
+ * non esiste: `risolviPerConnessione()` torna `null`, e chi lo chiama rifiuta.
36
+ *
37
+ * ⛔ Il tetto per sessione NON è burocrazia: su Windows ogni PTY porta con sé
38
+ * un processo `conhost` (node-pty#471, ConPTY — letto il 05/09/2026), e
39
+ * `conhost` è noto per sopravvivere alla morte del processo figlio. Schede
40
+ * illimitate = processi di sistema illimitati.
41
+ */
42
+ import { randomUUID } from 'node:crypto';
43
+
44
+ /**
45
+ * Tetto di schede per sessione. Otto: abbastanza per il lavoro vero (build,
46
+ * test, log, un paio di scratch), abbastanza poco da non lasciare in giro
47
+ * decine di `conhost` su Windows. ⛔ Dichiarato, non indovinato a runtime.
48
+ */
49
+ export const SCHEDE_MASSIME_PER_SESSIONE = 8;
50
+
51
+ /*
52
+ * ⛔⛔ Quanto vive una scheda DIMENTICATA — 24 ore dall'ultimo aggancio.
53
+ *
54
+ * Il registro delle PTY ha gia' il suo reaper, ma chiude la SHELL, non la
55
+ * scheda: e' voluto, perche' un F5 dieci minuti dopo deve riaprire nella
56
+ * cartella giusta invece di prendere un 403. Il prezzo era che le schede non
57
+ * si dimenticavano MAI: su un server acceso per settimane e' crescita lenta,
58
+ * e non era misurata da nessuno.
59
+ *
60
+ * ⛔ Il tetto e' molto piu' lungo della grazia delle PTY, di proposito: dopo
61
+ * la scadenza una riconnessione riceve 403, quindi il numero non e' un
62
+ * dettaglio di implementazione ma una PROMESSA — «una scheda che non tocchi
63
+ * per un giorno non c'e' piu'». Lo stato dell'arte dice esattamente questo:
64
+ * la scadenza si applica lato SERVER, con una politica di durata dichiarata,
65
+ * e la pulizia si programma invece di affidarla al caso (una GC probabilistica
66
+ * puo' non passare mai). Ricerca del 05/09/2026:
67
+ * cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html ·
68
+ * modelcontextprotocol.io/seps/2567-sessionless-mcp
69
+ *
70
+ * ⛔ E una scheda tolta in SILENZIO sarebbe il difetto peggiore di quello che
71
+ * cura: `reap()` torna chi ha tolto e perche', e `misura()` dice quante ne
72
+ * esistono adesso — un problema di dimensionamento che nessuno puo' vedere
73
+ * non e' un problema risolto.
74
+ */
75
+ export const ORE_PRIMA_DI_DIMENTICARE_UNA_SCHEDA = 24;
76
+
77
+ /**
78
+ * Registro delle schede terminale.
79
+ *
80
+ * @param {object} deps
81
+ * @param {(sessionId:string)=>string|null} deps.cartellaDiSessione L'UNICA autorità sulla cartella. `null` ⇒ la sessione non esiste ⇒ niente terminale.
82
+ * @param {(terminalId:string)=>void} [deps.chiudiPtyFn] Chiude la PTY vera quando una scheda viene chiusa esplicitamente.
83
+ * @param {(terminalId:string)=>{viva:boolean}|null} [deps.statoPtyFn] Per dire in elenco se la shell è ancora viva, senza esporre la voce interna del registro PTY.
84
+ * @param {string|null} [deps.cartellaStandaloneLegacy] ⛔ DEBITO DICHIARATO, vedi `risolviPerConnessione`. `null` (default) = registro STRETTO: un id ignoto si rifiuta e basta.
85
+ */
86
+ export function creaRegistroSchedeTerminale({
87
+ cartellaDiSessione,
88
+ chiudiPtyFn = () => {},
89
+ statoPtyFn = () => null,
90
+ cartellaStandaloneLegacy = null,
91
+ schedeMassimePerSessione = SCHEDE_MASSIME_PER_SESSIONE,
92
+ orePrimaDiDimenticare = ORE_PRIMA_DI_DIMENTICARE_UNA_SCHEDA,
93
+ generaId = randomUUID,
94
+ clock = () => new Date(),
95
+ } = {}) {
96
+ /** @type {Map<string, {terminalId:string, sessionId:string|null, cartella:string, creatoAlle:string, origine:string}>} */
97
+ const schede = new Map();
98
+
99
+ function schedeDi(sessionId) {
100
+ return [...schede.values()].filter((voce) => voce.sessionId === sessionId);
101
+ }
102
+
103
+ function pubblica(voce) {
104
+ return {
105
+ terminalId: voce.terminalId,
106
+ sessionId: voce.sessionId,
107
+ cartella: voce.cartella,
108
+ creatoAlle: voce.creatoAlle,
109
+ origine: voce.origine,
110
+ /* ⛔ Tre stati, non due: `attiva:false` significa "scheda registrata, shell non ancora aperta o già uscita" — mai confuso con "scheda inesistente" (quella non compare proprio). */
111
+ attiva: statoPtyFn(voce.terminalId)?.viva === true,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Registra la PRIMA scheda di una sessione con `terminalId === sessionId`.
117
+ *
118
+ * ⭐ Compatibilità all'indietro OBBLIGATORIA: `public/harness-ui/app.js` è
119
+ * congelato da un contratto (`frontend/tests/contract/legacy-contract-snapshot.test.mjs`
120
+ * blocca byte-count e sha256) e si collega a `?id=<sessionId>`. Tenendo la
121
+ * prima scheda uguale al `sessionId`, il monolite continua a funzionare
122
+ * **senza una riga di modifica**.
123
+ *
124
+ * ⛔ Non è un fallback travestito: la cartella la dà comunque
125
+ * `cartellaDiSessione()`, cioè il registro delle sessioni. Se la sessione
126
+ * non esiste, qui non nasce niente.
127
+ */
128
+ function registraPrimaScheda(sessionId, cartella) {
129
+ const voce = {
130
+ terminalId: sessionId,
131
+ sessionId,
132
+ cartella,
133
+ creatoAlle: clock().toISOString(),
134
+ ultimoAggancioMs: clock().getTime(),
135
+ origine: 'prima-scheda',
136
+ };
137
+ schede.set(sessionId, voce);
138
+ return voce;
139
+ }
140
+
141
+ return {
142
+ /**
143
+ * Crea una scheda per una sessione VERA. È l'unica porta da cui nasce un
144
+ * `terminalId` nuovo, e l'id lo sceglie il SERVER (`randomUUID`), mai il
145
+ * client — un id imprevedibile è metà della cura raccomandata da OWASP
146
+ * per le WebSocket (il browser non applica la same-origin policy alle WS
147
+ * come la applica a `fetch`: serve il controllo Origin esplicito **più**
148
+ * un valore che non si indovina — WebSocket Security Cheat Sheet, letto
149
+ * il 05/09/2026).
150
+ */
151
+ crea({ sessionId } = {}) {
152
+ if (typeof sessionId !== 'string' || sessionId === '') {
153
+ return { erroreAvvio: 'Sessione non valida', code: 'QUERY_INVALID' };
154
+ }
155
+ const cartella = cartellaDiSessione(sessionId);
156
+ if (typeof cartella !== 'string' || cartella === '') {
157
+ /* ⛔ Qui viveva il `??` di server.mjs:589. Una sessione che non esiste non prende una cartella di ripiego: non prende NIENTE. */
158
+ return { erroreAvvio: 'Sessione non trovata', code: 'NOT_FOUND' };
159
+ }
160
+ const gia = schedeDi(sessionId);
161
+ if (gia.length >= schedeMassimePerSessione) {
162
+ return { erroreAvvio: `Massimo ${schedeMassimePerSessione} terminali per sessione`, code: 'TERMINAL_LIMIT_REACHED' };
163
+ }
164
+ if (gia.length === 0) return pubblica(registraPrimaScheda(sessionId, cartella));
165
+ let terminalId = generaId();
166
+ /* ⛔ AL CONTRARIO — un `generaId` che collide (o un test che ne inietta uno fisso) non deve MAI sovrascrivere una scheda viva di qualcun altro. */
167
+ while (schede.has(terminalId)) terminalId = `${terminalId}-${schede.size}`;
168
+ const voce = {
169
+ terminalId,
170
+ sessionId,
171
+ cartella,
172
+ creatoAlle: clock().toISOString(),
173
+ ultimoAggancioMs: clock().getTime(),
174
+ origine: 'rotta',
175
+ };
176
+ schede.set(terminalId, voce);
177
+ return pubblica(voce);
178
+ },
179
+
180
+ /** Elenco delle schede di UNA sessione — mai di tutte: una sessione non vede i terminali delle altre. */
181
+ elenca(sessionId) {
182
+ if (typeof sessionId !== 'string' || sessionId === '') return { erroreAvvio: 'Sessione non valida', code: 'QUERY_INVALID' };
183
+ if (cartellaDiSessione(sessionId) === null) return { erroreAvvio: 'Sessione non trovata', code: 'NOT_FOUND' };
184
+ return { items: schedeDi(sessionId).map(pubblica) };
185
+ },
186
+
187
+ /**
188
+ * Chiusura ESPLICITA di una scheda: chiude anche la PTY vera.
189
+ *
190
+ * ⛔⛔ La proprietà si verifica, non si presume: chiudere un terminale che
191
+ * appartiene a un'ALTRA sessione risponde `NOT_FOUND` — lo stesso codice di
192
+ * un terminale inesistente, di proposito. Distinguere «non esiste» da «non
193
+ * è tuo» regalerebbe a chi prova id a caso una sonda per scoprire quali
194
+ * esistono (OWASP API1:2023, ricerca 05/09/2026).
195
+ */
196
+ chiudi({ sessionId, terminalId } = {}) {
197
+ if (typeof sessionId !== 'string' || sessionId === '' || typeof terminalId !== 'string' || terminalId === '') {
198
+ return { erroreAvvio: 'Terminale non valido', code: 'QUERY_INVALID' };
199
+ }
200
+ const voce = schede.get(terminalId);
201
+ if (!voce || voce.sessionId !== sessionId) {
202
+ return { erroreAvvio: 'Terminale non trovato', code: 'NOT_FOUND' };
203
+ }
204
+ chiudiPtyFn(terminalId);
205
+ schede.delete(terminalId);
206
+ return { ok: true, terminalId, sessionId };
207
+ },
208
+
209
+ /**
210
+ * L'UNICA domanda che `terminal-ws.mjs` ha il diritto di fare: «questo id
211
+ * può attaccarsi, e a quale cartella?». Tre esiti, in quest'ordine:
212
+ *
213
+ * 1. **scheda già registrata** → la sua voce, con la cartella congelata
214
+ * alla creazione (⛔ mai ri-risolta a ogni connessione: è così che si
215
+ * evita che un cambio di stato altrove sposti una shell già aperta);
216
+ * 2. **id che è un `sessionId` VIVO** → prima scheda di quella sessione,
217
+ * registrata al volo. È la porta di compatibilità del monolite
218
+ * congelato, e l'autorità resta il registro delle sessioni;
219
+ * 3. **tutto il resto** → `null`, cioè **rifiuto**. Nessuna PTY nasce.
220
+ *
221
+ * ⛔ DEBITO DICHIARATO — `cartellaStandaloneLegacy`. Il monolite congelato
222
+ * ha una TERZA strada, misurata il 05/09 leggendo `public/app.js` (non
223
+ * presunta): `idTerminaleCorrente()` fa
224
+ * `if (state.realSession.id) return state.realSession.id;` e **altrimenti
225
+ * inventa un `crypto.randomUUID()` lato client** — un terminale
226
+ * "standalone" senza nessuna sessione, raggiungibile aprendo il tab
227
+ * Terminale prima di avviare qualsiasi cosa. Quel caso è esattamente ciò
228
+ * che il `??` di server.mjs teneva in piedi. Qui il default è `null`, cioè
229
+ * **rifiuto**; chi vuole tenere viva quella funzione del monolite deve
230
+ * passare una cartella ESPLICITA a costruzione — una riga sola, visibile,
231
+ * con un tetto suo — invece di una catena di `??` valutata a ogni
232
+ * connessione. ⛔ Si spegne rimettendo `null` in `server.mjs` il giorno in
233
+ * cui il frontend nuovo sostituisce il monolite.
234
+ */
235
+ risolviPerConnessione(id) {
236
+ if (typeof id !== 'string' || id === '') return null;
237
+ const registrata = schede.get(id);
238
+ if (registrata) {
239
+ // ⛔ «Ultimo aggancio», non «ultima creazione»: e' l'uso che tiene viva
240
+ // una scheda, ed e' l'unico momento in cui qualcuno la nomina davvero.
241
+ registrata.ultimoAggancioMs = clock().getTime();
242
+ return registrata;
243
+ }
244
+ const cartellaSessione = cartellaDiSessione(id);
245
+ if (typeof cartellaSessione === 'string' && cartellaSessione !== '') {
246
+ return registraPrimaScheda(id, cartellaSessione);
247
+ }
248
+ if (typeof cartellaStandaloneLegacy === 'string' && cartellaStandaloneLegacy !== '') {
249
+ const standalone = [...schede.values()].filter((voce) => voce.sessionId === null);
250
+ /* ⛔ Tetto anche qui: senza, «qualunque stringa apre una shell» resterebbe vero per la porta legacy. */
251
+ if (standalone.length >= schedeMassimePerSessione) return null;
252
+ const voce = {
253
+ terminalId: id,
254
+ sessionId: null,
255
+ cartella: cartellaStandaloneLegacy,
256
+ creatoAlle: clock().toISOString(),
257
+ ultimoAggancioMs: clock().getTime(),
258
+ origine: 'standalone-legacy',
259
+ };
260
+ schede.set(id, voce);
261
+ return voce;
262
+ }
263
+ return null;
264
+ },
265
+
266
+ /**
267
+ * ⭐⭐⭐ Dimentica le schede che nessuno tocca da `orePrimaDiDimenticare`.
268
+ *
269
+ * ⛔ Una scheda si toglie SOLO se la sua shell e' gia' morta: finche' una
270
+ * PTY e' viva la scheda serve, per lunga che sia l'attesa — buttarla
271
+ * lascerebbe un processo vivo senza piu' nessuno che possa raggiungerlo,
272
+ * che e' esattamente la perdita che questa funzione dovrebbe curare.
273
+ *
274
+ * ⛔ Torna CHI ha tolto e PERCHE': una scheda sparita in silenzio sarebbe
275
+ * peggio del problema, perche' la prossima riconnessione riceve 403 e
276
+ * nessuno saprebbe dire da dove viene.
277
+ */
278
+ dimenticaLeVecchie({ adesso = clock().getTime() } = {}) {
279
+ const limite = orePrimaDiDimenticare * 60 * 60 * 1000;
280
+ const tolte = [];
281
+ for (const [terminalId, voce] of [...schede]) {
282
+ const ferma = adesso - (voce.ultimoAggancioMs ?? 0);
283
+ if (ferma < limite) continue;
284
+ const pty = statoPtyFn(terminalId);
285
+ if (pty && pty.viva) continue;
286
+ schede.delete(terminalId);
287
+ tolte.push({ terminalId, sessionId: voce.sessionId, origine: voce.origine, fermaDaMs: ferma });
288
+ }
289
+ return { tolte, restano: schede.size };
290
+ },
291
+
292
+ /**
293
+ * Quante schede esistono adesso, e da quanto tace la piu' vecchia.
294
+ * ⛔ Serve a vedere un problema di dimensionamento PRIMA che diventi una
295
+ * perdita: un numero che nessuno puo' leggere non e' una garanzia.
296
+ */
297
+ misura({ adesso = clock().getTime() } = {}) {
298
+ let piuVecchiaMs = null;
299
+ let conShellViva = 0;
300
+ for (const [terminalId, voce] of schede) {
301
+ const ferma = adesso - (voce.ultimoAggancioMs ?? 0);
302
+ if (piuVecchiaMs === null || ferma > piuVecchiaMs) piuVecchiaMs = ferma;
303
+ const pty = statoPtyFn(terminalId);
304
+ if (pty && pty.viva) conShellViva += 1;
305
+ }
306
+ return { schede: schede.size, conShellViva, piuVecchiaFermaDaMs: piuVecchiaMs, orePrimaDiDimenticare };
307
+ },
308
+
309
+ /** ⛔ Solo per i test e per lo shutdown — mai per decidere un permesso. */
310
+ _schede: schede,
311
+ };
312
+ }