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.
- package/LICENSE +661 -0
- package/README.md +111 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +97 -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 +185 -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 +43 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +82 -0
- package/dist/i18n/en/errors.js +246 -0
- package/dist/i18n/en/firstrun.js +89 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +736 -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 +330 -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/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-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 +262 -0
- package/dist/runtime/talos-composition.js +1006 -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/app.js +3761 -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 +559 -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 +32 -0
- package/dist/tui/components/terminal-shell.js +205 -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/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 +139 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +45 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +39 -0
- package/dist/tui/overlays/approval-dialog.js +640 -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 +72 -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 +145 -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 +287 -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 +778 -0
- package/dist/tui/session-export.js +54 -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 +80 -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 +25 -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 +63 -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,452 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* L9 (12/09/2026) — LA RACCOLTA VIVA: il collettore portato, messo DENTRO la corsa vera.
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* Che cosa c'era prima di questo file, e perché non bastava
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* `collector.mjs` è il porto fedele di `researchCollector.ts`: dato un ramo, cerca, legge le
|
|
9
|
+
* prime pagine e tiene il testo. È provato, è puro, e l'I/O gli entra da `deps` — ma sul
|
|
10
|
+
* desktop NON GIRAVA. La ricerca approfondita qui è una sessione figlia che parla col kernel e
|
|
11
|
+
* chiama `web_search` e `naviga` quando vuole lei: non c'è nessun punto in cui qualcuno chiami
|
|
12
|
+
* `talosResearchCollect(deps, ramo)`. Conseguenza misurata l'11 e il 12/09: giornale con TRE
|
|
13
|
+
* eventi (`run_started`, `run_finished` e poco altro), `Piano` e `Speso` vuoti a schermo, le
|
|
14
|
+
* fonti mai tenute su disco, e quindi le affermazioni depositate tutte «non verificate» —
|
|
15
|
+
* perché senza il testo della pagina non c'è niente contro cui confrontare un passaggio.
|
|
16
|
+
*
|
|
17
|
+
* ⛔ La cura NON è far guidare il collettore al posto della figlia. Il motore del mobile
|
|
18
|
+
* decide lui che cosa cercare; qui è il modello a decidere, ed è il prodotto che l'owner ha
|
|
19
|
+
* scelto (una ricerca che ragiona, non uno scraper con un piano fisso). Costringere la figlia
|
|
20
|
+
* dentro `talosResearchCollect` vorrebbe dire buttare via la parte che funziona.
|
|
21
|
+
*
|
|
22
|
+
* ⇒ Questo file è il PONTE: gli attrezzi che la figlia usa davvero (`web_search`, `naviga`)
|
|
23
|
+
* passano da qui, e qui succede esattamente quello che il collettore faceva — la cache della
|
|
24
|
+
* corsa (`fetch-cache.mjs`), il budget di pagina (`page-budget.mjs`), il testo TENUTO
|
|
25
|
+
* (`fonti/<sha256>.txt`), la spesa CONTATA — più la cosa che il collettore non poteva fare da
|
|
26
|
+
* solo: ogni passo finisce nel GIORNALE, che è ciò che rende la corsa riprendibile.
|
|
27
|
+
*
|
|
28
|
+
* ⛔⛔ LA PORTA È UNA SOLA, ed è deliberato: questo oggetto espone `around(descrittore,
|
|
29
|
+
* produttore)`, cioè la STESSA firma di `fetch-cache.mjs`. Il kernel riceve un `cacheWeb` e
|
|
30
|
+
* non sa (e non deve sapere) se dietro c'è la cache nuda o la raccolta: un secondo punto di
|
|
31
|
+
* aggancio nel kernel sarebbe un secondo contratto da tenere allineato, ed è il modo in cui
|
|
32
|
+
* due strade divergono in silenzio. Il kernel resta condiviso col mobile e non importa una
|
|
33
|
+
* sola riga di `src/research/`.
|
|
34
|
+
*
|
|
35
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
36
|
+
* Ricerca web PRIMA di scrivere — fonti primarie, lette il 12/09/2026
|
|
37
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
38
|
+
* (`WebSearch` esaurito, 200/200 — come per L1-L6: fonti primarie via `WebFetch`.)
|
|
39
|
+
*
|
|
40
|
+
* - **Anthropic, «How we built our multi-agent research system»**
|
|
41
|
+
* (<https://www.anthropic.com/engineering/multi-agent-research-system>): «agents typically
|
|
42
|
+
* use about 4× more tokens than chat interactions, and multi-agent systems use about **15×**
|
|
43
|
+
* more tokens than chats». ⇒ è il numero che giustifica il «costo detto prima» del piano: su
|
|
44
|
+
* una corsa che costa quindici volte una chat, scoprire la spesa DOPO non è un dettaglio di
|
|
45
|
+
* presentazione. Stessa pagina: «The LeadResearcher begins by thinking through the approach
|
|
46
|
+
* and **saving its plan to Memory to persist the context**» e «we built systems that can
|
|
47
|
+
* **resume from where the agent was** when the errors occurred» — le due cose che questo
|
|
48
|
+
* lotto aggiunge alla corsa, non inventate qui.
|
|
49
|
+
* - **Gao et al., «Enabling Large Language Models to Generate Text with Citations»**
|
|
50
|
+
* (arXiv:2305.14627, 24/05/2023, rev. 31/10/2023 — benchmark ALCE): «on the ELI5 dataset,
|
|
51
|
+
* even the best models **lack complete citation support 50% of the time**». ⇒ il passaggio
|
|
52
|
+
* che il modello dichiara non è una prova finché qualcuno non lo ritrova nel testo della
|
|
53
|
+
* pagina: è esattamente ciò per cui il testo va TENUTO, e perché tenerlo è un lotto e non un
|
|
54
|
+
* optional.
|
|
55
|
+
*
|
|
56
|
+
* ⛔ Il vincolo che la ricerca ha aggiunto e che non conoscevo: il costo di una corsa
|
|
57
|
+
* multi-agente non è dominato dalla generazione ma dalla RILETTURA del contesto. È la ragione
|
|
58
|
+
* per cui qui si conta la FINESTRA (ciò che arriva al modello) e non il testo conservato:
|
|
59
|
+
* conservare di più non costa un token, mostrare di più sì.
|
|
60
|
+
*
|
|
61
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
62
|
+
* L'attribuzione al ramo — una CONVENZIONE dichiarata, non una misura
|
|
63
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
64
|
+
*
|
|
65
|
+
* Il giornale vuole un `branchId` per ogni passo, e il modello non riceve gli id dei rami (sono
|
|
66
|
+
* un dato del server: `b1`, `b2`, …). L'attribuzione è quindi **per ordine di arrivo**: la
|
|
67
|
+
* prima ricerca distinta è il ramo 1 del piano, la seconda il ramo 2, e così via; oltre il
|
|
68
|
+
* piano si usano nomi `oltre-N` che NON esistono nel piano, così nessuno li scambi per rami
|
|
69
|
+
* approvati. Le letture di pagina ereditano il ramo della ricerca che le ha trovate.
|
|
70
|
+
*
|
|
71
|
+
* ⛔ Perché è onesta abbastanza: la consegna della figlia ADESSO elenca le linee d'indagine del
|
|
72
|
+
* piano, nell'ordine (`research-orchestrator.promptRicerca`), quindi l'ordine non è un
|
|
73
|
+
* sorteggio — è quello che le abbiamo chiesto di seguire. Ma resta una convenzione: ciò che è
|
|
74
|
+
* **esatto** è il conteggio (quante ricerche, quante pagine, quanti caratteri); ciò che è
|
|
75
|
+
* **approssimato** è a quale ramo appartengano, e serve a una cosa sola — dire a una ripresa
|
|
76
|
+
* quali linee sono ancora aperte. Dichiararlo qui costa una riga; scoprirlo dopo costa una
|
|
77
|
+
* diagnosi sbagliata.
|
|
78
|
+
* ⛔ E la chiave del passo è derivata dal CONTENUTO (la chiave di cache del descrittore), mai da
|
|
79
|
+
* un contatore: la stessa ricerca fatta due volte è LO STESSO passo, che è la regola che
|
|
80
|
+
* `run.mjs` scrive a lettere maiuscole («un nome nuovo è un passo che nessuno può riconoscere
|
|
81
|
+
* come già fatto — il modo in cui un giro ripreso paga due volte la stessa ricerca»).
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
import { createHash } from 'node:crypto';
|
|
85
|
+
|
|
86
|
+
import { talosResearchFetchKey } from './fetch-cache.mjs';
|
|
87
|
+
import { TALOS_RESEARCH_HEAD_SHARE, talosResearchPageBudget } from './page-budget.mjs';
|
|
88
|
+
import { talosResearchStepIdFor } from './run.mjs';
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @typedef {import('./run.mjs').TalosResearchBranch} TalosResearchBranch
|
|
92
|
+
* @typedef {import('./collector.mjs').TalosResearchSource} TalosResearchSource
|
|
93
|
+
* @typedef {import('./fetch-cache.mjs').TalosResearchFetchDescriptor} TalosResearchFetchDescriptor
|
|
94
|
+
*/
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Quanto testo di una pagina si CONSERVA sul desktop.
|
|
98
|
+
*
|
|
99
|
+
* ⛔ Diverso dal telefono di proposito, e il numero ha una ragione: sul mobile
|
|
100
|
+
* (`collector.mjs`) il tetto è 20.000 caratteri perché l'intero dossier vive in un database
|
|
101
|
+
* solo. Qui ogni fonte è un file suo in `fonti/<sha256>.txt`, e il collettore lo dice in chiaro
|
|
102
|
+
* («Sul desktop il dossier sta su disco e non in un database solo: chi lo sa alza questo, e il
|
|
103
|
+
* budget resta sotto»). 200.000 caratteri sono ~50 pagine di prosa: abbastanza per ritrovare un
|
|
104
|
+
* passaggio un anno dopo, abbastanza poco da non riempire un disco con una corsa.
|
|
105
|
+
* ⛔ E resta SOPRA la finestra mostrata al modello: ciò che si conserva non è ciò che si mostra,
|
|
106
|
+
* e invertirli renderebbe inutile il budget.
|
|
107
|
+
*/
|
|
108
|
+
export const TALOS_RESEARCH_CONSERVA_DESKTOP = 200_000;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* ⛔⛔⛔⭐⭐⭐ TROVATO DAL VIVO IL 12/09/2026 — IL BUDGET DA 15.000 NON PASSAVA DALLA PORTA.
|
|
112
|
+
*
|
|
113
|
+
* Il rapporto L6 ha misurato su **430 pagine vere** che 15.000 caratteri (testa 75%, coda 25%)
|
|
114
|
+
* sono il taglio giusto, e `TALOS_RESEARCH_PAGE_BUDGET` vale quello. Ma il risultato di un
|
|
115
|
+
* attrezzo, nel kernel, viene tagliato a **8.000 caratteri** prima di entrare nel messaggio
|
|
116
|
+
* `role:'tool'` (`talosHarness.mjs`: `String(esito).slice(0, 8_000)`, salvo `contextHooks`
|
|
117
|
+
* attivi). ⇒ una finestra da 15.191 caratteri arrivava al modello **senza il marcatore e senza
|
|
118
|
+
* la coda**: cioè esattamente le due cose per cui il budget esiste, tolte in silenzio da un
|
|
119
|
+
* tetto che sta in un altro file e che nessuno aveva confrontato col primo.
|
|
120
|
+
*
|
|
121
|
+
* ⛔ L'ho trovato perché un test lo ha fatto fallire (`CHIUSURA` non arrivava), non perché
|
|
122
|
+
* l'avessi previsto: due tetti su due file diversi, scritti in due lotti diversi, e il più
|
|
123
|
+
* stretto vince senza dirlo. È la firma del difetto che questo repo chiama «il cancello che
|
|
124
|
+
* non guardava tutto».
|
|
125
|
+
*
|
|
126
|
+
* ⛔ Perché NON si alza il tetto del kernel: quel `slice` vale per OGNI attrezzo di OGNI harness,
|
|
127
|
+
* TALOS-BANCO compreso — cambiarlo sposterebbe il profilo di token di ogni campagna per una
|
|
128
|
+
* ragione che riguarda una sola funzione. Si abbassa la finestra, che è la cosa locale.
|
|
129
|
+
*
|
|
130
|
+
* ⇒ 7.500, e il numero è ARITMETICA, non una scelta tonda. Il tetto conta **tutto** ciò che
|
|
131
|
+
* finisce nella stringa, e la finestra non è solo testa+coda:
|
|
132
|
+
* 7.500 i caratteri MOSTRATI (`cap`, cioè testa + coda: è ciò che `page-budget` garantisce)
|
|
133
|
+
* + ~190 il MARCATORE, che porta dentro il percorso della fonte (`fonti/<64 esadecimali>.txt`)
|
|
134
|
+
* + ~60 la riga di testa di `naviga` (`HTTP 200 · <url>\n`)
|
|
135
|
+
* ─────
|
|
136
|
+
* ~7.750 su 8.000 — con margine, invece che al pelo.
|
|
137
|
+
* ⛔ La prima versione di questa costante era **7.800** e continuava a perdere la coda: il
|
|
138
|
+
* marcatore non era nel conto. È lo stesso difetto una seconda volta, in miniatura — un
|
|
139
|
+
* tetto confrontato con un numero che non è quello vero — e l'ha trovato di nuovo il test,
|
|
140
|
+
* non il ragionamento.
|
|
141
|
+
* ⇒ Resta comunque quasi il DOPPIO dei 4.000 di oggi, e soprattutto porta la CODA, che il
|
|
142
|
+
* taglio di oggi non porta mai.
|
|
143
|
+
* ⛔ E quando `contextHooks` è attivo il tetto del kernel non si applica: allora questa finestra
|
|
144
|
+
* è più stretta del necessario. Dichiarato, non risolto — legare i due significherebbe far
|
|
145
|
+
* dipendere il budget da una configurazione che qui dentro non si vede.
|
|
146
|
+
*/
|
|
147
|
+
export const TALOS_RESEARCH_FINESTRA_SOTTO_IL_KERNEL = 7_500;
|
|
148
|
+
|
|
149
|
+
/** Quattro caratteri per token: la stessa regola pratica di `collector.mjs`, non una seconda. */
|
|
150
|
+
const CARATTERI_PER_TOKEN = 4;
|
|
151
|
+
|
|
152
|
+
/** @param {string} testo */
|
|
153
|
+
function normalizza(testo) {
|
|
154
|
+
return String(testo ?? '').replace(/\s+/g, ' ').trim();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @param {string} valore */
|
|
158
|
+
function impronta12(valore) {
|
|
159
|
+
return createHash('sha256').update(String(valore), 'utf8').digest('hex').slice(0, 12);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* ⛔ `null` invece di lanciare: una data che la fonte non dichiara è `null`, mai una
|
|
164
|
+
* supposizione — è la stessa regola di `formattaRisultatiRicerca` nel kernel («una data assente
|
|
165
|
+
* si DICHIARA, mai una supposizione»).
|
|
166
|
+
*
|
|
167
|
+
* @param {unknown} valore
|
|
168
|
+
* @returns {string | null}
|
|
169
|
+
*/
|
|
170
|
+
function dataDichiarata(valore) {
|
|
171
|
+
return typeof valore === 'string' && valore.trim().length > 0 ? valore.trim() : null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* La raccolta di UNA corsa.
|
|
176
|
+
*
|
|
177
|
+
* @param {object} opzioni
|
|
178
|
+
* @param {{around: (d: TalosResearchFetchDescriptor, p: () => Promise<any>) => Promise<{value:any, fromCache:boolean, key:string}>, snapshot?: () => any, restore?: (i:any) => number, stats?: () => any}} opzioni.cache
|
|
179
|
+
* La cache vera della corsa (`fetch-cache.mjs`). ⛔ Obbligatoria: senza, questa classe
|
|
180
|
+
* sarebbe una seconda implementazione della stessa idea.
|
|
181
|
+
* @param {(evento: object) => Promise<void> | void} opzioni.registra
|
|
182
|
+
* Una riga nel giornale. ⛔ Un guasto del giornale NON deve fermare la corsa: chi la passa
|
|
183
|
+
* inghiotte già l'errore (`research-orchestrator.registra`), e qui non si ricontrolla —
|
|
184
|
+
* due `catch` sullo stesso guasto sono due politiche che divergono.
|
|
185
|
+
* @param {(testo: string) => Promise<{ref: string, giaPresente: boolean}>} opzioni.tieniFonte
|
|
186
|
+
* Deposita il testo di una pagina e torna il suo `fonti/<sha256>.txt`.
|
|
187
|
+
* @param {(voce: {url: string, ref: string, titolo: string, dataDichiarata: string|null, ottenuta: 'page'|'snippet'}) => Promise<void> | void} [opzioni.annotaFonte]
|
|
188
|
+
* L'indice url → ref, che è ciò che permette a una VERIFICA dopo un riavvio di ritrovare il
|
|
189
|
+
* testo. Senza, la verifica funziona finché il processo vive e non un minuto di più.
|
|
190
|
+
* @param {() => readonly TalosResearchBranch[]} [opzioni.piano] I rami approvati, per l'attribuzione.
|
|
191
|
+
* @param {number} [opzioni.tettoFinestra] Caratteri mostrati al modello.
|
|
192
|
+
* @param {number} [opzioni.tettoConservato] Caratteri conservati su disco.
|
|
193
|
+
*/
|
|
194
|
+
export function creaRaccoltaViva({
|
|
195
|
+
cache,
|
|
196
|
+
registra,
|
|
197
|
+
tieniFonte,
|
|
198
|
+
annotaFonte = null,
|
|
199
|
+
piano = () => [],
|
|
200
|
+
tettoFinestra = TALOS_RESEARCH_FINESTRA_SOTTO_IL_KERNEL,
|
|
201
|
+
tettoConservato = TALOS_RESEARCH_CONSERVA_DESKTOP,
|
|
202
|
+
}) {
|
|
203
|
+
/** chiave di cache → id del passo. Il nome di un passo è derivato, mai contato. */
|
|
204
|
+
const passoPerChiave = new Map();
|
|
205
|
+
/** url (richiesto E finale) → la finestra già calcolata per quella pagina. */
|
|
206
|
+
const finestraPerUrl = new Map();
|
|
207
|
+
/** url → la fonte raccolta, col testo TENUTO. È ciò che la verifica legge. */
|
|
208
|
+
const fontiPerUrl = new Map();
|
|
209
|
+
/** Quante ricerche distinte sono già passate: decide il ramo della prossima. */
|
|
210
|
+
let ricercheDistinte = 0;
|
|
211
|
+
/** Il ramo dell'ultima ricerca: le pagine aperte dopo appartengono a lei. */
|
|
212
|
+
let ramoCorrente = null;
|
|
213
|
+
|
|
214
|
+
/** @param {TalosResearchFetchDescriptor} descrittore */
|
|
215
|
+
function nomeDelPasso(descrittore) {
|
|
216
|
+
const chiave = talosResearchFetchKey(descrittore);
|
|
217
|
+
const gia = passoPerChiave.get(chiave);
|
|
218
|
+
if (gia) return { ...gia, chiave, nuovo: false };
|
|
219
|
+
|
|
220
|
+
if (descrittore.kind === 'search') {
|
|
221
|
+
const rami = piano() ?? [];
|
|
222
|
+
const ramo = ricercheDistinte < rami.length
|
|
223
|
+
? rami[ricercheDistinte].id
|
|
224
|
+
// ⛔ `oltre-N` e non `b<N>`: un id che non è nel piano non deve poter essere scambiato
|
|
225
|
+
// per un ramo approvato da chi rilegge il giornale.
|
|
226
|
+
: `oltre-${ricercheDistinte + 1 - rami.length}`;
|
|
227
|
+
ricercheDistinte += 1;
|
|
228
|
+
ramoCorrente = ramo;
|
|
229
|
+
const voce = { stepId: talosResearchStepIdFor(ramo, 'search'), branchId: ramo, stepKind: /** @type {const} */ ('search') };
|
|
230
|
+
passoPerChiave.set(chiave, voce);
|
|
231
|
+
return { ...voce, chiave, nuovo: true };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const ramo = ramoCorrente ?? (piano()?.[0]?.id ?? 'oltre-1');
|
|
235
|
+
const voce = {
|
|
236
|
+
// ⛔ L'impronta dell'URL dentro il nome: due pagine dello stesso ramo sono due passi, e
|
|
237
|
+
// la stessa pagina riaperta è LO STESSO passo, qualunque cosa sia successa in mezzo.
|
|
238
|
+
stepId: `${ramo}:read:${impronta12(descrittore.url ?? '')}`,
|
|
239
|
+
branchId: ramo,
|
|
240
|
+
stepKind: /** @type {const} */ ('read'),
|
|
241
|
+
};
|
|
242
|
+
passoPerChiave.set(chiave, voce);
|
|
243
|
+
return { ...voce, chiave, nuovo: true };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* @param {string} testo
|
|
248
|
+
* @param {string} url
|
|
249
|
+
* @returns {{finestra: string, omessi: number}}
|
|
250
|
+
*/
|
|
251
|
+
function finestraDi(testo, url) {
|
|
252
|
+
const tagliata = talosResearchPageBudget(testo, {
|
|
253
|
+
cap: tettoFinestra,
|
|
254
|
+
headShare: TALOS_RESEARCH_HEAD_SHARE,
|
|
255
|
+
/*
|
|
256
|
+
* ⛔ Il riferimento è il PERCORSO VERO del testo tenuto, non una promessa: il marcatore
|
|
257
|
+
* dice al modello dove sta il resto, e `page-budget.mjs` tace quando non c'è un
|
|
258
|
+
* deposito («chi non ha un deposito non passa niente»). Qui c'è, perché la fonte è già
|
|
259
|
+
* stata scritta prima di calcolare la finestra.
|
|
260
|
+
*/
|
|
261
|
+
reference: fontiPerUrl.get(url)?.riferimento ?? null,
|
|
262
|
+
});
|
|
263
|
+
return { finestra: tagliata.window, omessi: tagliata.omitted };
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Tiene una pagina: il testo su disco, la voce nell'indice, la finestra in memoria.
|
|
268
|
+
*
|
|
269
|
+
* @param {string} url
|
|
270
|
+
* @param {string} corpo
|
|
271
|
+
* @param {{titolo?: string, pubblicato?: string|null, ottenuta?: 'page'|'snippet'}} [extra]
|
|
272
|
+
*/
|
|
273
|
+
async function tieni(url, corpo, extra = {}) {
|
|
274
|
+
const testo = normalizza(corpo).slice(0, tettoConservato);
|
|
275
|
+
if (testo.length === 0) return null;
|
|
276
|
+
const ottenuta = extra.ottenuta ?? 'page';
|
|
277
|
+
let ref = null;
|
|
278
|
+
try {
|
|
279
|
+
({ ref } = await tieniFonte(testo));
|
|
280
|
+
} catch {
|
|
281
|
+
/*
|
|
282
|
+
* ⛔ Una fonte che non si è potuta SCRIVERE non ferma la corsa, esattamente come il
|
|
283
|
+
* giornale: si perde la ri-verifica di quella pagina, non il lavoro già pagato. Ma la
|
|
284
|
+
* fonte resta in memoria, quindi la verifica di QUESTA corsa la vede lo stesso.
|
|
285
|
+
*/
|
|
286
|
+
ref = null;
|
|
287
|
+
}
|
|
288
|
+
const voce = {
|
|
289
|
+
url,
|
|
290
|
+
title: extra.titolo || url,
|
|
291
|
+
publishedAt: dataDichiarata(extra.pubblicato),
|
|
292
|
+
text: testo,
|
|
293
|
+
obtained: ottenuta,
|
|
294
|
+
ref,
|
|
295
|
+
riferimento: ref ? { percorso: ref } : null,
|
|
296
|
+
};
|
|
297
|
+
fontiPerUrl.set(url, voce);
|
|
298
|
+
if (annotaFonte && ref) {
|
|
299
|
+
try {
|
|
300
|
+
await annotaFonte({
|
|
301
|
+
url, ref, titolo: voce.title, dataDichiarata: voce.publishedAt, ottenuta,
|
|
302
|
+
});
|
|
303
|
+
} catch { /* come sopra: l'indice è un risparmio, non una condizione per lavorare. */ }
|
|
304
|
+
}
|
|
305
|
+
return voce;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
return Object.freeze({
|
|
309
|
+
/**
|
|
310
|
+
* LA PORTA. Stessa firma di `fetch-cache.around`, e il kernel non distingue le due.
|
|
311
|
+
*
|
|
312
|
+
* @template T
|
|
313
|
+
* @param {TalosResearchFetchDescriptor} descrittore
|
|
314
|
+
* @param {() => Promise<T>} produttore
|
|
315
|
+
* @returns {Promise<{value: T, fromCache: boolean, key: string}>}
|
|
316
|
+
*/
|
|
317
|
+
async around(descrittore, produttore) {
|
|
318
|
+
const passo = nomeDelPasso(descrittore);
|
|
319
|
+
await registra({ kind: 'step_started', stepId: passo.stepId, branchId: passo.branchId, stepKind: passo.stepKind });
|
|
320
|
+
|
|
321
|
+
let esito;
|
|
322
|
+
try {
|
|
323
|
+
esito = await cache.around(descrittore, produttore);
|
|
324
|
+
} catch (guasto) {
|
|
325
|
+
/*
|
|
326
|
+
* ⛔ `step_failed`, e poi si RILANCIA. Il giornale registra che quel passo ha provato e
|
|
327
|
+
* non ce l'ha fatta — che è diverso da «interrotto» (`run.mjs`: «il primo è un
|
|
328
|
+
* risultato, il secondo è una domanda») — ma l'errore appartiene al kernel, che sa
|
|
329
|
+
* dirlo al modello. Inghiottirlo qui trasformerebbe una pagina bloccata in una
|
|
330
|
+
* pagina vuota, cioè in un silenzio.
|
|
331
|
+
*/
|
|
332
|
+
await registra({
|
|
333
|
+
kind: 'step_failed', stepId: passo.stepId,
|
|
334
|
+
error: guasto instanceof Error ? guasto.message : String(guasto),
|
|
335
|
+
});
|
|
336
|
+
throw guasto;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (descrittore.kind === 'search') {
|
|
340
|
+
const righe = Array.isArray(esito.value) ? esito.value : [];
|
|
341
|
+
/*
|
|
342
|
+
* ⛔ Gli estratti dei risultati si TENGONO come fonti `snippet`, e non è zelo: se la
|
|
343
|
+
* figlia cita una pagina che non ha mai aperto, senza questo la verifica direbbe «la
|
|
344
|
+
* fonte citata non esiste fra quelle raccolte» — un motivo FALSO per un fatto vero
|
|
345
|
+
* («l'ha vista solo dall'elenco»). `resolved: 'snippet'` è la risposta giusta, e
|
|
346
|
+
* `report.mjs` la stampa come «solo estratto dal motore di ricerca».
|
|
347
|
+
* ⛔ Ma non si sovrascrive una pagina già APERTA con il suo estratto: una prova più
|
|
348
|
+
* debole non deve poter cancellare una più forte.
|
|
349
|
+
*/
|
|
350
|
+
for (const riga of righe) {
|
|
351
|
+
const url = typeof riga?.url === 'string' ? riga.url : '';
|
|
352
|
+
if (!url || fontiPerUrl.has(url)) continue;
|
|
353
|
+
await tieni(url, riga?.snippet ?? '', {
|
|
354
|
+
titolo: riga?.title, pubblicato: riga?.pubblicato ?? riga?.publishedAt, ottenuta: 'snippet',
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
const caratteri = righe.reduce(
|
|
358
|
+
(t, r) => t + String(r?.title ?? '').length + String(r?.url ?? '').length + String(r?.snippet ?? '').length,
|
|
359
|
+
0,
|
|
360
|
+
);
|
|
361
|
+
await registra({
|
|
362
|
+
kind: 'step_finished', stepId: passo.stepId,
|
|
363
|
+
spend: {
|
|
364
|
+
// ⛔ Servita dalla cache ⇒ NON è stata pagata. Contarla renderebbe invisibile il
|
|
365
|
+
// risparmio proprio nel numero fatto per mostrare il costo (`collector.mjs`).
|
|
366
|
+
searches: esito.fromCache ? 0 : 1,
|
|
367
|
+
pages: 0,
|
|
368
|
+
tokens: Math.ceil(caratteri / CARATTERI_PER_TOKEN),
|
|
369
|
+
},
|
|
370
|
+
resultRef: null,
|
|
371
|
+
});
|
|
372
|
+
return esito;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const pagina = esito.value ?? {};
|
|
376
|
+
const urlRichiesto = descrittore.url ?? '';
|
|
377
|
+
const urlFinale = typeof pagina?.url === 'string' && pagina.url ? pagina.url : urlRichiesto;
|
|
378
|
+
const tenuta = await tieni(urlFinale, pagina?.corpo ?? '');
|
|
379
|
+
const { finestra, omessi } = tenuta ? finestraDi(tenuta.text, urlFinale) : { finestra: '', omessi: 0 };
|
|
380
|
+
// Indicizzata su ENTRAMBI gli indirizzi: il modello ha chiesto il primo, la pagina ha
|
|
381
|
+
// risposto col secondo dopo i reindirizzamenti, e chi cerca la finestra può avere l'uno o
|
|
382
|
+
// l'altro in mano.
|
|
383
|
+
finestraPerUrl.set(urlFinale, finestra);
|
|
384
|
+
if (urlRichiesto) finestraPerUrl.set(urlRichiesto, finestra);
|
|
385
|
+
await registra({
|
|
386
|
+
kind: 'step_finished', stepId: passo.stepId,
|
|
387
|
+
spend: {
|
|
388
|
+
searches: 0,
|
|
389
|
+
pages: esito.fromCache ? 0 : 1,
|
|
390
|
+
// ⛔ Si contano i caratteri della FINESTRA, non del testo conservato: i token si
|
|
391
|
+
// pagano su ciò che arriva al modello. Identica scelta e identica riga in
|
|
392
|
+
// `collector.mjs`.
|
|
393
|
+
tokens: Math.ceil((finestra.length || 0) / CARATTERI_PER_TOKEN),
|
|
394
|
+
},
|
|
395
|
+
resultRef: tenuta?.ref ?? null,
|
|
396
|
+
});
|
|
397
|
+
if (omessi > 0) { /* il marcatore lo dice già dentro la finestra: niente evento in più. */ }
|
|
398
|
+
return esito;
|
|
399
|
+
},
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Che cosa vede il modello di questa pagina. `null` = «non lo so», e chi chiama ricade sul
|
|
403
|
+
* suo taglio di sempre invece di mostrare il vuoto.
|
|
404
|
+
*
|
|
405
|
+
* @param {string} url
|
|
406
|
+
* @param {string} corpo
|
|
407
|
+
* @returns {Promise<string | null>}
|
|
408
|
+
*/
|
|
409
|
+
async paginaLetta(url, corpo) {
|
|
410
|
+
const gia = finestraPerUrl.get(url);
|
|
411
|
+
if (typeof gia === 'string') return gia;
|
|
412
|
+
/*
|
|
413
|
+
* ⛔ Il ripiego esiste per un caso reale, non per simmetria: `naviga` può arrivare qui
|
|
414
|
+
* senza essere passato da `around` (una versione futura del kernel, un test che aggancia
|
|
415
|
+
* solo questo). Meglio una finestra calcolata al volo che nessuna.
|
|
416
|
+
*/
|
|
417
|
+
const tenuta = await tieni(url, corpo ?? '');
|
|
418
|
+
if (!tenuta) return null;
|
|
419
|
+
const { finestra } = finestraDi(tenuta.text, url);
|
|
420
|
+
finestraPerUrl.set(url, finestra);
|
|
421
|
+
return finestra;
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Le fonti raccolte, nell'ordine in cui sono arrivate. ⛔ Col `text` TENUTO: è ciò che la
|
|
426
|
+
* verifica confronta col passaggio citato, e senza il quale ogni affermazione resta «non
|
|
427
|
+
* verificata» per mancanza di prova invece che per mancanza di giudice.
|
|
428
|
+
*
|
|
429
|
+
* @returns {readonly (TalosResearchSource & {ref: string|null})[]}
|
|
430
|
+
*/
|
|
431
|
+
fonti() {
|
|
432
|
+
return [...fontiPerUrl.values()].map(({ riferimento, ...resto }) => resto);
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
/** url → testo tenuto. La mappa che la verifica usa per riempire `source.text`. */
|
|
436
|
+
testiPerUrl() {
|
|
437
|
+
const mappa = new Map();
|
|
438
|
+
for (const [url, voce] of fontiPerUrl) mappa.set(url, voce.text);
|
|
439
|
+
return mappa;
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
/** Quanti passi distinti sono stati nominati finora. Per i test e per le sonde, mai per il modello. */
|
|
443
|
+
passiNominati() {
|
|
444
|
+
return passoPerChiave.size;
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
/** La cache vera che sta sotto: chi deve salvarne l'istantanea la prende da qui. */
|
|
448
|
+
cacheSottostante() {
|
|
449
|
+
return cache;
|
|
450
|
+
},
|
|
451
|
+
});
|
|
452
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* PORTO FEDELE di AVM/mobile/src/lib/research/researchRecheckDocument.ts (57 righe, 11/09/2026).
|
|
3
|
+
*
|
|
4
|
+
* Il ricontrollo, scritto giù così non si deve pagare due volte.
|
|
5
|
+
*
|
|
6
|
+
* Stesso principio del rapporto: un controllo che non lascia traccia è un
|
|
7
|
+
* controllo che viene rifatto ogni volta che qualcuno se lo chiede e — peggio —
|
|
8
|
+
* non si può confrontare con quello di prima. Archiviato in Libreria accanto al
|
|
9
|
+
* dossier di cui parla.
|
|
10
|
+
*
|
|
11
|
+
* La frase più semplice del file è quella sulle fonti irraggiungibili: la
|
|
12
|
+
* pagina è sparita E il testo è ancora qui. È la frase che nessun concorrente
|
|
13
|
+
* può scrivere, e vale la pena spenderci una riga.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ E porta in coda un blocco che si RILEGGE esatto: la prosa qui sopra è
|
|
16
|
+
* per una persona, e ricavarne i numeri ripassando l'italiano stampato è il
|
|
17
|
+
* modo in cui una misura diventa un'invenzione. Senza, «quanto vale oggi un
|
|
18
|
+
* rapporto di ieri» resta una domanda a cui il file non sa rispondere.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { talosResearchRecheckStanding } from './recheck.mjs';
|
|
22
|
+
import { talosResearchRecheckBlock } from './recheck-history.mjs';
|
|
23
|
+
|
|
24
|
+
/** @typedef {import('./recheck.mjs').TalosResearchRecheck} TalosResearchRecheck */
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} question
|
|
28
|
+
* @param {TalosResearchRecheck} recheck
|
|
29
|
+
* @param {string} runId
|
|
30
|
+
* @returns {string}
|
|
31
|
+
*/
|
|
32
|
+
export function talosResearchRecheckDocument(question, recheck, runId) {
|
|
33
|
+
const standing = talosResearchRecheckStanding(recheck);
|
|
34
|
+
|
|
35
|
+
return [
|
|
36
|
+
`# Ricontrollo — ${question}`,
|
|
37
|
+
'',
|
|
38
|
+
`Data del ricontrollo: ${recheck.at}`,
|
|
39
|
+
'',
|
|
40
|
+
`Fonti ricontrollate: ${standing.total}`,
|
|
41
|
+
`- intatte: ${standing.intact}`,
|
|
42
|
+
`- cambiate dal giorno della ricerca: ${standing.changed}`,
|
|
43
|
+
`- non rispondono più: ${standing.unreachable}`,
|
|
44
|
+
standing.passagesLost > 0
|
|
45
|
+
? `\n**${standing.passagesLost} passaggi citati non sono più nella loro fonte.**`
|
|
46
|
+
: '\nTutti i passaggi citati sono ancora nelle loro fonti.',
|
|
47
|
+
standing.unreachable > 0
|
|
48
|
+
? '\nLe fonti che non rispondono più restano leggibili qui: il testo estratto\nè stato conservato il giorno della ricerca.'
|
|
49
|
+
: '',
|
|
50
|
+
'',
|
|
51
|
+
'## Fonte per fonte',
|
|
52
|
+
...recheck.sources.map((source) => {
|
|
53
|
+
const head = source.state === 'unreachable'
|
|
54
|
+
? `non risponde più${source.reason ? ` (${source.reason})` : ''}`
|
|
55
|
+
: source.state === 'intact'
|
|
56
|
+
? `intatta (${Math.round((source.survived ?? 0) * 100)}% del testo di allora è ancora lì)`
|
|
57
|
+
: `cambiata (${Math.round((source.survived ?? 0) * 100)}% del testo di allora è ancora lì)`;
|
|
58
|
+
const quotes = source.passagesLost > 0
|
|
59
|
+
? ` ${source.passagesLost} passaggi citati non ci sono più, ${source.passagesStanding} reggono ancora`
|
|
60
|
+
: source.passagesStanding > 0
|
|
61
|
+
? ` i ${source.passagesStanding} passaggi citati reggono ancora`
|
|
62
|
+
: '';
|
|
63
|
+
return [`- ${source.title} — ${source.url}`, ` ${head}`, quotes].filter(Boolean).join('\n');
|
|
64
|
+
}),
|
|
65
|
+
// Il `filter` qui sotto toglie le righe vuote: la riga bianca prima del
|
|
66
|
+
// blocco va dentro la stringa, se no prosa e recinto si toccano.
|
|
67
|
+
'\n' + talosResearchRecheckBlock(runId, recheck),
|
|
68
|
+
].filter((line) => line !== '').join('\n');
|
|
69
|
+
}
|