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.
- package/CHANGELOG.md +87 -0
- package/LICENSE +661 -0
- package/README.md +116 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +100 -0
- package/dist/automations/history-store.js +34 -0
- package/dist/automations/policy-store.js +43 -0
- package/dist/automations/runner.js +107 -0
- package/dist/automations/schedule.js +85 -0
- package/dist/automations/windows-task.js +53 -0
- package/dist/commands/advanced-cli.js +233 -0
- package/dist/commands/checkpoint-cli.js +45 -0
- package/dist/commands/config-cli.js +107 -0
- package/dist/commands/context.js +17 -0
- package/dist/commands/extensions-cli.js +207 -0
- package/dist/commands/project-cli.js +74 -0
- package/dist/commands/project-commands.js +143 -0
- package/dist/commands/provider-cli.js +244 -0
- package/dist/commands/services-cli.js +248 -0
- package/dist/commands/session-cli.js +171 -0
- package/dist/commands/system-cli.js +405 -0
- package/dist/config/commands.js +60 -0
- package/dist/config/load.js +382 -0
- package/dist/config/migrations.js +48 -0
- package/dist/config/types.js +11 -0
- package/dist/diagnostics/development-log.js +154 -0
- package/dist/diagnostics/doctor.js +104 -0
- package/dist/diagnostics/redact.js +80 -0
- package/dist/diagnostics/zip.js +52 -0
- package/dist/errors.js +156 -0
- package/dist/events/bridge.js +130 -0
- package/dist/extensions/installer.js +178 -0
- package/dist/extensions/package-schema.js +24 -0
- package/dist/headless/result.js +84 -0
- package/dist/headless/run.js +230 -0
- package/dist/i18n/en/approval.js +46 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +92 -0
- package/dist/i18n/en/errors.js +267 -0
- package/dist/i18n/en/firstrun.js +101 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +774 -0
- package/dist/i18n/en/tools.js +127 -0
- package/dist/i18n/en.js +14 -0
- package/dist/i18n/error-view.js +307 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/kernel-map.js +139 -0
- package/dist/io.js +45 -0
- package/dist/main.js +331 -0
- package/dist/paths.js +41 -0
- package/dist/protocol/v2/codec.js +9 -0
- package/dist/protocol/v2/events.js +555 -0
- package/dist/protocol/v2/index.js +4 -0
- package/dist/protocol/v2/replay.js +28 -0
- package/dist/protocol/v2/types.js +1 -0
- package/dist/provider/control-plane.js +135 -0
- package/dist/provider/environment-keys.js +275 -0
- package/dist/provider/health.js +110 -0
- package/dist/provider/missing-key.js +48 -0
- package/dist/provider/model-catalog.js +168 -0
- package/dist/provider/openrouter-login.js +172 -0
- package/dist/provider/provider-text.js +13 -0
- package/dist/provider/store.js +95 -0
- package/dist/provider/system-keyring.js +214 -0
- package/dist/runtime/active-run.js +63 -0
- package/dist/runtime/agent-tree.js +74 -0
- package/dist/runtime/attachments.js +84 -0
- package/dist/runtime/brokered-executor.js +395 -0
- package/dist/runtime/context-archive.js +69 -0
- package/dist/runtime/context-status.js +76 -0
- package/dist/runtime/create-runtime.js +111 -0
- package/dist/runtime/model-profile.js +361 -0
- package/dist/runtime/output-store.js +137 -0
- package/dist/runtime/provider-attempts.js +185 -0
- package/dist/runtime/replay-buffer.js +153 -0
- package/dist/runtime/repo.js +95 -0
- package/dist/runtime/session-facade.js +135 -0
- package/dist/runtime/session-summary.js +125 -0
- package/dist/runtime/supervisor.js +265 -0
- package/dist/runtime/talos-composition.js +1011 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/usage-snapshot.js +82 -0
- package/dist/security/auto-classifier.js +38 -0
- package/dist/security/credential-free-environment.js +54 -0
- package/dist/security/evaluate.js +243 -0
- package/dist/security/execution-backends.js +236 -0
- package/dist/security/execution-broker.js +102 -0
- package/dist/security/forge-scan.js +74 -0
- package/dist/security/from-approval.js +39 -0
- package/dist/security/mxc-execution-backend.js +281 -0
- package/dist/security/permission-engine.js +138 -0
- package/dist/security/permission-explanation.js +54 -0
- package/dist/security/persist.js +179 -0
- package/dist/security/plugin-guard.js +230 -0
- package/dist/security/process-tree-evidence-store.js +74 -0
- package/dist/security/process-tree-probe.js +554 -0
- package/dist/security/project-resource-inventory.js +186 -0
- package/dist/security/project-trust-gate.js +38 -0
- package/dist/security/project-trust.js +367 -0
- package/dist/security/rule-parser.js +201 -0
- package/dist/security/shell-segmentation.js +68 -0
- package/dist/security/trust-authority.js +324 -0
- package/dist/security/types.js +1 -0
- package/dist/security/workspace-identity.js +102 -0
- package/dist/services/automation-facade.js +59 -0
- package/dist/services/forge-facade.js +77 -0
- package/dist/services/hook-facade.js +240 -0
- package/dist/services/index.js +16 -0
- package/dist/services/library-facade.js +174 -0
- package/dist/services/mcp-facade.js +348 -0
- package/dist/services/memory-facade.js +126 -0
- package/dist/services/notes-facade.js +157 -0
- package/dist/services/plugin-facade.js +308 -0
- package/dist/services/research-facade.js +110 -0
- package/dist/services/task-board-facade.js +236 -0
- package/dist/services/workflow-catalog.js +246 -0
- package/dist/sessions/export-format.js +98 -0
- package/dist/sessions/metadata-store.js +160 -0
- package/dist/sessions/share.js +122 -0
- package/dist/sessions/transfer.js +16 -0
- package/dist/subcommands.js +62 -0
- package/dist/tui/agent-roster.js +36 -0
- package/dist/tui/agent-view.js +51 -0
- package/dist/tui/app.js +4098 -0
- package/dist/tui/approval.js +36 -0
- package/dist/tui/boot/boot-sequence.js +76 -0
- package/dist/tui/boot/cinematic.js +243 -0
- package/dist/tui/boot/desktop-logo.js +82 -0
- package/dist/tui/boot.js +3 -0
- package/dist/tui/busy-input.js +58 -0
- package/dist/tui/catalog-service.js +560 -0
- package/dist/tui/components/assistant-stream.js +1 -0
- package/dist/tui/components/command-menu.js +68 -0
- package/dist/tui/components/composer.js +219 -0
- package/dist/tui/components/diff.js +35 -0
- package/dist/tui/components/footer.js +48 -0
- package/dist/tui/components/header.js +6 -0
- package/dist/tui/components/markdown.js +305 -0
- package/dist/tui/components/status-indicator.js +38 -0
- package/dist/tui/components/terminal-shell.js +214 -0
- package/dist/tui/components/thinking-row.js +19 -0
- package/dist/tui/components/tool-row.js +97 -0
- package/dist/tui/components/transcript-virtualizer.js +165 -0
- package/dist/tui/components/transcript.js +43 -0
- package/dist/tui/descendant-approvals.js +104 -0
- package/dist/tui/diff-model.js +77 -0
- package/dist/tui/editor-history.js +21 -0
- package/dist/tui/editor.js +210 -0
- package/dist/tui/event-adapter.js +436 -0
- package/dist/tui/exit-output.js +58 -0
- package/dist/tui/external-editor.js +53 -0
- package/dist/tui/file-completion.js +89 -0
- package/dist/tui/focus-manager.js +5 -0
- package/dist/tui/highlight.js +30 -0
- package/dist/tui/input-router.js +18 -0
- package/dist/tui/interrupt.js +99 -0
- package/dist/tui/keybindings.js +194 -0
- package/dist/tui/keymap-resolver.js +91 -0
- package/dist/tui/launch-state.js +148 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +56 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +43 -0
- package/dist/tui/overlays/approval-dialog.js +642 -0
- package/dist/tui/overlays/automation-center.js +51 -0
- package/dist/tui/overlays/checkpoint-picker.js +47 -0
- package/dist/tui/overlays/context-inspector.js +36 -0
- package/dist/tui/overlays/effort-line.js +110 -0
- package/dist/tui/overlays/forge-center.js +46 -0
- package/dist/tui/overlays/help-dialog.js +16 -0
- package/dist/tui/overlays/history-picker.js +37 -0
- package/dist/tui/overlays/hook-center.js +70 -0
- package/dist/tui/overlays/library-center.js +55 -0
- package/dist/tui/overlays/mcp-center.js +60 -0
- package/dist/tui/overlays/memory-center.js +63 -0
- package/dist/tui/overlays/model-picker.js +77 -0
- package/dist/tui/overlays/notes-center.js +53 -0
- package/dist/tui/overlays/overlay-host.js +8 -0
- package/dist/tui/overlays/plugin-center.js +76 -0
- package/dist/tui/overlays/provider-picker.js +171 -0
- package/dist/tui/overlays/queue-editor.js +32 -0
- package/dist/tui/overlays/research-center.js +59 -0
- package/dist/tui/overlays/scroll-window.js +234 -0
- package/dist/tui/overlays/session-picker.js +115 -0
- package/dist/tui/overlays/tasks-center.js +83 -0
- package/dist/tui/overlays/theme-picker.js +21 -0
- package/dist/tui/overlays/transcript-search.js +58 -0
- package/dist/tui/overlays/trust-center.js +29 -0
- package/dist/tui/overlays/workflow-center.js +46 -0
- package/dist/tui/project-file-index.js +214 -0
- package/dist/tui/project-references.js +85 -0
- package/dist/tui/project-trust-prompt.js +289 -0
- package/dist/tui/prompt-history-store.js +116 -0
- package/dist/tui/queue-store.js +110 -0
- package/dist/tui/regions/budget.js +59 -0
- package/dist/tui/regions/views.js +96 -0
- package/dist/tui/render-coordinator.js +148 -0
- package/dist/tui/render-scheduler.js +4 -0
- package/dist/tui/run.js +69 -0
- package/dist/tui/selection-list.js +29 -0
- package/dist/tui/session-controller.js +791 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/setup-wizard.js +46 -0
- package/dist/tui/shell-input.js +35 -0
- package/dist/tui/shell-layout.js +48 -0
- package/dist/tui/shell-model.js +98 -0
- package/dist/tui/slash-commands.js +82 -0
- package/dist/tui/state.js +151 -0
- package/dist/tui/status-bar.js +125 -0
- package/dist/tui/status-view.js +40 -0
- package/dist/tui/terminal-capabilities.js +9 -0
- package/dist/tui/terminal-session.js +11 -0
- package/dist/tui/text-width.js +66 -0
- package/dist/tui/theme-catalog.js +27 -0
- package/dist/tui/theme-store.js +23 -0
- package/dist/tui/theme.js +23 -0
- package/dist/tui/tool-display.js +135 -0
- package/dist/tui/tool-facts.js +1 -0
- package/dist/tui/tools/bash-renderer.js +32 -0
- package/dist/tui/tools/change.js +61 -0
- package/dist/tui/tools/edit-renderer.js +17 -0
- package/dist/tui/tools/generic-renderer.js +13 -0
- package/dist/tui/tools/list-renderer.js +14 -0
- package/dist/tui/tools/read-renderer.js +13 -0
- package/dist/tui/tools/registry.js +20 -0
- package/dist/tui/tools/row-format.js +187 -0
- package/dist/tui/tools/search-renderer.js +38 -0
- package/dist/tui/tools/shared.js +97 -0
- package/dist/tui/tools/write-renderer.js +15 -0
- package/dist/tui/transcript-model.js +441 -0
- package/dist/tui/ui-preferences.js +79 -0
- package/dist/tui/usage-view.js +77 -0
- package/dist/tui/vim-mode.js +99 -0
- package/dist/update/check.js +15 -0
- package/dist/update/npm.js +51 -0
- package/dist/update/run.js +129 -0
- package/dist/version.js +2 -0
- package/dist/workspace/checkpoint-store.js +210 -0
- package/dist/workspace/checkpoint.js +413 -0
- package/dist/workspace/restore.js +232 -0
- package/package.json +64 -5
- package/vendor/context-engine/package.json +11 -0
- package/vendor/context-engine/src/compaction-planner.mjs +57 -0
- package/vendor/context-engine/src/contracts.mjs +48 -0
- package/vendor/context-engine/src/engine.mjs +445 -0
- package/vendor/context-engine/src/node/context-export.mjs +164 -0
- package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
- package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
- package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
- package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
- package/vendor/context-engine/src/profiles.mjs +10 -0
- package/vendor/context-engine/src/retrieval.mjs +104 -0
- package/vendor/context-engine/src/summary.mjs +97 -0
- package/vendor/context-engine/src/usage.mjs +34 -0
- package/vendor/harness-ui/package.json +38 -0
- package/vendor/harness-ui/src/acp-agent.mjs +350 -0
- package/vendor/harness-ui/src/agent-service.mjs +2188 -0
- package/vendor/harness-ui/src/agui-events.mjs +452 -0
- package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
- package/vendor/harness-ui/src/artifact-store.mjs +39 -0
- package/vendor/harness-ui/src/assistenza.mjs +123 -0
- package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
- package/vendor/harness-ui/src/automation-store.mjs +145 -0
- package/vendor/harness-ui/src/browser-annota.mjs +639 -0
- package/vendor/harness-ui/src/browser-frame.mjs +211 -0
- package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
- package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
- package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
- package/vendor/harness-ui/src/browser-stream.mjs +445 -0
- package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
- package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
- package/vendor/harness-ui/src/config.mjs +697 -0
- package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
- package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
- package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
- package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
- package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
- package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
- package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
- package/vendor/harness-ui/src/context-runtime.mjs +118 -0
- package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
- package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
- package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
- package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
- package/vendor/harness-ui/src/custom-task.mjs +171 -0
- package/vendor/harness-ui/src/doctor.mjs +142 -0
- package/vendor/harness-ui/src/document-filename.mjs +97 -0
- package/vendor/harness-ui/src/document-generator.mjs +493 -0
- package/vendor/harness-ui/src/document-report.mjs +331 -0
- package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
- package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
- package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
- package/vendor/harness-ui/src/forge-contract.mjs +221 -0
- package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
- package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
- package/vendor/harness-ui/src/generation-idle.mjs +332 -0
- package/vendor/harness-ui/src/gguf-header.mjs +207 -0
- package/vendor/harness-ui/src/git-service.mjs +626 -0
- package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
- package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
- package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
- package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
- package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
- package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
- package/vendor/harness-ui/src/hook-registry.mjs +186 -0
- package/vendor/harness-ui/src/http-app.mjs +6259 -0
- package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
- package/vendor/harness-ui/src/id-archivio.mjs +27 -0
- package/vendor/harness-ui/src/image-generator.mjs +143 -0
- package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
- package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
- package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
- package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
- package/vendor/harness-ui/src/library-store.mjs +652 -0
- package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
- package/vendor/harness-ui/src/local-model-store.mjs +339 -0
- package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
- package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
- package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
- package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
- package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
- package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
- package/vendor/harness-ui/src/mcp-client.mjs +98 -0
- package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
- package/vendor/harness-ui/src/mcp-session.mjs +177 -0
- package/vendor/harness-ui/src/memory-store.mjs +227 -0
- package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
- package/vendor/harness-ui/src/model-catalog.mjs +129 -0
- package/vendor/harness-ui/src/model-destination.mjs +189 -0
- package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
- package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
- package/vendor/harness-ui/src/notes-store.mjs +250 -0
- package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
- package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
- package/vendor/harness-ui/src/path-policy.mjs +442 -0
- package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
- package/vendor/harness-ui/src/plugin-session.mjs +180 -0
- package/vendor/harness-ui/src/process-policy.mjs +345 -0
- package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
- package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
- package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
- package/vendor/harness-ui/src/provider-probe.mjs +582 -0
- package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
- package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
- package/vendor/harness-ui/src/public-problem.mjs +109 -0
- package/vendor/harness-ui/src/research/card.mjs +235 -0
- package/vendor/harness-ui/src/research/citations.mjs +142 -0
- package/vendor/harness-ui/src/research/collector.mjs +275 -0
- package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
- package/vendor/harness-ui/src/research/dossier.mjs +114 -0
- package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
- package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
- package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
- package/vendor/harness-ui/src/research/independence.mjs +159 -0
- package/vendor/harness-ui/src/research/ledger.mjs +166 -0
- package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
- package/vendor/harness-ui/src/research/narration.mjs +181 -0
- package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
- package/vendor/harness-ui/src/research/opposing.mjs +305 -0
- package/vendor/harness-ui/src/research/outline.mjs +111 -0
- package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
- package/vendor/harness-ui/src/research/pdf.mjs +291 -0
- package/vendor/harness-ui/src/research/plan.mjs +301 -0
- package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
- package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
- package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
- package/vendor/harness-ui/src/research/recheck.mjs +194 -0
- package/vendor/harness-ui/src/research/report.mjs +203 -0
- package/vendor/harness-ui/src/research/run.mjs +527 -0
- package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
- package/vendor/harness-ui/src/research/verification.mjs +572 -0
- package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
- package/vendor/harness-ui/src/research-store.mjs +1133 -0
- package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
- package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
- package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
- package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
- package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
- package/vendor/harness-ui/src/search-source-store.mjs +172 -0
- package/vendor/harness-ui/src/session-registry.mjs +6095 -0
- package/vendor/harness-ui/src/session-store.mjs +220 -0
- package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
- package/vendor/harness-ui/src/setup-stato.mjs +31 -0
- package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
- package/vendor/harness-ui/src/skill-registry.mjs +120 -0
- package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
- package/vendor/harness-ui/src/static-files.mjs +96 -0
- package/vendor/harness-ui/src/stream-partition.mjs +123 -0
- package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
- package/vendor/harness-ui/src/task-catalog.mjs +65 -0
- package/vendor/harness-ui/src/tasks-store.mjs +220 -0
- package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
- package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
- package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
- package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
- package/vendor/harness-ui/src/usage-cache.mjs +315 -0
- package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
- package/vendor/harness-ui/src/workspace-context.mjs +124 -0
- package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
- package/vendor/harness-ui/src/workspace-files.mjs +589 -0
- package/vendor/harness-ui/src/workspace-info.mjs +189 -0
- package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
- package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
- package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
- package/vendor/manifest.json +170 -0
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
import {
|
|
2
|
+
accessSync,
|
|
3
|
+
constants,
|
|
4
|
+
readFileSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
statSync,
|
|
7
|
+
} from 'node:fs';
|
|
8
|
+
import { createPrivateKey } from 'node:crypto';
|
|
9
|
+
import { spawnSync } from 'node:child_process';
|
|
10
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
|
|
13
|
+
import { ID_DESTINAZIONE_CHAT } from './provider-registry.mjs';
|
|
14
|
+
|
|
15
|
+
// P-L · JSON del runtime non segreto dell'agente esterno, letto solo alla richiesta.
|
|
16
|
+
export const ENV_AGENTE_ESTERNO = 'TALOS_AGENTE_ESTERNO';
|
|
17
|
+
// P-L · fine variabili.
|
|
18
|
+
|
|
19
|
+
export const DEFAULT_HOST = '127.0.0.1';
|
|
20
|
+
export const DEFAULT_PORT = 4174;
|
|
21
|
+
|
|
22
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', '::1', 'localhost']);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* ⛔⛔ ALLOWLIST: TALOS_HARNESS_UI_MODEL può solo SCEGLIERE fra questi, mai introdurne uno
|
|
26
|
+
* nuovo — la regola dell'owner (20/8, "mai modelli di punta, sempre flash")
|
|
27
|
+
* si applica qui a livello di configurazione, non come convenzione da
|
|
28
|
+
* ricordare a ogni chiamata. Entrambi già usati in questo stesso ecosistema:
|
|
29
|
+
* `z-ai/glm-4.7-flash` (candidato Stadio B, dossier 24/8), `qwen/qwen3.7-flash`
|
|
30
|
+
* (`provaTalos.mjs`) — non nomi nuovi, valori già misurati.
|
|
31
|
+
*/
|
|
32
|
+
export const MODELLI_AMMESSI = Object.freeze(['z-ai/glm-4.7-flash', 'qwen/qwen3.7-flash']);
|
|
33
|
+
|
|
34
|
+
export class ConfigurationError extends Error {
|
|
35
|
+
constructor(message) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'ConfigurationError';
|
|
38
|
+
this.code = 'CONFIG_INVALID';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function fail(message) {
|
|
43
|
+
throw new ConfigurationError(message);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/*
|
|
47
|
+
* ⭐⭐⭐ 03/9 — R-01, lanciatore doppio-clic (owner: "parta correttamente,
|
|
48
|
+
* porta libera scelta da sola"). Non è un errore di CONFIGURAZIONE (la
|
|
49
|
+
* porta chiesta era valida): è un fatto di RUNTIME, qualcos'altro la sta
|
|
50
|
+
* usando in questo momento. Classe propria, non ConfigurationError, così
|
|
51
|
+
* chi la intercetta non la confonde con TALOS_HARNESS_UI_PORT malformata.
|
|
52
|
+
*/
|
|
53
|
+
export class PortaInUsoError extends Error {
|
|
54
|
+
constructor(porta, { esplicita = false, tentativi } = {}) {
|
|
55
|
+
super(esplicita
|
|
56
|
+
? `La porta ${porta} è già in uso (TALOS_HARNESS_UI_PORT è impostata esplicitamente: non si sceglie una porta diversa da sola).`
|
|
57
|
+
: `Nessuna porta libera trovata da quella richiesta fino a ${porta} (${tentativi} tentativi).`);
|
|
58
|
+
this.name = 'PortaInUsoError';
|
|
59
|
+
this.code = 'PORT_IN_USE';
|
|
60
|
+
this.porta = porta;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/*
|
|
65
|
+
* Sceglie una porta libera partendo da `portaIniziale`. `tentaLegameFn(porta)`
|
|
66
|
+
* è iniettata: deve provare a legare quella porta e restituire `{ ok: true }`
|
|
67
|
+
* se ci riesce, `{ ok: false, errore }` se la porta è occupata (o per
|
|
68
|
+
* qualunque altro motivo il legame fallisce) — mai side-effect nascosti,
|
|
69
|
+
* mai un vero socket qui dentro: è `server.mjs` a fornire l'implementazione
|
|
70
|
+
* reale, i test ne iniettano una finta senza aprire porte davvero.
|
|
71
|
+
*
|
|
72
|
+
* `esplicita:true` (l'owner ha impostato TALOS_HARNESS_UI_PORT): un solo
|
|
73
|
+
* tentativo, mai spostata da sola — chi la imposta la vuole quella.
|
|
74
|
+
* `esplicita:false` (default): prova le porte successive, una alla volta,
|
|
75
|
+
* fino a `tentativiMassimi`.
|
|
76
|
+
*/
|
|
77
|
+
export async function trovaPortaLibera(portaIniziale, { esplicita = false, tentativiMassimi = 20, tentaLegameFn } = {}) {
|
|
78
|
+
if (typeof tentaLegameFn !== 'function') fail('trovaPortaLibera richiede tentaLegameFn');
|
|
79
|
+
let porta = portaIniziale;
|
|
80
|
+
for (let tentativo = 1; ; tentativo += 1) {
|
|
81
|
+
const esito = await tentaLegameFn(porta);
|
|
82
|
+
if (esito.ok) return porta;
|
|
83
|
+
if (esplicita) throw new PortaInUsoError(porta, { esplicita: true });
|
|
84
|
+
if (tentativo >= tentativiMassimi) throw new PortaInUsoError(porta, { esplicita: false, tentativi: tentativiMassimi });
|
|
85
|
+
porta += 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* ⭐⭐⭐ 04/9 — W0-04, scheletro `labs/`. I flag ammessi sono SOLO quelli
|
|
91
|
+
* dichiarati in `labs/feature-flags.json` (letto qui, accanto al server):
|
|
92
|
+
* `TALOS_LABS=a,b` accende quelli, un nome sconosciuto è CONFIG_INVALID con
|
|
93
|
+
* l'elenco degli ammessi — mai un flag inventato al volo. Il file assente o
|
|
94
|
+
* illeggibile = nessun flag dichiarato, quindi qualunque TALOS_LABS fallisce
|
|
95
|
+
* (onesto: non si può accendere ciò che non è dichiarato).
|
|
96
|
+
*/
|
|
97
|
+
function leggiFlagLabs(moduleUrl) {
|
|
98
|
+
try {
|
|
99
|
+
const dati = JSON.parse(readFileSync(fileURLToPath(new URL('./labs/feature-flags.json', moduleUrl)), 'utf8'));
|
|
100
|
+
return dati && typeof dati === 'object' && !Array.isArray(dati) ? Object.keys(dati) : [];
|
|
101
|
+
} catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function parseLabs(raw, moduleUrl) {
|
|
106
|
+
if (raw === undefined || raw === null || String(raw).trim() === '') return Object.freeze([]);
|
|
107
|
+
const ammessi = leggiFlagLabs(moduleUrl);
|
|
108
|
+
const richiesti = [...new Set(String(raw).split(',').map((s) => s.trim()).filter(Boolean))];
|
|
109
|
+
const ignoti = richiesti.filter((nome) => !ammessi.includes(nome));
|
|
110
|
+
if (ignoti.length > 0) {
|
|
111
|
+
fail(`TALOS_LABS: flag non dichiarato in labs/feature-flags.json: ${ignoti.join(', ')} — ammessi: ${ammessi.length ? ammessi.join(', ') : '(nessuno: file assente)'}`);
|
|
112
|
+
}
|
|
113
|
+
return Object.freeze(richiesti);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/*
|
|
117
|
+
* ⭐⭐⭐ 04/9 — W1-10, spike Electron: il token di loopback. Quando è
|
|
118
|
+
* impostato, ogni `/api/*` e l'upgrade WebSocket del terminale vogliono il
|
|
119
|
+
* cookie `talos_token` (impostato da `GET /?token=…`): così solo la finestra
|
|
120
|
+
* della shell — che conosce il token generato all'avvio — usa il server,
|
|
121
|
+
* anche se un altro processo sulla stessa macchina raggiunge la porta.
|
|
122
|
+
* Senza variabile: comportamento di sempre (browser-first, nessun cookie).
|
|
123
|
+
* ⛔ Minimo 32 caratteri: un token corto è una serratura di cartone.
|
|
124
|
+
*/
|
|
125
|
+
export const LUNGHEZZA_MINIMA_TOKEN = 32;
|
|
126
|
+
function parseToken(raw) {
|
|
127
|
+
if (raw === undefined || raw === null || raw === '') return undefined;
|
|
128
|
+
if (typeof raw !== 'string' || raw.trim().length < LUNGHEZZA_MINIMA_TOKEN || /\s/.test(raw.trim())) {
|
|
129
|
+
fail(`TALOS_HARNESS_UI_TOKEN deve avere almeno ${LUNGHEZZA_MINIMA_TOKEN} caratteri, senza spazi`);
|
|
130
|
+
}
|
|
131
|
+
return raw.trim();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function parseModello(raw) {
|
|
135
|
+
if (raw === undefined || raw === '') return MODELLI_AMMESSI[0];
|
|
136
|
+
if (typeof raw !== 'string' || !MODELLI_AMMESSI.includes(raw)) {
|
|
137
|
+
fail(`TALOS_HARNESS_UI_MODEL deve essere uno fra: ${MODELLI_AMMESSI.join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
return raw;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* ⭐⭐⭐ 27/8, owner: "rendi il composer funzionante al 100%... poter
|
|
144
|
+
* scegliere almeno tutti i modelli openrouter e deepseek, per testare, poi
|
|
145
|
+
* estendiamo a tutti i provider supportati, nessuna eccezione".
|
|
146
|
+
*
|
|
147
|
+
* ⛔ Diversa da `MODELLI_AMMESSI`/`parseModello` sopra apposta: quelli
|
|
148
|
+
* restano il DEFAULT del server (whitelist stretta, la regola "mai modelli
|
|
149
|
+
* di punta" applicata al valore di partenza quando nessuno ha scelto
|
|
150
|
+
* niente). Questa valida una scelta ESPLICITA dell'owner per una singola
|
|
151
|
+
* sessione — "nessuna eccezione" vuol dire qualunque ID modello
|
|
152
|
+
* OpenRouter valido, non un elenco scritto a mano che invecchia. TALOS
|
|
153
|
+
* chiama SEMPRE `https://openrouter.ai/api/v1/chat/completions`
|
|
154
|
+
* (`talosHarness.mjs`), che instrada già DeepSeek (`deepseek/...`) e
|
|
155
|
+
* decine di altri vendor allo stesso endpoint — non serve un secondo
|
|
156
|
+
* adattatore per "OpenRouter e DeepSeek insieme", solo smettere di
|
|
157
|
+
* limitare quale ID si può chiedere.
|
|
158
|
+
*
|
|
159
|
+
* Il pattern è quello reale degli ID OpenRouter: `vendor/nome`, con
|
|
160
|
+
* varianti tipo `:free`/`:beta` ammesse dopo il nome. Non un whitelist di
|
|
161
|
+
* vendor — un controllo di FORMA, per escludere iniezioni/spazi/righe
|
|
162
|
+
* vuote, mai di CONTENUTO.
|
|
163
|
+
*
|
|
164
|
+
* ⛔⛔ 27/8 — trovato dalla pipeline QA visiva (scripts/qa-visual-pipeline.mjs),
|
|
165
|
+
* non ipotizzato: 12 dei 417 modelli VERI di OpenRouter oggi sono alias
|
|
166
|
+
* "sempre l'ultima versione" con un prefisso `~` (es.
|
|
167
|
+
* `~anthropic/claude-sonnet-latest`, `~deepseek/deepseek-v4-flash-latest`)
|
|
168
|
+
* — verificato con GET https://openrouter.ai/api/v1/models. Senza il `~?`
|
|
169
|
+
* opzionale, scegliere uno di questi nel picker produceva SEMPRE un 400
|
|
170
|
+
* su /api/v1/sessions/custom: la regola "tutti i modelli OpenRouter,
|
|
171
|
+
* nessuna eccezione" (owner, 27/8 mattina) era rotta esattamente sugli
|
|
172
|
+
* alias più comodi da scegliere.
|
|
173
|
+
*/
|
|
174
|
+
/*
|
|
175
|
+
* ⛔⛔⛔ 03/9 — IL CANCELLO CHE AVREBBE FERMATO I MODELLI LOCALI.
|
|
176
|
+
*
|
|
177
|
+
* Owner: «bisogna far girare davvero i modelli di diversi provider… se non
|
|
178
|
+
* riesco a usare i modelli locali, l'applicazione è spacciata».
|
|
179
|
+
*
|
|
180
|
+
* Trovato provando, non leggendo: la convenzione di fonte `local:qwen3-0.6b…`
|
|
181
|
+
* veniva RESPINTA QUI, con un 400 su /api/v1/sessions, molto prima di
|
|
182
|
+
* arrivare al kernel o all'instradamento. Questo formato pretende `autore/
|
|
183
|
+
* nome` — lo slash — perché nasce dagli id OpenRouter, e un modello locale
|
|
184
|
+
* non ha un autore: ha un nome e basta.
|
|
185
|
+
*
|
|
186
|
+
* ⇒ Si ammette un prefisso di FONTE opzionale, e solo per le fonti vere
|
|
187
|
+
* (`local:`, `ollama:`, `openai:`, `deepseek:`, `openrouter:`, `anthropic:`,
|
|
188
|
+
* `gemini:` — le stesse di `model-destination.mjs`). Dopo il prefisso lo
|
|
189
|
+
* slash diventa facoltativo, perché `local:qwen3-0.6b-q2-k` è un nome intero.
|
|
190
|
+
* ⛔ Senza prefisso NIENTE cambia: resta esattamente il formato di prima, e
|
|
191
|
+
* nessuna sessione salvata si comporta diversamente.
|
|
192
|
+
*/
|
|
193
|
+
/*
|
|
194
|
+
* ⛔⛔ 12/09 — P-A: QUESTA STRINGA ERA UNA COPIA DI `FONTI_MODELLO` DENTRO UNA REGEX.
|
|
195
|
+
* Il settimo dei tredici elenchi paralleli, e il piu subdolo: una fonte aggiunta a
|
|
196
|
+
* `model-destination.mjs` e non qui veniva respinta con un 400 su /api/v1/sessions **prima** di
|
|
197
|
+
* arrivare all'instradamento — cioe il fornitore risultava «non riconosciuto» in un file che non
|
|
198
|
+
* parla di fornitori. Adesso la costruisce il registro, e non puo piu divergere.
|
|
199
|
+
* ⛔ Gli id sono validati dal registro (`^[a-z][a-z0-9-]{0,31}$`): nessun carattere da citare.
|
|
200
|
+
*/
|
|
201
|
+
const FONTI_AMMESSE_MODELLO = ID_DESTINAZIONE_CHAT.join('|');
|
|
202
|
+
/** Il formato OpenRouter di sempre: `vendor/nome`, con `~` e `:variante`. INVARIATO. */
|
|
203
|
+
const FORMA_OPENROUTER = '~?[a-z0-9](?:[a-z0-9._-]{0,63}[a-z0-9])?\/[a-z0-9](?:[a-z0-9._:-]{0,63}[a-z0-9])?';
|
|
204
|
+
/**
|
|
205
|
+
* Con un prefisso di fonte lo slash diventa FACOLTATIVO — e solo lì.
|
|
206
|
+
*
|
|
207
|
+
* ⛔ La prima stesura lo rendeva facoltativo dappertutto, e ha fatto cadere
|
|
208
|
+
* quattro test che presidiavano il contratto giusto: «senza slash non è un id
|
|
209
|
+
* OpenRouter». Avevano ragione loro. Un id senza prefisso deve continuare a
|
|
210
|
+
* essere rifiutato esattamente come prima, byte per byte: rilassare un
|
|
211
|
+
* cancello più del necessario è il modo tipico di far passare, mesi dopo,
|
|
212
|
+
* qualcosa che nessuno voleva.
|
|
213
|
+
*/
|
|
214
|
+
const FORMA_CON_FONTE = `(?:${FONTI_AMMESSE_MODELLO}):[a-z0-9](?:[a-z0-9._:/-]{0,127}[a-z0-9])?`;
|
|
215
|
+
const FORMATO_MODELLO_RICHIESTA = new RegExp(`^(?:${FORMA_OPENROUTER}|${FORMA_CON_FONTE})$`, 'i');
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Pura — nessun throw, chi chiama decide il `code`/status HTTP giusto per
|
|
219
|
+
* il proprio contesto (stesso principio delle `require*Body` di
|
|
220
|
+
* `http-app.mjs`, che tengono `QUERY_INVALID` locale a quel file).
|
|
221
|
+
*/
|
|
222
|
+
export function modelloRichiestaValido(raw) {
|
|
223
|
+
return typeof raw === 'string' && FORMATO_MODELLO_RICHIESTA.test(raw);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* ⭐⭐⭐ 27/8, R1 — valori VERI di OpenRouter (docs.ag-ui.com/openrouter,
|
|
228
|
+
* verificato via WebSearch nel piano, sezione "RICOGNIZIONE COMPETITIVA"),
|
|
229
|
+
* non inventati: `effort` controlla il budget di token di ragionamento,
|
|
230
|
+
* `summary` la verbosità di quanto ne viene mostrato.
|
|
231
|
+
*/
|
|
232
|
+
/*
|
|
233
|
+
* ⛔⛔⛔ 06/9 — trovato provando una sessione vera dal primo avvio: scegliendo «Z.ai GLM 5.3 Flash»
|
|
234
|
+
* dal foglio dei modelli, il primo messaggio tornava «Query non valida» e la sessione non partiva
|
|
235
|
+
* MAI. Causa: il catalogo che serviamo noi (`/api/v1/models`, dati OpenRouter) dichiara per quel
|
|
236
|
+
* modello `supportedEfforts: ["max","high","low"]` con `defaultEffort: "max"`, la UI adotta il
|
|
237
|
+
* livello dichiarato dal modello, e questo cancello non conosceva `max`. Non è un caso isolato:
|
|
238
|
+
* **59 modelli del catalogo dichiarano `max`**, 8 lo hanno come predefinito.
|
|
239
|
+
* Ricerca 06/09/2026 (openrouter.ai «Reasoning tokens»; big-AGI #940; OpenRouterTeam/ai-sdk-provider,
|
|
240
|
+
* «Provider options and reasoning»): l'insieme canonico è xhigh/high/medium/low/minimal/none, e i
|
|
241
|
+
* alcune famiglie di modelli accettano anche `max`; ciò che un modello non supporta viene mappato da OpenRouter
|
|
242
|
+
* sul livello più vicino. ⇒ Il cancello ammette anche `max`: rifiutare un valore che il NOSTRO
|
|
243
|
+
* catalogo dichiara è un cancello che litiga con i propri dati.
|
|
244
|
+
*/
|
|
245
|
+
const EFFORT_AMMESSI = new Set(['max', 'xhigh', 'high', 'medium', 'low', 'minimal', 'none']);
|
|
246
|
+
const SUMMARY_AMMESSI = new Set(['auto', 'concise', 'detailed']);
|
|
247
|
+
|
|
248
|
+
/** Stesso principio di modelloRichiestaValido: pura, nessun throw. */
|
|
249
|
+
export function reasoningRichiestaValido(raw) {
|
|
250
|
+
if (raw === null || raw === undefined) return true; // assente è sempre valido: nessun reasoning richiesto
|
|
251
|
+
if (typeof raw !== 'object' || Array.isArray(raw)) return false;
|
|
252
|
+
const chiavi = Object.keys(raw);
|
|
253
|
+
if (chiavi.length === 0 || !chiavi.every((k) => k === 'effort' || k === 'summary')) return false;
|
|
254
|
+
if ('effort' in raw && !EFFORT_AMMESSI.has(raw.effort)) return false;
|
|
255
|
+
if ('summary' in raw && !SUMMARY_AMMESSI.has(raw.summary)) return false;
|
|
256
|
+
return true;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/*
|
|
260
|
+
* ⭐⭐⭐ 28/8 — LA PILLOLA PERMESSI, owner: "read only/workspace write/on
|
|
261
|
+
* request/full access". Le stesse quattro stringhe già usate dal foglio
|
|
262
|
+
* decorativo del frontend (`sheetTemplates.permissions`, app.js, scritto
|
|
263
|
+
* il 27/8) — una grammatica sola, non una seconda tradotta qui (stessa
|
|
264
|
+
* disciplina di `permissions-single-global-grammar.md` in memoria).
|
|
265
|
+
* `undefined`/assente è sempre valido: significa "Workspace write", il
|
|
266
|
+
* comportamento di sempre, mai un valore inventato quando il client non
|
|
267
|
+
* sceglie esplicitamente.
|
|
268
|
+
*/
|
|
269
|
+
const PERMESSI_AMMESSI = new Set(['Read only', 'Workspace write', 'On request', 'Full access']);
|
|
270
|
+
|
|
271
|
+
/** Stesso principio di reasoningRichiestaValido: pura, nessun throw. */
|
|
272
|
+
export function permessiRichiestaValido(raw) {
|
|
273
|
+
return raw === undefined || raw === null || (typeof raw === 'string' && PERMESSI_AMMESSI.has(raw));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/*
|
|
277
|
+
* ⭐⭐⭐ FASE B (28/8) — permesso PER-ATTREZZO, un override più specifico
|
|
278
|
+
* di `permessi` sopra. Le chiavi ammesse sono i 4 nomi attrezzo che
|
|
279
|
+
* chiamano DAVVERO `verificaPermessoScrittura` nel kernel (verificato
|
|
280
|
+
* leggendo talosHarness.mjs, non i 3 di `AZIONI_MUTANTI_PER_HOOK`, un
|
|
281
|
+
* perimetro diverso) — un nome REALE ma fuori da questi 4 (es. `leggi`)
|
|
282
|
+
* sarebbe comunque un override che il gate ignora sempre in silenzio,
|
|
283
|
+
* stesso difetto di un nome INVENTATO: entrambi rifiutati qui, mai solo
|
|
284
|
+
* il secondo.
|
|
285
|
+
*/
|
|
286
|
+
// ⭐ 29/8 — FASE H: `generate_image` aggiunto, quinto attrezzo con ricevuta nel kernel (talosHarness.mjs, ATTREZZI_CON_RICEVUTA) — stesso trattamento degli altri quattro.
|
|
287
|
+
/*
|
|
288
|
+
* ⛔⛔⛔ REVIEW PO-12, 13/09/2026 — `file_edit` MANCAVA, ed era un buco travestito da voce assente.
|
|
289
|
+
*
|
|
290
|
+
* Nato oggi, l'attrezzo di modifica scrive su un file per percorso esattamente come `scrivi`, e il
|
|
291
|
+
* cancello lo VEDE: `verificaPermessoScrittura` cerca l'eccezione con `permessiPerAttrezzo[azione.tipo]`
|
|
292
|
+
* (talosHarness.mjs r. 5727) e per la modifica il tipo dell'azione e' gia' `file_edit` (r. 9218).
|
|
293
|
+
*
|
|
294
|
+
* ⇒ Il cancello avrebbe onorato un'eccezione su `file_edit`. L'unica cosa che lo impediva era QUESTO
|
|
295
|
+
* insieme: `permessiPerAttrezzoRichiestaValido` rifiuta ogni chiave che non sia qui dentro, quindi la
|
|
296
|
+
* regola non era nemmeno CONFIGURABILE. Due conseguenze misurate in review:
|
|
297
|
+
* · chi aveva messo «chiedi» o «nega» su `scrivi` non aveva NESSUN controllo equivalente sulla
|
|
298
|
+
* modifica — cioe' l'attrezzo con cui il modello lavora sul codice che gia' esiste;
|
|
299
|
+
* · e chi provava a metterlo si vedeva rifiutare la richiesta come invalida.
|
|
300
|
+
*
|
|
301
|
+
* ⭐ Per la regola scritta qui sopra ci appartiene di diritto: le chiavi ammesse sono i nomi che
|
|
302
|
+
* CHIAMANO DAVVERO il cancello di scrittura, e questo lo chiama. Non e' un'aggiunta di comodo.
|
|
303
|
+
* ⛔ E il Capability hub legge da qui: senza questa riga mostrava «nessun cancello» su un attrezzo
|
|
304
|
+
* che scrive.
|
|
305
|
+
*/
|
|
306
|
+
/** ⭐ O-01 (04/9) — esportato: il Capability hub mostra su OGNI attrezzo se ha un cancello per-attrezzo, e deve leggerlo da QUI, mai da un secondo elenco. */
|
|
307
|
+
export const ATTREZZI_CON_PERMESSO_PER_ATTREZZO = new Set(['scrivi', 'file_edit', 'prova', 'shell', 'document_create', 'generate_image']);
|
|
308
|
+
const VALORI_PERMESSO_PER_ATTREZZO = new Set(['sempre', 'chiedi', 'nega']);
|
|
309
|
+
|
|
310
|
+
/** Stesso principio di reasoningRichiestaValido: pura, nessun throw. */
|
|
311
|
+
export function permessiPerAttrezzoRichiestaValido(raw) {
|
|
312
|
+
if (raw === undefined || raw === null) return true; // assente è sempre valido: nessun override, comportamento di oggi
|
|
313
|
+
if (typeof raw !== 'object' || Array.isArray(raw)) return false;
|
|
314
|
+
const chiavi = Object.keys(raw);
|
|
315
|
+
if (chiavi.length === 0) return false; // {} esplicito non ha senso: si omette il campo, non si manda vuoto
|
|
316
|
+
return chiavi.every((k) => ATTREZZI_CON_PERMESSO_PER_ATTREZZO.has(k) && VALORI_PERMESSO_PER_ATTREZZO.has(raw[k]));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* ⭐⭐⭐ 27/8 — una cartella libera (non il corpus benchmark) su cui far
|
|
321
|
+
* girare un compito VERO, DIRETTAMENTE. Come i competitor verificati,
|
|
322
|
+
* l’installazione parte dalla workspace del progetto che ospita il server:
|
|
323
|
+
* l’utente non deve preparare variabili prima di poter aprire "Nuova
|
|
324
|
+
* sessione". `TALOS_HARNESS_UI_PROJECT_DIRS` resta l’estensione esplicita
|
|
325
|
+
* per aggiungere altre cartelle.
|
|
326
|
+
*
|
|
327
|
+
* Elenco separato da `;` (come PATH su Windows, mai virgola: un percorso
|
|
328
|
+
* reale può contenerne una). Ogni percorso deve esistere, essere una
|
|
329
|
+
* directory, leggibile E scrivibile — un agente che ci scrive davvero
|
|
330
|
+
* su una cartella non scrivibile fallirebbe a metà lavoro, meglio
|
|
331
|
+
* scoprirlo all’avvio del server che a sessione già in corso.
|
|
332
|
+
*/
|
|
333
|
+
function parseCartelleProgetto(raw, defaultProjectDir) {
|
|
334
|
+
const richiesti = typeof raw === 'string'
|
|
335
|
+
? raw.split(';').map((valore) => valore.trim()).filter((valore) => valore.length > 0)
|
|
336
|
+
: [];
|
|
337
|
+
if (raw === undefined || raw === '' || richiesti.length === 0) {
|
|
338
|
+
if (!defaultProjectDir) return Object.freeze([]);
|
|
339
|
+
let percorso;
|
|
340
|
+
try {
|
|
341
|
+
percorso = realpathSync(defaultProjectDir);
|
|
342
|
+
if (!statSync(percorso).isDirectory()) fail('La workspace predefinita non è una directory');
|
|
343
|
+
accessSync(percorso, constants.R_OK | constants.W_OK);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
if (error instanceof ConfigurationError) throw error;
|
|
346
|
+
fail('La workspace predefinita non esiste o non è leggibile/scrivibile');
|
|
347
|
+
}
|
|
348
|
+
return Object.freeze([{ id: 'default', percorso, nome: percorso.split(/[\\/]/).pop() || percorso }]);
|
|
349
|
+
}
|
|
350
|
+
if (typeof raw !== 'string') fail('TALOS_HARNESS_UI_PROJECT_DIRS non valida');
|
|
351
|
+
const cartelle = richiesti.map((percorsoInput, indice) => {
|
|
352
|
+
if (!isAbsolute(percorsoInput)) fail(`TALOS_HARNESS_UI_PROJECT_DIRS[${indice}] deve essere assoluta: ${percorsoInput}`);
|
|
353
|
+
let percorso;
|
|
354
|
+
try {
|
|
355
|
+
percorso = realpathSync(percorsoInput);
|
|
356
|
+
if (!statSync(percorso).isDirectory()) fail(`TALOS_HARNESS_UI_PROJECT_DIRS[${indice}] non è una directory: ${percorsoInput}`);
|
|
357
|
+
accessSync(percorso, constants.R_OK | constants.W_OK);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
if (error instanceof ConfigurationError) throw error;
|
|
360
|
+
fail(`TALOS_HARNESS_UI_PROJECT_DIRS[${indice}] non esiste o non è leggibile/scrivibile: ${percorsoInput}`);
|
|
361
|
+
}
|
|
362
|
+
return Object.freeze({ id: String(indice), percorso, nome: percorso.split(/[\\/]/).pop() || percorso });
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
const duplicati = new Set();
|
|
366
|
+
for (const { percorso } of cartelle) {
|
|
367
|
+
if (duplicati.has(percorso)) fail(`TALOS_HARNESS_UI_PROJECT_DIRS ripete la stessa cartella: ${percorso}`);
|
|
368
|
+
duplicati.add(percorso);
|
|
369
|
+
}
|
|
370
|
+
return Object.freeze(cartelle);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function parsePort(raw) {
|
|
374
|
+
if (raw === undefined || raw === '') return DEFAULT_PORT;
|
|
375
|
+
if (typeof raw !== 'string' || !/^\d+$/.test(raw)) {
|
|
376
|
+
fail('TALOS_HARNESS_UI_PORT non valida');
|
|
377
|
+
}
|
|
378
|
+
const port = Number(raw);
|
|
379
|
+
if (!Number.isInteger(port) || port < 1024 || port > 65535) {
|
|
380
|
+
fail('TALOS_HARNESS_UI_PORT fuori intervallo');
|
|
381
|
+
}
|
|
382
|
+
return port;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function parseLlamaServerPath(raw, moduleUrl, sondaMotore) {
|
|
386
|
+
if (typeof raw === 'string' && raw.trim() !== '') return resolve(raw.trim());
|
|
387
|
+
/*
|
|
388
|
+
* ⭐⭐⭐ 03/9 — SI PREFERISCE LA BUILD CON LA GPU, se c'è.
|
|
389
|
+
*
|
|
390
|
+
* ⛔ Debito che stavo per lasciare aperto: la GPU l'avevo accesa passando
|
|
391
|
+
* il percorso a mano, quindi al primo riavvio normale il server sarebbe
|
|
392
|
+
* tornato sulla build CPU-only — e nessuno se ne sarebbe accorto, perché
|
|
393
|
+
* funziona lo stesso, solo lentissimo. Una cura che vive in una variabile
|
|
394
|
+
* d'ambiente digitata una volta non è una cura.
|
|
395
|
+
*
|
|
396
|
+
* MISURATO: `llama-b10517-bin-win-cpu-x64` risponde «Available devices:
|
|
397
|
+
* (none)»; la variante Vulkan della stessa versione elenca la scheda con
|
|
398
|
+
* la sua VRAM, e su un 27B porta da «non finisce» a 7,7 token/s.
|
|
399
|
+
*
|
|
400
|
+
* ⛔ L'ordine conta: prima la variante accelerata, poi quella di sempre.
|
|
401
|
+
* E resta solo un DEFAULT: `TALOS_LLAMA_SERVER_PATH` continua a vincere,
|
|
402
|
+
* perché chi sa cosa sta facendo deve poter scegliere.
|
|
403
|
+
*/
|
|
404
|
+
for (const cartella of ['.local-runtime/b10517-vulkan/', '.local-runtime/b10517/']) {
|
|
405
|
+
try {
|
|
406
|
+
const candidate = fileURLToPath(new URL(`${cartella}llama-server.exe`, moduleUrl));
|
|
407
|
+
if (!statSync(candidate).isFile()) continue;
|
|
408
|
+
if (cartella.includes('-vulkan')) {
|
|
409
|
+
const esito = sondaMotore(candidate, ['--list-devices'], { shell: false, windowsHide: true, timeout: 15000, encoding: 'utf8', maxBuffer: 256 * 1024 });
|
|
410
|
+
if (esito.error || esito.status !== 0 || !/^\s*Vulkan\d+:\s*\S.+$/m.test(`${esito.stdout ?? ''}\n${esito.stderr ?? ''}`)) continue;
|
|
411
|
+
}
|
|
412
|
+
return candidate;
|
|
413
|
+
} catch {
|
|
414
|
+
// The official runtime is optional during development and in clean clones.
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* ⭐⭐⭐ 02/9 — dove vivono le sessioni persistite. Fino a oggi era CABLATO
|
|
422
|
+
* in `server.mjs` (`.sessions-store/` accanto al file), senza modo di
|
|
423
|
+
* spostarlo: conseguenza misurata, la suite Playwright — che punta al
|
|
424
|
+
* 4174 dell'owner — girava sulle sue sessioni VERE, e lo stesso codice
|
|
425
|
+
* dava 21 rossi a un giro e 19 al successivo. Un conteggio così non è un
|
|
426
|
+
* cancello (vedi LEDGER-STREAMING-SCROLL-TERMINALE-2026-09-02.md).
|
|
427
|
+
*
|
|
428
|
+
* ⭐ Ricerca 02/9 — lo stato dell'arte rende la cartella di stato
|
|
429
|
+
* sovrascrivibile da ambiente, esattamente per non toccare i dati veri
|
|
430
|
+
* dell'utente durante i test (una home isolata per i profili di prova,
|
|
431
|
+
* un percorso di configurazione separato da quello reale, un database di
|
|
432
|
+
* sessione proprio per ogni profilo). Questa variabile è la nostra.
|
|
433
|
+
*
|
|
434
|
+
* ⛔ Nessun `fail()` se manca e nessuna creazione qui: assente = il
|
|
435
|
+
* comportamento di sempre, la cartella accanto a `server.mjs`. Un
|
|
436
|
+
* percorso relativo si risolve sulla cwd, come ogni altro percorso di
|
|
437
|
+
* questo file.
|
|
438
|
+
*/
|
|
439
|
+
function parseCartellaStore(raw, moduleUrl) {
|
|
440
|
+
if (typeof raw === 'string' && raw.trim() !== '') return resolve(raw.trim());
|
|
441
|
+
return fileURLToPath(new URL('.sessions-store/', moduleUrl));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// P-E: un mirror del catalogo pubblico può cambiare; non può contenere credenziali.
|
|
445
|
+
function parseModelsDevUrl(raw) {
|
|
446
|
+
if (raw === undefined || raw === '') return 'https://models.dev/api.json';
|
|
447
|
+
try {
|
|
448
|
+
if (typeof raw !== 'string') throw new Error();
|
|
449
|
+
const url = new URL(raw.trim());
|
|
450
|
+
if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password || url.hash) throw new Error();
|
|
451
|
+
return url.href;
|
|
452
|
+
} catch {
|
|
453
|
+
fail('TALOS_HARNESS_UI_MODELS_DEV_URL deve essere un indirizzo HTTP(S) senza credenziali o frammenti');
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/*
|
|
458
|
+
* ⛔⛔⛔ 07/9 — IL KERNEL ORA VIVE NEL REPO, e questa funzione è il motivo per cui prima non
|
|
459
|
+
* bastava averlo. Senza `TALOS_OWNER_RUNTIME_MODULE` questa tornava `undefined`: il server partiva,
|
|
460
|
+
* `/api/v1/health` rispondeva 200, e **ogni giro reale falliva** con `OWNER_RUNTIME_NOT_CONFIGURED`.
|
|
461
|
+
* Chi clonava il repo otteneva un prodotto che sembrava sano e non poteva fare niente — e il README
|
|
462
|
+
* non nominava nemmeno la variabile.
|
|
463
|
+
* ⇒ Il kernel canonico è `src/kernel/talosHarness.mjs`, dentro questo repo: si usa quello per
|
|
464
|
+
* default. La variabile resta, e serve a puntare ALTROVE — è così che l'owner sviluppa il kernel
|
|
465
|
+
* nel suo worktree senza toccare il repo.
|
|
466
|
+
* ⛔ Il default non si inventa: se quel file non c'è (pacchetto tagliato, copia parziale) si torna
|
|
467
|
+
* a `undefined`, cioè al comportamento di prima, invece di far fallire l'avvio del server.
|
|
468
|
+
* ⛔ E il kernel vive sotto `src/` per una ragione misurata, non estetica: cerca il compilatore
|
|
469
|
+
* TypeScript in `../../node_modules/typescript/lib` (il suo cancello semantico legge `lib.*.d.ts`).
|
|
470
|
+
* Da `src/kernel/` quel percorso è `harness-ui/node_modules`, dove `npm ci` lo installa davvero:
|
|
471
|
+
* spostandolo altrove i due test del cancello semantico falliscono — provato, 536/538.
|
|
472
|
+
*/
|
|
473
|
+
function kernelNelRepo(moduleUrl) {
|
|
474
|
+
try {
|
|
475
|
+
const percorso = fileURLToPath(new URL('src/kernel/talosHarness.mjs', moduleUrl));
|
|
476
|
+
return statSync(percorso).isFile() ? percorso : undefined;
|
|
477
|
+
} catch {
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function parseOwnerRuntimeModule(raw, moduleUrl) {
|
|
483
|
+
if (raw === undefined || raw === '') return kernelNelRepo(moduleUrl);
|
|
484
|
+
if (typeof raw !== 'string' || raw.trim() === '' || !isAbsolute(raw.trim())) {
|
|
485
|
+
fail('TALOS_OWNER_RUNTIME_MODULE deve essere un file assoluto');
|
|
486
|
+
}
|
|
487
|
+
const percorso = resolve(raw.trim());
|
|
488
|
+
try {
|
|
489
|
+
if (!statSync(percorso).isFile()) fail('TALOS_OWNER_RUNTIME_MODULE non è un file');
|
|
490
|
+
} catch (error) {
|
|
491
|
+
if (error instanceof ConfigurationError) throw error;
|
|
492
|
+
fail('TALOS_OWNER_RUNTIME_MODULE non esiste o non è leggibile');
|
|
493
|
+
}
|
|
494
|
+
return percorso;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function parseContextTrial(env) {
|
|
498
|
+
const raw = env.TALOS_CONTEXT_TRIAL;
|
|
499
|
+
if (raw === undefined || raw === '') return null;
|
|
500
|
+
if (typeof raw !== 'string' || raw.length > 65536) fail('TALOS_CONTEXT_TRIAL deve essere un manifest JSON limitato alle chat di prova.');
|
|
501
|
+
if (!env.TALOS_HARNESS_UI_PORT || parsePort(env.TALOS_HARNESS_UI_PORT) === 4174 || typeof env.TALOS_HARNESS_UI_SESSIONS_DIR !== 'string' || !env.TALOS_HARNESS_UI_SESSIONS_DIR.trim()) fail('Il trial del contesto richiede una porta esplicita diversa da 4174 e una directory sessioni isolata.');
|
|
502
|
+
let trial;
|
|
503
|
+
try { trial = JSON.parse(raw); } catch { fail('TALOS_CONTEXT_TRIAL non contiene JSON valido.'); }
|
|
504
|
+
if (!trial || Array.isArray(trial) || Object.keys(trial).some(key => !['sessionIds', 'models'].includes(key)) || !Array.isArray(trial.sessionIds) || !trial.sessionIds.length || !Array.isArray(trial.models) || !trial.models.length) fail('Manifest del trial del contesto non valido.');
|
|
505
|
+
if (trial.sessionIds.some(id => typeof id !== 'string' || !/^[a-zA-Z0-9_-]{1,256}$/u.test(id)) || new Set(trial.sessionIds).size !== trial.sessionIds.length) fail('Identità delle chat di prova non valide o duplicate.');
|
|
506
|
+
const models = new Set();
|
|
507
|
+
for (const profile of trial.models) {
|
|
508
|
+
if (!profile || Array.isArray(profile) || Object.keys(profile).some(key => !['provider', 'model', 'windowTokens', 'responseReserve'].includes(key)) || !ID_DESTINAZIONE_CHAT.includes(profile.provider) || typeof profile.model !== 'string' || !profile.model.trim() || !Number.isSafeInteger(profile.windowTokens) || !Number.isSafeInteger(profile.responseReserve) || profile.responseReserve < 1 || profile.responseReserve >= profile.windowTokens) fail('Profilo modello del trial non valido: indicare finestra e riserva, senza credenziali.');
|
|
509
|
+
const id = `${profile.provider}:${profile.model}`;
|
|
510
|
+
if (models.has(id)) fail('Profilo modello duplicato nel trial.');
|
|
511
|
+
models.add(id); Object.freeze(profile);
|
|
512
|
+
}
|
|
513
|
+
Object.freeze(trial.models); Object.freeze(trial.sessionIds);
|
|
514
|
+
return Object.freeze(trial);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
export function loadConfig(
|
|
518
|
+
env,
|
|
519
|
+
moduleUrl = new URL('../server.mjs', import.meta.url),
|
|
520
|
+
{ sondaMotore = spawnSync } = {},
|
|
521
|
+
) {
|
|
522
|
+
if (!env || typeof env !== 'object') fail('Configurazione ambiente non valida');
|
|
523
|
+
|
|
524
|
+
const host = env.TALOS_HARNESS_UI_HOST || DEFAULT_HOST;
|
|
525
|
+
if (typeof host !== 'string' || !LOOPBACK_HOSTS.has(host)) {
|
|
526
|
+
fail('TALOS_HARNESS_UI_HOST deve essere loopback');
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/*
|
|
530
|
+
* ⛔⛔⛔ 02/09 — CORRETTO, il commento sotto era rimasto FALSO per due
|
|
531
|
+
* giorni: descriveva ancora DEC-053 (26/8, `mobile/public/harness-ui/`
|
|
532
|
+
* canonico) mentre il codice, dal commit `16677c48` (31/8, "chiude i
|
|
533
|
+
* tre blocchi runtime desktop"), risolve `./public/` — verificato dal
|
|
534
|
+
* vivo (il server serve `app.js` da 530.197 byte, combacia con
|
|
535
|
+
* `harness-ui/public/`, non con `mobile/public/harness-ui/` che è
|
|
536
|
+
* rimasto fermo al 31/8 ore 14:52, ~95KB indietro). Il cambio non era
|
|
537
|
+
* un refuso: nello STESSO commit sono arrivati `harness-ui/public/talos/
|
|
538
|
+
* brand/` (font e logo propri del desktop) e `harness-ui/scripts/
|
|
539
|
+
* build-ui.mjs`/`verify-ui-manifest.mjs`, che hanno `harness-ui/public`
|
|
540
|
+
* come sorgente esplicita e cablata (`SOURCE = ... join(ROOT, 'public')`,
|
|
541
|
+
* `manifest.json` dichiara `source: 'harness-ui/public'`). ⇒ Il desktop
|
|
542
|
+
* ha un bundle proprio (font/logo/pipeline di build+verifica dedicati),
|
|
543
|
+
* non più preso in prestito dal mobile — una direzione ragionevole per
|
|
544
|
+
* un prodotto desktop maturo. Non è mai stato scritto un successore
|
|
545
|
+
* dichiarato a DEC-053: questo commento lo è.
|
|
546
|
+
*
|
|
547
|
+
* ⛔ Aperto, non deciso qui: DEC-053 esisteva ANCHE per servire lo stesso
|
|
548
|
+
* bundle al telefono via `adb reverse` (piano §3, mai implementato).
|
|
549
|
+
* Se quell'intento è ancora vivo, va ripensato esplicitamente (il
|
|
550
|
+
* telefono aggancerebbe un bundle desktop-specifico, non più condiviso)
|
|
551
|
+
* — decisione dell'owner, non presa qui. `mobile/public/harness-ui/`
|
|
552
|
+
* resta sul disco, stantio, non cancellato: nessuno l'ha dichiarato
|
|
553
|
+
* morto per iscritto.
|
|
554
|
+
*
|
|
555
|
+
* Override via env solo per i test, mai per uso normale (nessun fail()
|
|
556
|
+
* se assente: resta il default).
|
|
557
|
+
*/
|
|
558
|
+
let publicDir;
|
|
559
|
+
try {
|
|
560
|
+
publicDir = env.TALOS_HARNESS_UI_PUBLIC_DIR
|
|
561
|
+
? resolve(String(env.TALOS_HARNESS_UI_PUBLIC_DIR))
|
|
562
|
+
: resolve(fileURLToPath(new URL('./public/', moduleUrl)));
|
|
563
|
+
} catch {
|
|
564
|
+
fail('Percorso modulo non valido');
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return Object.freeze({
|
|
568
|
+
host,
|
|
569
|
+
contextTrial: parseContextTrial(env),
|
|
570
|
+
port: parsePort(env.TALOS_HARNESS_UI_PORT),
|
|
571
|
+
// ⭐ 03/9, R-01: true solo se l'owner ha scritto qualcosa in
|
|
572
|
+
// TALOS_HARNESS_UI_PORT — vedi trovaPortaLibera() sopra.
|
|
573
|
+
portaEsplicita: typeof env.TALOS_HARNESS_UI_PORT === 'string' && env.TALOS_HARNESS_UI_PORT !== '',
|
|
574
|
+
publicDir,
|
|
575
|
+
modello: parseModello(env.TALOS_HARNESS_UI_MODEL),
|
|
576
|
+
// Come lo stato dell'arte: in assenza di elenco esplicito la prima
|
|
577
|
+
// workspace è la radice del progetto desktop che contiene il server.
|
|
578
|
+
cartelleProgetto: parseCartelleProgetto(
|
|
579
|
+
env.TALOS_HARNESS_UI_PROJECT_DIRS,
|
|
580
|
+
resolve(fileURLToPath(new URL('../', moduleUrl))),
|
|
581
|
+
),
|
|
582
|
+
/*
|
|
583
|
+
* ⛔ Nessun fail() se manca: Harness UI resta usabile in sola lettura
|
|
584
|
+
* (campagne, elenco task) anche senza una chiave configurata — è
|
|
585
|
+
* session-registry.avvia() a rifiutare per-richiesta con CONFIG_INVALID
|
|
586
|
+
* quando si prova davvero ad avviare una sessione, non l'avvio del
|
|
587
|
+
* server. Stesso nome env di TALOS-BANCO/provaTalos.mjs: una chiave
|
|
588
|
+
* sola, non una copia con un nome diverso che potrebbe disallinearsi.
|
|
589
|
+
*/
|
|
590
|
+
chiaveApi: typeof env.OPENROUTER_API_KEY === 'string' ? env.OPENROUTER_API_KEY : undefined,
|
|
591
|
+
hfToken: typeof env.HF_TOKEN === 'string' && env.HF_TOKEN.trim() ? env.HF_TOKEN.trim() : undefined,
|
|
592
|
+
cartellaStore: parseCartellaStore(env.TALOS_HARNESS_UI_SESSIONS_DIR, moduleUrl),
|
|
593
|
+
modelsDevUrl: parseModelsDevUrl(env.TALOS_HARNESS_UI_MODELS_DEV_URL),
|
|
594
|
+
llamaServerPath: parseLlamaServerPath(env.TALOS_LLAMA_SERVER_PATH, moduleUrl, sondaMotore),
|
|
595
|
+
llamaServerFallbackPath: typeof env.TALOS_LLAMA_SERVER_FALLBACK_PATH === 'string' && env.TALOS_LLAMA_SERVER_FALLBACK_PATH.trim()
|
|
596
|
+
? resolve(env.TALOS_LLAMA_SERVER_FALLBACK_PATH.trim())
|
|
597
|
+
: (!env.TALOS_LLAMA_SERVER_PATH?.trim() ? parseLlamaServerPath(undefined, moduleUrl, () => ({ status: 1 })) : undefined),
|
|
598
|
+
ownerRuntimeModule: parseOwnerRuntimeModule(env.TALOS_OWNER_RUNTIME_MODULE, moduleUrl),
|
|
599
|
+
ricercaWeb: parseRicercaWeb(env),
|
|
600
|
+
labs: parseLabs(env.TALOS_LABS, moduleUrl), // ⭐ 04/9, W0-04
|
|
601
|
+
token: parseToken(env.TALOS_HARNESS_UI_TOKEN), // ⭐ 04/9, W1-10
|
|
602
|
+
|
|
603
|
+
firmaRicevute: parseFirmaRicevute(env),
|
|
604
|
+
immagine: parseImmagine(env),
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* ⭐⭐⭐ 29/8, FASE D — la firma Ed25519 delle ricevute
|
|
610
|
+
* (`talosHarness.mjs`, `creaRicevutaOperazione`/`firma`). Stesso principio
|
|
611
|
+
* onesto di `parseRicercaWeb` due funzioni sotto: `undefined` quando non
|
|
612
|
+
* configurata, il server resta usabile — le ricevute restano non firmate,
|
|
613
|
+
* comportamento di sempre, mai un errore che blocca l'avvio per una
|
|
614
|
+
* funzione opzionale. Provisioning: `node src/harness-receipt-keypair.mjs
|
|
615
|
+
* --env-file <path>` (vedi quel file — stesso pattern di
|
|
616
|
+
* `browser-action-keypair.mjs`, AVM, con l'algoritmo giusto per una
|
|
617
|
+
* ricevuta d'audit permanente, non un token con scadenza).
|
|
618
|
+
*
|
|
619
|
+
* ⛔ Validato qui, non solo passato: una chiave malformata configurata a
|
|
620
|
+
* metà (solo l'id, senza la chiave — o viceversa) fallisce l'avvio con
|
|
621
|
+
* ConfigurationError invece di firmare silenziosamente con un valore
|
|
622
|
+
* rotto e produrre ricevute che nessuno può verificare.
|
|
623
|
+
*/
|
|
624
|
+
function parseFirmaRicevute(env) {
|
|
625
|
+
const keyId = typeof env.TALOS_HARNESS_RECEIPT_KEY_ID === 'string' ? env.TALOS_HARNESS_RECEIPT_KEY_ID.trim() : '';
|
|
626
|
+
const privateKeyB64 = typeof env.TALOS_HARNESS_RECEIPT_PRIVATE_KEY_B64 === 'string'
|
|
627
|
+
? env.TALOS_HARNESS_RECEIPT_PRIVATE_KEY_B64.trim()
|
|
628
|
+
: '';
|
|
629
|
+
if (!keyId && !privateKeyB64) return undefined;
|
|
630
|
+
if (!keyId || !privateKeyB64) {
|
|
631
|
+
fail('TALOS_HARNESS_RECEIPT_KEY_ID e TALOS_HARNESS_RECEIPT_PRIVATE_KEY_B64 vanno configurate insieme, mai una sola');
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
let chiavePrivata;
|
|
635
|
+
try {
|
|
636
|
+
chiavePrivata = Buffer.from(privateKeyB64, 'base64').toString('utf8');
|
|
637
|
+
const keyObject = createPrivateKey(chiavePrivata);
|
|
638
|
+
if (keyObject.asymmetricKeyType !== 'ed25519') fail('TALOS_HARNESS_RECEIPT_PRIVATE_KEY_B64 non è una chiave Ed25519');
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error instanceof ConfigurationError) throw error;
|
|
641
|
+
fail('TALOS_HARNESS_RECEIPT_PRIVATE_KEY_B64 non è una chiave privata PKCS8 valida in base64');
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
return Object.freeze({ chiavePrivata, keyId });
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/*
|
|
648
|
+
* ⭐⭐⭐ 29/8 — FASE H, `generate_image`. A differenza di `parseRicercaWeb`
|
|
649
|
+
* sotto: ZERO nuova credenziale (il ONE-UP dichiarato, vedi il piano
|
|
650
|
+
* madre) — `image-generator.mjs` riusa `chiaveApi` sopra,
|
|
651
|
+
* mai una seconda chiave. Il DEFAULT è un modello DEDICATO reale e
|
|
652
|
+
* verificato dal vivo il 29/8 (`GET /api/v1/images/models`, non
|
|
653
|
+
* presunto): funziona sempre, senza che l'owner debba configurare
|
|
654
|
+
* niente. `TALOS_HARNESS_UI_IMMAGINE_NATIVA=1` dichiara che il modello
|
|
655
|
+
* scelto è invece un modello NATIVO (chat/completions+modalities) — un
|
|
656
|
+
* fatto sul MODELLO, non deducibile da questo file (vedi la doc in
|
|
657
|
+
* image-generator.mjs sul perché non si chiama un catalogo per
|
|
658
|
+
* scoprirlo a runtime).
|
|
659
|
+
*/
|
|
660
|
+
const IMMAGINE_MODELLO_DEDICATO_DEFAULT = 'bytedance-seed/seedream-4.5';
|
|
661
|
+
|
|
662
|
+
function parseImmagine(env) {
|
|
663
|
+
const modello = typeof env.TALOS_HARNESS_UI_IMMAGINE_MODELLO === 'string' && env.TALOS_HARNESS_UI_IMMAGINE_MODELLO.trim()
|
|
664
|
+
? env.TALOS_HARNESS_UI_IMMAGINE_MODELLO.trim()
|
|
665
|
+
: IMMAGINE_MODELLO_DEDICATO_DEFAULT;
|
|
666
|
+
const nativo = String(env.TALOS_HARNESS_UI_IMMAGINE_NATIVA ?? '').trim() === '1';
|
|
667
|
+
return Object.freeze({ modello, nativo });
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const PROVIDER_RICERCA_AMMESSI = new Set(['tavily', 'brave', 'searxng', 'custom']);
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* ⭐⭐⭐ 28/8, owner: "l'harness desktop diventa l'unica chat, con tutti i
|
|
674
|
+
* tool come... la ricerca web" — stesso quattro-fonti già scelto per il
|
|
675
|
+
* mobile (`ATTREZZI_ESTESI` in talosHarness.mjs, D1 24/8: Tavily prima
|
|
676
|
+
* porta). `undefined` quando non configurato — stesso principio onesto di
|
|
677
|
+
* `chiaveApi`: il server resta usabile, il tool `web_search` dichiara "not
|
|
678
|
+
* configured" invece di tentare una chiamata senza credenziali (vedi il
|
|
679
|
+
* dispatcher in talosHarness.mjs).
|
|
680
|
+
*/
|
|
681
|
+
function parseRicercaWeb(env) {
|
|
682
|
+
const provider = typeof env.TALOS_HARNESS_SEARCH_PROVIDER === 'string' && env.TALOS_HARNESS_SEARCH_PROVIDER.trim()
|
|
683
|
+
? env.TALOS_HARNESS_SEARCH_PROVIDER.trim().toLowerCase()
|
|
684
|
+
: 'tavily';
|
|
685
|
+
const apiKey = typeof env.TALOS_HARNESS_SEARCH_API_KEY === 'string' && env.TALOS_HARNESS_SEARCH_API_KEY.trim()
|
|
686
|
+
? env.TALOS_HARNESS_SEARCH_API_KEY.trim()
|
|
687
|
+
: undefined;
|
|
688
|
+
const endpoint = typeof env.TALOS_HARNESS_SEARCH_ENDPOINT === 'string' && env.TALOS_HARNESS_SEARCH_ENDPOINT.trim()
|
|
689
|
+
? env.TALOS_HARNESS_SEARCH_ENDPOINT.trim()
|
|
690
|
+
: undefined;
|
|
691
|
+
// Nessuna credenziale/endpoint: web_search resta offerto (parità di attrezzi) ma il dispatcher del kernel lo dichiara onestamente non configurato — nessun tentativo di rete.
|
|
692
|
+
if (!apiKey && !endpoint) return undefined;
|
|
693
|
+
if (!PROVIDER_RICERCA_AMMESSI.has(provider)) {
|
|
694
|
+
fail(`TALOS_HARNESS_SEARCH_PROVIDER deve essere uno fra: ${[...PROVIDER_RICERCA_AMMESSI].join(', ')}`);
|
|
695
|
+
}
|
|
696
|
+
return Object.freeze({ provider, ...(apiKey ? { apiKey } : {}), ...(endpoint ? { endpoint } : {}) });
|
|
697
|
+
}
|