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,66 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { statfs } from 'node:fs/promises';
|
|
3
|
+
|
|
4
|
+
export const MACHINE_CAPACITY_SCHEMA = 'talos.model-lab.capacity/1';
|
|
5
|
+
export const MODEL_RESERVE_BYTES = 1024 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
export class MachineCapacityError extends Error {
|
|
8
|
+
constructor(message, code = 'MACHINE_CAPACITY_UNAVAILABLE') {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'MachineCapacityError';
|
|
11
|
+
this.code = code;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function numero(value) {
|
|
16
|
+
return typeof value === 'bigint' ? Number(value) : value;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function interoPositivo(value) {
|
|
20
|
+
const result = numero(value);
|
|
21
|
+
return Number.isFinite(result) && result >= 0 ? Math.floor(result) : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Measures only the host resources that are safe to expose to the desktop UI.
|
|
26
|
+
* No process, GPU probe, path listing or model execution is performed.
|
|
27
|
+
*/
|
|
28
|
+
export async function misuraCapacitaMacchina({
|
|
29
|
+
totalmemFn = os.totalmem,
|
|
30
|
+
freememFn = os.freemem,
|
|
31
|
+
statfsFn = statfs,
|
|
32
|
+
platformFn = os.platform,
|
|
33
|
+
archFn = os.arch,
|
|
34
|
+
storagePath = process.cwd(),
|
|
35
|
+
reserveBytes = MODEL_RESERVE_BYTES,
|
|
36
|
+
} = {}) {
|
|
37
|
+
let totalMemoryBytes;
|
|
38
|
+
let freeMemoryBytes;
|
|
39
|
+
let filesystem;
|
|
40
|
+
try {
|
|
41
|
+
totalMemoryBytes = interoPositivo(totalmemFn());
|
|
42
|
+
freeMemoryBytes = interoPositivo(freememFn());
|
|
43
|
+
filesystem = await statfsFn(storagePath, { bigint: true });
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new MachineCapacityError(`Misura capacità macchina non disponibile: ${error?.message || 'errore sconosciuto'}`);
|
|
46
|
+
}
|
|
47
|
+
const blockSize = interoPositivo(filesystem?.bsize);
|
|
48
|
+
const availableBlocks = interoPositivo(filesystem?.bavail);
|
|
49
|
+
const totalBlocks = interoPositivo(filesystem?.blocks);
|
|
50
|
+
const availableStorageBytes = blockSize !== null && availableBlocks !== null ? blockSize * availableBlocks : null;
|
|
51
|
+
const totalStorageBytes = blockSize !== null && totalBlocks !== null ? blockSize * totalBlocks : null;
|
|
52
|
+
const reserve = interoPositivo(reserveBytes) ?? MODEL_RESERVE_BYTES;
|
|
53
|
+
const allocatableStorageBytes = availableStorageBytes === null ? null : Math.max(0, availableStorageBytes - reserve);
|
|
54
|
+
if (totalMemoryBytes === null || freeMemoryBytes === null || availableStorageBytes === null || totalStorageBytes === null) {
|
|
55
|
+
throw new MachineCapacityError('Il sistema ha restituito una misura incompleta', 'MACHINE_CAPACITY_INVALID');
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
schema: MACHINE_CAPACITY_SCHEMA,
|
|
59
|
+
platform: String(platformFn()),
|
|
60
|
+
arch: String(archFn()),
|
|
61
|
+
measuredAt: new Date().toISOString(),
|
|
62
|
+
memory: { totalBytes: totalMemoryBytes, freeBytes: freeMemoryBytes },
|
|
63
|
+
storage: { totalBytes: totalStorageBytes, availableBytes: availableStorageBytes, reserveBytes: reserve, allocatableBytes: allocatableStorageBytes },
|
|
64
|
+
runtime: { status: 'unconfigured', reason: 'Runtime locale desktop non scelto' },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mappa-cartelle.mjs — BLOCCO 4 del preambolo: la forma del progetto, senza l'inventario dei file.
|
|
3
|
+
*
|
|
4
|
+
* ── PERCHÉ ESISTE, e perché NON è l'elenco dei file ──────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Misurato l'11/09/2026 con `elenco-profondo.mjs` + `gitignore-elenco.mjs` (stesso stimatore del
|
|
7
|
+
* prodotto, `costo-elenco.mjs` metodo «stimato», 3,5 byte/token):
|
|
8
|
+
* · `AVM/mobile` .... elenco dei file = 17.149 token, 1.500 percorsi, **TRONCATO** (l'albero ne
|
|
9
|
+
* ha 1.842: paghiamo 17k token per mostrarne il 30% e per dire al modello di non fidarsi);
|
|
10
|
+
* · `harness-ui/` ... elenco dei file = 7.267 token, 693 percorsi.
|
|
11
|
+
* La mappa delle sole cartelle degli stessi due alberi sta fra i 1.400 e i 1.700 token ed è
|
|
12
|
+
* **COMPLETA**: non mente mai, perché non c'è niente da troncare.
|
|
13
|
+
*
|
|
14
|
+
* ⛔ Nessuno dei quattro concorrenti letti nel codice l'11/09 (Codex, Claude Code 2.1.268, Hermes
|
|
15
|
+
* v0.21, DeepSeek harness) manda al modello un elenco di file: mandano `AGENTS.md`/`CLAUDE.md`
|
|
16
|
+
* e lasciano cercare. Loro possono: hanno ripgrep, glob, regex, paginazione e giri di fatto
|
|
17
|
+
* illimitati. Noi abbiamo `GIRI_MASSIMI = 24`, e il banco ha già misurato che **TALOS esaurisce
|
|
18
|
+
* i giri, non le capacità** ([[talos-esaurisce-i-giri-non-le-capacita]]: fallisce in 80 s
|
|
19
|
+
* mentre gli altri ne usano 332-630). La mappa delle cartelle è il nostro +1: toglie i giri di
|
|
20
|
+
* orientamento senza pagare l'inventario.
|
|
21
|
+
*
|
|
22
|
+
* RICERCA WEB, letta l'11/09/2026 PRIMA di scrivere (fonte + data + misura):
|
|
23
|
+
* 1. Anthropic, «Effective context engineering for AI agents», 29/09/2025
|
|
24
|
+
* <https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents> —
|
|
25
|
+
* verbatim: *«Folder hierarchies, naming conventions, and timestamps all provide important
|
|
26
|
+
* signals that help both humans and agents understand how and when to utilize information.»*
|
|
27
|
+
* La stessa pagina difende il «just in time» (identificatori leggeri + ricerca a runtime) MA
|
|
28
|
+
* ne dichiara il prezzo: *«there's a trade-off: runtime exploration is slower than retrieving
|
|
29
|
+
* pre-computed data»*, e indica come migliore una strategia IBRIDA — *«retrieving some data up
|
|
30
|
+
* front for speed, and pursuing further autonomous exploration at its discretion»*. Questo
|
|
31
|
+
* modulo è esattamente la metà «up front» di quell'ibrido, tenuta al minimo: la FORMA, non il
|
|
32
|
+
* contenuto.
|
|
33
|
+
* 2. Chroma, «Context Rot: How Increasing Input Tokens Impacts LLM Performance», 14/07/2025
|
|
34
|
+
* <https://www.trychroma.com/research/context-rot> — 18 modelli di frontiera: *«a single
|
|
35
|
+
* distractor reduces performance relative to the baseline»*, e il degrado cresce con
|
|
36
|
+
* l'input. ⇒ 17.000 token di elenco troncato non sono solo un costo: sono **distrattori**.
|
|
37
|
+
*
|
|
38
|
+
* ── LE CINQUE DECISIONI ──────────────────────────────────────────────────────────────────────
|
|
39
|
+
*
|
|
40
|
+
* (1) SOLO CARTELLE, e il conteggio dei file per cartella. `src/ (104)` dice al modello dove sta
|
|
41
|
+
* il codice senza nominare un file. Il conteggio costa ~6 caratteri e risponde da solo alla
|
|
42
|
+
* domanda «dove guardo per prima»; senza, la mappa è un elenco di nomi tutti uguali.
|
|
43
|
+
*
|
|
44
|
+
* (2) COMPLETA, e se un tetto morde si taglia IN PROFONDITÀ e LO SI DICHIARA. Un tetto che taglia
|
|
45
|
+
* «le prime N cartelle in ordine alfabetico» produce una mappa che sembra intera e non lo è —
|
|
46
|
+
* la bugia che `elenco-profondo.mjs` esiste per non dire, rifatta in forma nuova. Tagliare in
|
|
47
|
+
* profondità ha una proprietà che il taglio alfabetico non ha: ciò che resta è **vero e
|
|
48
|
+
* chiuso** («fin qui l'albero è tutto, sotto non l'ho guardato»), non «vero a metà».
|
|
49
|
+
*
|
|
50
|
+
* (3) ORDINE DETERMINISTICO, imposto durante la camminata. Identica ragione di
|
|
51
|
+
* `elenco-profondo.mjs` decisione (3), e ora con la fonte primaria: Claude Platform Docs,
|
|
52
|
+
* «Prompt caching» (letto 11/09/2026, <https://platform.claude.com/docs/en/docs/build-with-claude/prompt-caching>)
|
|
53
|
+
* — *«Cache hits require 100% identical prompt segments»* e la lookup lavora sul **prefisso
|
|
54
|
+
* intero** fino al breakpoint, non sul singolo blocco. Un byte diverso alla posizione N
|
|
55
|
+
* invalida tutto da lì in poi. ⛔ Mai `localeCompare`: dipende dai dati ICU compilati in Node.
|
|
56
|
+
*
|
|
57
|
+
* (4) IL `.gitignore` SI RISPETTA, con lo STESSO filtro del camminatore dei file — e il filtro si
|
|
58
|
+
* INIETTA già costruito. ⛔ Non si accetta una `creaFiltro(radice)` da chiamare qui dentro:
|
|
59
|
+
* è esattamente il contratto che il 10/09 ha prodotto 25.163 token invece di 6.518, perché
|
|
60
|
+
* `creaFiltroGitignore` vuole `{radice}` e riceveva la stringa nuda, Node lanciava
|
|
61
|
+
* `ERR_INVALID_ARG_TYPE` e un `catch` messo lì per il «.gitignore illeggibile» lo scambiava per
|
|
62
|
+
* quello. Qui il filtro arriva già pronto: non c'è nessun contratto da sbagliare.
|
|
63
|
+
*
|
|
64
|
+
* (5) UNA CARTELLA VUOTA RESTA NELLA MAPPA. Esiste sul disco; nasconderla perché non ha file
|
|
65
|
+
* dentro farebbe concludere al modello che non c'è. Costa una riga.
|
|
66
|
+
*/
|
|
67
|
+
import { readdir, realpath, stat } from 'node:fs/promises';
|
|
68
|
+
import { basename, join } from 'node:path';
|
|
69
|
+
/* ⛔ UN SOLO contatore in tutto il repo: e' la regola scritta in testa a `costo-elenco.mjs`.
|
|
70
|
+
Due stime diverse per la stessa cosa sono peggio di nessuna stima. */
|
|
71
|
+
import { costoDelTesto } from './costo-elenco.mjs';
|
|
72
|
+
|
|
73
|
+
export class MappaCartelleError extends Error {
|
|
74
|
+
constructor(message) {
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = 'MappaCartelleError';
|
|
77
|
+
this.code = 'MAPPA_INVALIDA';
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* ⛔ La stessa lista di `elenco-profondo.mjs`: due potature diverse per lo stesso albero
|
|
82
|
+
darebbero al modello una mappa che non combacia con ciò che l'elenco/la ricerca trovano. */
|
|
83
|
+
export { CARTELLE_ESCLUSE_PREDEFINITE } from './elenco-profondo.mjs';
|
|
84
|
+
import { CARTELLE_ESCLUSE_PREDEFINITE } from './elenco-profondo.mjs';
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Profondità massima della mappa.
|
|
88
|
+
*
|
|
89
|
+
* ⛔⛔⛔ BC-40, 12/09/2026 — ERA 8, ED ERA UN CONTO CHE NON TORNAVA. Owner, dopo la lentezza del
|
|
90
|
+
* primo messaggio: «ci deve essere un metodo migliore, magari mettere il preambolo come tool o
|
|
91
|
+
* qualcosa del genere». Il TEMPO era già stato curato il 12/09 (`TEMPO_MASSIMO_MAPPA_MS`); qui si
|
|
92
|
+
* curano i TOKEN e la FORMA. Misurato lo stesso giorno con lo stimatore del prodotto
|
|
93
|
+
* (`costo-elenco.mjs`, metodo «stimato»), filtro `.gitignore` vero, sui tre spazi reali:
|
|
94
|
+
*
|
|
95
|
+
* spazio OGGI (camminata 8) → resa NATIVA a 2 camminata token
|
|
96
|
+
* ────────────────── ───────────────────────────── ─────────── ─────────── ────────────
|
|
97
|
+
* harness-ui/ 55 cart., resa a prof 5, 374 29 cart., 229 22 → 5 ms −39%
|
|
98
|
+
* AVM-harness-desktop 2.000 cart. (TETTO), resa 4, 3.689 124, 825 507 → 19 ms −78%
|
|
99
|
+
* Desktop 2.000 cart. (TETTO), resa 2, 911 84, 841 434 → 12 ms −8%
|
|
100
|
+
*
|
|
101
|
+
* ⇒ Tre fatti, e nessuno è un'opinione:
|
|
102
|
+
* (a) sui due spazi grandi la camminata a 8 leggeva **2.000 cartelle** e il tetto di token ne
|
|
103
|
+
* rendeva 571 e 84: si pagava il disco per buttare via il risultato;
|
|
104
|
+
* (b) la profondità che il tetto di token SCEGLIEVA DA SOLA era già 2 su `Desktop` e 4 sul repo
|
|
105
|
+
* — cioè 2 non è una perdita inventata a tavolino, è dove il tetto arrivava comunque;
|
|
106
|
+
* (c) il costo scende di 25-36× in tempo di camminata e di 4,5× in token sul caso peggiore.
|
|
107
|
+
*
|
|
108
|
+
* ⛔ E la profondità 2 NON è una legge: `profonditaMax` resta un parametro. Il corpus `storia` ha
|
|
109
|
+
* i file a 4-6 ([[talos-non-vede-i-file-del-corpus-storia]]) e una mappa a 2 non li nomina —
|
|
110
|
+
* ma una mappa a 8 **nemmeno**, perché il tetto di token la tagliava a 4 prima di partire. La
|
|
111
|
+
* differenza non la fa la profondità: la fanno `elenca` (che da BC-40 scende in una cartella
|
|
112
|
+
* PRECISA) e `cerca`. Questa è la metà «up front» dell'ibrido che Anthropic raccomanda
|
|
113
|
+
* («Effective context engineering for AI agents», 29/09/2025: *«retrieving some data up front
|
|
114
|
+
* for speed, and pursuing further autonomous exploration at its discretion»*), tenuta al
|
|
115
|
+
* minimo — ed è la stessa forma della *progressive disclosure* delle Agent Skills (Anthropic,
|
|
116
|
+
* 16/10/2025: *«Like a well-organized manual that starts with a table of contents, then
|
|
117
|
+
* specific chapters, and finally a detailed appendix»*). La mappa è l'indice; `elenca` è il
|
|
118
|
+
* capitolo; `leggi` è la pagina.
|
|
119
|
+
*
|
|
120
|
+
* ⛔⛔ IL RISCHIO, dichiarato e non minimizzato: un modello che vede meno può concludere che una
|
|
121
|
+
* cartella NON ESISTA — è successo, con questa frase esatta: «0 — la cartella `harness-ui/src`
|
|
122
|
+
* non esiste» quando ne conteneva 104 ([[un-modello-che-non-vede-non-tace-spiega]]). ⇒ Per
|
|
123
|
+
* questo una mappa ridotta DEVE dirlo (`fermatoInProfondita` → `testoMappaCartelle`) e DEVE
|
|
124
|
+
* dire come scendere. Una mappa corta che si spaccia per intera sarebbe peggio di una lunga.
|
|
125
|
+
*/
|
|
126
|
+
export const PROFONDITA_MAPPA_PREDEFINITA = 2;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Tetto sulle CARTELLE, non sui file. 2.000: misurato l'11/09, `AVM-harness-desktop` intero ne ha
|
|
130
|
+
* 559 e `AVM/mobile` 215, cioè il tetto non morde su nessuno spazio reale che abbiamo. Esiste per
|
|
131
|
+
* la cartella patologica (la scrivania dell'owner, un disco intero), dove morde e lo DICHIARA.
|
|
132
|
+
*/
|
|
133
|
+
export const TETTO_CARTELLE_PREDEFINITO = 2000;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* ⛔⛔⛔ 12/09/2026 — IL TETTO CHE MANCAVA ERA IL TEMPO. Owner: «i modelli OpenRouter sono
|
|
137
|
+
* estremamente lenti al primo messaggio, quasi un minuto». Misurato sui record `tempi-giro`: lo
|
|
138
|
+
* stesso `glm-5.3-flash` risponde in 2,4 s sulla cartella `harness-ui`, in 4,2 s su
|
|
139
|
+
* `AVM-harness-desktop`, in **34-42 s** sulla cartella `Desktop` — a OGNI invio distanziato più
|
|
140
|
+
* di cinque minuti. E `contestoDelProgetto` da sola, su `Desktop`, costa **34.356 ms** per una
|
|
141
|
+
* mappa che poi il tetto di token riduce a 998 token: trentaquattro secondi NOSTRI, spesi a
|
|
142
|
+
* camminare `projects/` (decine di repo) prima ancora di chiamare il fornitore. Il tetto sulle
|
|
143
|
+
* cartelle (2.000) non mordeva in tempo: su Windows ogni `readdir` costa millisecondi, e duemila
|
|
144
|
+
* cartelle sono decine di secondi.
|
|
145
|
+
* ⇒ Un tetto in TEMPO, dichiarato nel testo come gli altri due: ciò che si è letto entro il
|
|
146
|
+
* budget è vero e completo per quelle cartelle (camminata in ampiezza: sopravvive ciò che sta
|
|
147
|
+
* vicino alla radice); il resto «non l'ho guardato», e il modello ha `cerca`/`elenca` per
|
|
148
|
+
* scendere dove serve — è la forma «just in time» che Anthropic raccomanda («maintain
|
|
149
|
+
* lightweight identifiers… load data at runtime», Effective context engineering for AI agents,
|
|
150
|
+
* 29/09/2025) e che Claude Code applica (il suo `/doctor` TOGLIE dal contesto «directory
|
|
151
|
+
* layouts» perché il modello li ricava con gli strumenti — docs «How Claude remembers your
|
|
152
|
+
* project», letta il 12/09/2026).
|
|
153
|
+
* ⛔ 2.000 ms: sopra i 258 ms di `harness-ui/`, sotto i 3.438 ms di `AVM-harness-desktop` (che
|
|
154
|
+
* viene quindi tagliato in tempo: è il repo intero con worktree e `scratchpad/`, il caso
|
|
155
|
+
* patologico dichiarato l'11/09), e un ordine di grandezza sotto il primo token del modello.
|
|
156
|
+
*/
|
|
157
|
+
export const TEMPO_MASSIMO_MAPPA_MS = 2000;
|
|
158
|
+
|
|
159
|
+
const insiemeMinuscolo = (valore, predefinito) => {
|
|
160
|
+
const grezzo = valore === undefined || valore === null ? predefinito : valore;
|
|
161
|
+
const voci = grezzo instanceof Set ? [...grezzo] : Array.isArray(grezzo) ? grezzo : [grezzo];
|
|
162
|
+
return new Set(voci.filter((v) => typeof v === 'string').map((v) => v.toLowerCase()));
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const ordinaVoci = (voci) => [...voci].sort((a, b) => {
|
|
166
|
+
const na = typeof a === 'string' ? a : a.name;
|
|
167
|
+
const nb = typeof b === 'string' ? b : b.name;
|
|
168
|
+
return na === nb ? 0 : na < nb ? -1 : 1;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Cammina l'albero e restituisce SOLO le cartelle, con quanti file contengono (i propri, non
|
|
173
|
+
* quelli dei figli) e a che livello stanno.
|
|
174
|
+
*
|
|
175
|
+
* @param {object} input
|
|
176
|
+
* @param {string} input.radice
|
|
177
|
+
* @param {number} [input.profonditaMax=8] — livello di una cartella: `src` è 1, `src/kernel` è 2
|
|
178
|
+
* @param {number} [input.tettoCartelle=2000]
|
|
179
|
+
* @param {string[]|Set<string>} [input.escludiCartelle]
|
|
180
|
+
* @param {object} [input.fs] — `{readdir, realpath, stat}`, iniettabile come negli altri moduli
|
|
181
|
+
* @param {(percorsoRelativo: string, info?: {cartella: boolean}) => boolean} [input.filtro]
|
|
182
|
+
* GIÀ COSTRUITO (vedi decisione 4). true = tieni.
|
|
183
|
+
* @param {number} [input.tempoMassimoMs=2000] — il tetto in TEMPO (12/09): oltre, si smette di
|
|
184
|
+
* accodare e lo si dichiara. `Infinity` per le prove che vogliono l'albero intero.
|
|
185
|
+
* @param {() => number} [input.orologio] — iniettabile per le prove (default `performance.now`)
|
|
186
|
+
* @returns {Promise<{cartelle: {percorso:string, livello:number, file:number}[], fileTotali:number,
|
|
187
|
+
* troncato:boolean, motivoTroncamento:'cartelle'|'tempo'|null, msImpiegati:number,
|
|
188
|
+
* profonditaRaggiunta:number, illeggibili:number, fermatoInProfondita:boolean}>}
|
|
189
|
+
*
|
|
190
|
+
* ⛔⛔ BC-40 — `fermatoInProfondita` è il campo che rende ONESTA la profondità ridotta, ed è nato
|
|
191
|
+
* da un difetto che la cura stava per introdurre: con `profonditaMax` a 2 il camminatore finisce
|
|
192
|
+
* senza `troncato`, e `testoMappaCartelle` scriveva **«albero COMPLETO»** su un albero che
|
|
193
|
+
* continuava sotto. Cioè la riduzione avrebbe prodotto esattamente la bugia che questo modulo
|
|
194
|
+
* esiste per non dire (decisione 2). Qui invece si REGISTRA che c'erano figli e non sono stati
|
|
195
|
+
* guardati — `troncato` resta quello che è sempre stato (mi sono FERMATO), questo è un'altra
|
|
196
|
+
* cosa (ho SCELTO di non scendere), e il testo le dice separate.
|
|
197
|
+
*/
|
|
198
|
+
export async function costruisciMappaCartelle({
|
|
199
|
+
radice,
|
|
200
|
+
profonditaMax = PROFONDITA_MAPPA_PREDEFINITA,
|
|
201
|
+
tettoCartelle = TETTO_CARTELLE_PREDEFINITO,
|
|
202
|
+
tempoMassimoMs = TEMPO_MASSIMO_MAPPA_MS,
|
|
203
|
+
orologio = () => performance.now(),
|
|
204
|
+
escludiCartelle,
|
|
205
|
+
fs,
|
|
206
|
+
filtro,
|
|
207
|
+
} = {}) {
|
|
208
|
+
if (typeof radice !== 'string' || radice.length === 0 || radice.includes('\0')) {
|
|
209
|
+
throw new MappaCartelleError('La cartella di partenza non è valida');
|
|
210
|
+
}
|
|
211
|
+
if (filtro !== undefined && typeof filtro !== 'function') {
|
|
212
|
+
throw new MappaCartelleError('Il filtro deve essere una funzione');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const disco = { readdir, realpath, stat, ...(fs ?? {}) };
|
|
216
|
+
const escluse = insiemeMinuscolo(escludiCartelle, CARTELLE_ESCLUSE_PREDEFINITE);
|
|
217
|
+
const profondita = Number.isFinite(profonditaMax) ? Math.trunc(profonditaMax) : PROFONDITA_MAPPA_PREDEFINITA;
|
|
218
|
+
const tetto = Number.isFinite(tettoCartelle) ? Math.max(0, Math.trunc(tettoCartelle)) : TETTO_CARTELLE_PREDEFINITO;
|
|
219
|
+
const tieni = (percorso, cartella) => (filtro ? filtro(percorso, { cartella }) !== false : true);
|
|
220
|
+
|
|
221
|
+
const cartelle = [];
|
|
222
|
+
let radiceFile = 0;
|
|
223
|
+
let fileTotali = 0;
|
|
224
|
+
let troncato = false;
|
|
225
|
+
let motivoTroncamento = null;
|
|
226
|
+
let profonditaRaggiunta = 0;
|
|
227
|
+
let illeggibili = 0;
|
|
228
|
+
/* BC-40: true appena una cartella TENUTA resta fuori perche' `profonditaMax` non la copre. */
|
|
229
|
+
let fermatoInProfondita = false;
|
|
230
|
+
const budgetMs = Number.isFinite(tempoMassimoMs) ? Math.max(0, tempoMassimoMs) : Infinity;
|
|
231
|
+
const partenza = orologio();
|
|
232
|
+
|
|
233
|
+
const radiceReale = await disco.realpath(radice).catch(() => radice);
|
|
234
|
+
/* Ampiezza, come `elenco-profondo.mjs`: se il tetto morde, ciò che sopravvive è la roba vicina
|
|
235
|
+
alla radice — `src/`, `tests/` — non il primo sottoalbero seguito fino in fondo. */
|
|
236
|
+
const coda = [{ assoluto: radice, relativo: '', livello: 0, antenati: [radiceReale] }];
|
|
237
|
+
|
|
238
|
+
while (coda.length > 0) {
|
|
239
|
+
/* ⛔ Il tetto in tempo si guarda PRIMA di leggere la prossima cartella, non dopo: una lettura
|
|
240
|
+
già iniziata si finisce (i suoi file vanno contati e la sua riga scritta), quella dopo no.
|
|
241
|
+
Stessa disciplina del tetto sulle cartelle: si smette di camminare, non si butta il fatto. */
|
|
242
|
+
if (orologio() - partenza > budgetMs) { troncato = true; motivoTroncamento = 'tempo'; break; }
|
|
243
|
+
const corrente = coda.shift();
|
|
244
|
+
let voci;
|
|
245
|
+
try {
|
|
246
|
+
voci = await disco.readdir(corrente.assoluto, { withFileTypes: true });
|
|
247
|
+
} catch {
|
|
248
|
+
/* Una cartella illeggibile si salta e si CONTA: tacere dichiarerebbe completa una mappa
|
|
249
|
+
che non lo è — lo stesso principio di `elenco-profondo.mjs`. */
|
|
250
|
+
illeggibili += 1;
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let fileQui = 0;
|
|
255
|
+
const figli = [];
|
|
256
|
+
let contate = 0;
|
|
257
|
+
for (const voce of ordinaVoci(voci)) {
|
|
258
|
+
/* ⛔ 12/09: una cartella sola puo' avere migliaia di voci (la radice di `Desktop`), e ognuna
|
|
259
|
+
passa dal filtro: il tetto in tempo si guarda anche QUI, ogni 256 voci, o una cartella
|
|
260
|
+
enorme lo scavalca da sola. Le voci gia' contate restano; il testo dichiara il taglio. */
|
|
261
|
+
if ((contate++ & 255) === 255 && orologio() - partenza > budgetMs) { troncato = true; motivoTroncamento = 'tempo'; break; }
|
|
262
|
+
const nome = typeof voce === 'string' ? voce : voce.name;
|
|
263
|
+
const relativo = corrente.relativo ? `${corrente.relativo}/${nome}` : nome;
|
|
264
|
+
const assoluto = join(corrente.assoluto, nome);
|
|
265
|
+
|
|
266
|
+
let eCartella = Boolean(voce.isDirectory?.());
|
|
267
|
+
let eFile = Boolean(voce.isFile?.());
|
|
268
|
+
if (voce.isSymbolicLink?.()) {
|
|
269
|
+
const informazioni = await disco.stat(assoluto).catch(() => null);
|
|
270
|
+
if (!informazioni) { illeggibili += 1; continue; } // link rotto: onesto, non fatale
|
|
271
|
+
eCartella = Boolean(informazioni.isDirectory?.());
|
|
272
|
+
eFile = Boolean(informazioni.isFile?.());
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (eCartella) {
|
|
276
|
+
if (escluse.has(nome.toLowerCase())) continue;
|
|
277
|
+
if (!tieni(relativo, true)) continue;
|
|
278
|
+
figli.push({ nome, relativo, assoluto, collegamento: Boolean(voce.isSymbolicLink?.()) });
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
/* ⛔ Il conteggio passa dallo STESSO filtro dei file: se non lo facesse, `src/ (104)`
|
|
282
|
+
conterebbe anche ciò che `git` ignora e il numero non combacerebbe con quello che il
|
|
283
|
+
modello trova cercando. Un conteggio che non combacia è peggio di nessun conteggio. */
|
|
284
|
+
if (!eFile) continue;
|
|
285
|
+
if (!tieni(relativo, false)) continue;
|
|
286
|
+
fileQui += 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
fileTotali += fileQui;
|
|
290
|
+
if (corrente.relativo !== '') {
|
|
291
|
+
cartelle.push({ percorso: corrente.relativo, livello: corrente.livello, file: fileQui });
|
|
292
|
+
if (corrente.livello > profonditaRaggiunta) profonditaRaggiunta = corrente.livello;
|
|
293
|
+
} else {
|
|
294
|
+
/* La radice non è una voce della mappa (si chiama come lo spazio di lavoro, già detto
|
|
295
|
+
nell'intestazione), ma i suoi file contano nel totale e si dichiarano a parte. */
|
|
296
|
+
radiceFile = fileQui;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (corrente.livello + 1 > profondita) {
|
|
300
|
+
/* ⛔ BC-40 — NON un `continue` muto. Se questa cartella ha figli TENUTI (gia' passati dalle
|
|
301
|
+
esclusioni e dal filtro `.gitignore`) e la profondita' chiesta non li copre, l'albero
|
|
302
|
+
continua sotto e la mappa NON e' completa: si segna, e il testo lo dichiara. Senza questa
|
|
303
|
+
riga una mappa a profondita' 2 direbbe «albero COMPLETO» di un albero profondo sei. */
|
|
304
|
+
if (figli.length > 0) fermatoInProfondita = true;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
for (const figlio of figli) {
|
|
308
|
+
/*
|
|
309
|
+
* ⛔⛔ IL TETTO SMETTE DI ACCODARE, NON DI CAMMINARE — trovato da una prova al verso
|
|
310
|
+
* contrario l'11/09. La prima versione faceva `break` anche sul ciclo esterno: con un
|
|
311
|
+
* tetto raggiunto alla RADICE usciva una mappa **vuota**, che `testoMappaCartelle` poi
|
|
312
|
+
* dichiarava «nessuna sottocartella». Cioè, per colpa della guardia contro l'eccesso, la
|
|
313
|
+
* bugia esatta che questo modulo esiste per non dire — la stessa forma dell'errore che
|
|
314
|
+
* `elenco-profondo.mjs` racconta nella sua nota sugli anelli simbolici.
|
|
315
|
+
* ⇒ Chi è già in coda si finisce di visitare (i suoi file vanno contati, e la sua riga
|
|
316
|
+
* scritta); ciò che non entra non entra, e il testo lo DICHIARA.
|
|
317
|
+
*/
|
|
318
|
+
if (cartelle.length + coda.length >= tetto) { troncato = true; motivoTroncamento = 'cartelle'; break; }
|
|
319
|
+
/* ⛔ 12/09: `realpath` era una chiamata di sistema PER OGNI cartella, e serve solo a chi può
|
|
320
|
+
girare in tondo — un collegamento simbolico o una giunzione. Una cartella vera ha come
|
|
321
|
+
percorso reale il suo percorso: si prende senza chiedere al disco. Su `Desktop` erano
|
|
322
|
+
migliaia di chiamate in più per non scoprire niente. */
|
|
323
|
+
const reale = figlio.collegamento ? await disco.realpath(figlio.assoluto).catch(() => figlio.assoluto) : figlio.assoluto;
|
|
324
|
+
if (corrente.antenati.includes(reale)) continue; // ciclo: si torna su un ANTENATO del cammino
|
|
325
|
+
coda.push({ assoluto: figlio.assoluto, relativo: figlio.relativo, livello: corrente.livello + 1, antenati: [...corrente.antenati, reale] });
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/* ⛔ La camminata è in AMPIEZZA (decisione 3: se il tetto morde sopravvive la roba vicina alla
|
|
330
|
+
radice), ma il TESTO è un albero indentato: in ordine di ampiezza i figli non seguirebbero il
|
|
331
|
+
loro genitore e l'indentazione mentirebbe. Si riordina qui, in profondità e per nome, con lo
|
|
332
|
+
stesso confronto per unità di codice di `elenco-profondo.mjs` — mai `localeCompare`. */
|
|
333
|
+
cartelle.sort(confrontaCartelle);
|
|
334
|
+
|
|
335
|
+
return { cartelle, radiceFile, fileTotali, troncato, motivoTroncamento, msImpiegati: Math.round(orologio() - partenza), profonditaRaggiunta, illeggibili, fermatoInProfondita };
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Ordine totale, stabile e indipendente dall'ambiente: segmento per segmento, e un genitore
|
|
340
|
+
* viene sempre prima dei propri figli. ⛔ Mai `localeCompare` (dipende dai dati ICU compilati in
|
|
341
|
+
* Node: un ordine che cambia da una macchina all'altra rompe il prefisso della cache).
|
|
342
|
+
*/
|
|
343
|
+
export function confrontaCartelle(a, b) {
|
|
344
|
+
const sa = String(a?.percorso ?? a).split('/');
|
|
345
|
+
const sb = String(b?.percorso ?? b).split('/');
|
|
346
|
+
const comuni = Math.min(sa.length, sb.length);
|
|
347
|
+
for (let i = 0; i < comuni; i += 1) {
|
|
348
|
+
if (sa[i] === sb[i]) continue;
|
|
349
|
+
return sa[i] < sb[i] ? -1 : 1;
|
|
350
|
+
}
|
|
351
|
+
return sa.length - sb.length;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Il testo del blocco 4.
|
|
356
|
+
*
|
|
357
|
+
* FORMA: nomi INDENTATI, non percorsi interi. Misurato l'11/09 sui due spazi veri: il percorso
|
|
358
|
+
* intero ripete il prefisso del genitore su ogni riga e costa il 35-45% in più della sola
|
|
359
|
+
* indentazione a due spazi, per la stessa informazione. Un `/` finale distingue a colpo d'occhio
|
|
360
|
+
* una cartella da un file, e il conteggio fra parentesi dice dove sta il lavoro.
|
|
361
|
+
*
|
|
362
|
+
* ⛔ Nell'intestazione va il NOME della cartella, MAI il percorso assoluto: contiene il nome della
|
|
363
|
+
* persona e questo testo esce dalla macchina dentro un prompt. È la lezione
|
|
364
|
+
* [[cancello-4-non-guardava-tutto-mobile]] — un percorso personale pubblicato in chiaro perché
|
|
365
|
+
* nessuno aveva guardato dove finiva. Stessa regola di `testoElenco()`.
|
|
366
|
+
*/
|
|
367
|
+
export function testoMappaCartelle(mappa, { radice = '' } = {}) {
|
|
368
|
+
const nome = basename(String(radice ?? '').replace(/[\\/]+$/, '')) || 'la cartella di lavoro';
|
|
369
|
+
const voci = Array.isArray(mappa?.cartelle) ? mappa.cartelle : [];
|
|
370
|
+
|
|
371
|
+
if (voci.length === 0) {
|
|
372
|
+
/* ⛔ «nessuna sottocartella» è un fatto VERO e utile (un progetto piatto esiste); «nessun
|
|
373
|
+
file» invece si dice solo se davvero non ce ne sono, e in quel caso chi compone il preambolo
|
|
374
|
+
decide di non mandare niente — vedi `contesto-del-progetto.mjs`. */
|
|
375
|
+
return `Struttura di «${nome}»: nessuna sottocartella, ${mappa?.radiceFile ?? 0} file nella cartella principale.`;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/* ⛔⛔ BC-40 — DUE modi di NON essere completa, e non sono la stessa cosa:
|
|
379
|
+
· `troncato` .............. mi sono FERMATO (tetto sulle cartelle, o tetto in tempo): non
|
|
380
|
+
so nemmeno io che cosa mi sia rimasto fuori;
|
|
381
|
+
· `fermatoInProfondita` ... ho SCELTO di non scendere sotto la profondità dichiarata: so
|
|
382
|
+
per certo che sotto c'è altro, e so esattamente dove.
|
|
383
|
+
Un booleano solo li confonderebbe, e la seconda è l'unica che il modello può RIPARARE da sé,
|
|
384
|
+
con `elenca`. ⛔ «albero COMPLETO» si dice solo quando nessuna delle due è vera. */
|
|
385
|
+
const ridotta = Boolean(mappa?.fermatoInProfondita);
|
|
386
|
+
const righe = [];
|
|
387
|
+
/*
|
|
388
|
+
* ⛔⛔⛔ BC-40 — LA DICHIARAZIONE STA NELL'INTESTAZIONE, NON IN UN BLOCCO A PARTE, e non è
|
|
389
|
+
* una scelta di stile: MISURATA. La prima stesura metteva un paragrafo di avviso in testa
|
|
390
|
+
* («ti do i primi N livelli… non concludere mai che…»): **167 token**, cioè PIÙ dei 145 che
|
|
391
|
+
* la riduzione da profondità 5 a 2 risparmiava su `harness-ui/`. Il preambolo passava da
|
|
392
|
+
* 3.963 a 3.985 token: la cura peggiorava il caso normale per curare quello patologico,
|
|
393
|
+
* che è esattamente il difetto che l'11/09 aveva fatto nascere il tetto in token.
|
|
394
|
+
* ⇒ La dichiarazione costa mezza riga qui, e il promemoria VERO — «non dire che non esiste» —
|
|
395
|
+
* sta in fondo, dopo l'elenco, che è dove il modello guarda per ultimo prima di rispondere
|
|
396
|
+
* ([[il-promemoria-dove-guarda-per-ultimo]]: non riscrivere la regola, SPOSTARLA).
|
|
397
|
+
*/
|
|
398
|
+
const coda = ridotta
|
|
399
|
+
? `, primi ${mappa.profonditaRaggiunta} livelli soltanto (⚠ MAPPA INCOMPLETA PER SCELTA: fin qui è tutto vero, sotto ci sono altre cartelle che non ho elencato)`
|
|
400
|
+
: mappa.troncato ? '' : ', albero COMPLETO';
|
|
401
|
+
righe.push(`Struttura di «${nome}» — ${voci.length} cartelle${coda}. Fra parentesi quanti file contiene ognuna (i propri, non quelli delle sottocartelle).`);
|
|
402
|
+
righe.push('I singoli file non sono elencati: usa `cerca` per trovarli per nome o per contenuto, e `elenca` con `percorso` per aprire una cartella precisa (es. `elenca {"percorso":"src"}`).');
|
|
403
|
+
if (mappa.troncato && mappa.motivoTroncamento === 'tempo') {
|
|
404
|
+
righe.push(`⚠ MAPPA INCOMPLETA — questa cartella è grande e leggerla tutta avrebbe fatto aspettare: mi sono fermato dopo ${voci.length} cartelle (profondità ${mappa.profonditaRaggiunta}). Quelle elencate sono vere; le altre non le ho guardate: cercale con \`cerca\` o \`elenca\` prima di dire che mancano.`);
|
|
405
|
+
} else if (mappa.troncato) {
|
|
406
|
+
righe.push(`⚠ MAPPA INCOMPLETA — mi sono fermato a ${voci.length} cartelle (profondità ${mappa.profonditaRaggiunta}): più in basso l'albero continua e non l'ho guardato.`);
|
|
407
|
+
}
|
|
408
|
+
if (mappa.illeggibili > 0) {
|
|
409
|
+
righe.push(`(${mappa.illeggibili} cartelle non si sono lasciate leggere: quello che contengono non compare qui.)`);
|
|
410
|
+
}
|
|
411
|
+
righe.push('');
|
|
412
|
+
righe.push(`${nome}/ (${mappa.radiceFile ?? 0})`);
|
|
413
|
+
for (const voce of voci) {
|
|
414
|
+
const foglia = voce.percorso.slice(voce.percorso.lastIndexOf('/') + 1);
|
|
415
|
+
righe.push(`${' '.repeat(voce.livello)}${foglia}/ (${voce.file})`);
|
|
416
|
+
}
|
|
417
|
+
if (mappa.troncato || ridotta) {
|
|
418
|
+
righe.push('');
|
|
419
|
+
righe.push('⚠ Fine di una mappa INCOMPLETA. ⛔ Se una cartella non compare qui sopra NON vuol dire che non esista: aprila con `elenca {"percorso":"…"}` o cercala con `cerca` prima di dire che manca.');
|
|
420
|
+
}
|
|
421
|
+
return righe.join('\n');
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Il TETTO DELLA MAPPA, in TOKEN — perche' un tetto sulle cartelle non e' un tetto sul costo.
|
|
426
|
+
*
|
|
427
|
+
* ⛔ MISURATO L'11/09/2026, ed e' il numero che ha fatto nascere questa funzione: col solo tetto
|
|
428
|
+
* sulle cartelle (2.000), lo spazio di lavoro «repo intero» (`AVM-harness-desktop`, 12.715 file
|
|
429
|
+
* e oltre 2.000 cartelle, worktree e `scratchpad/prove` compresi) produceva una mappa da
|
|
430
|
+
* **17.966 token** — cioe' il preambolo nuovo costava **+8,4% del vecchio** invece di meno.
|
|
431
|
+
* Una cura che peggiora il caso peggiore non e' una cura.
|
|
432
|
+
* ⇒ 4.000 token: sotto, tutti gli spazi di lavoro veri misurati quel giorno (`harness-ui/` 358,
|
|
433
|
+
* `AVM/mobile` 3.937); sopra, solo gli alberi patologici, che e' esattamente dove deve mordere.
|
|
434
|
+
*
|
|
435
|
+
* ⛔ E si taglia IN PROFONDITA', non in ordine alfabetico. La differenza e' fra «fin qui l'albero
|
|
436
|
+
* e' TUTTO, sotto non l'ho guardato» — una verita' chiusa, su cui il modello puo' ragionare — e
|
|
437
|
+
* «ne ho mostrate le prime N», che sembra intero e non lo e'. E si DICHIARA.
|
|
438
|
+
*/
|
|
439
|
+
/*
|
|
440
|
+
* ⛔⛔ BC-40, 12/09/2026 — ERA 4.000, e con la profondità predefinita a 8 era il numero giusto:
|
|
441
|
+
* serviva a non far esplodere l'albero patologico. Con la profondità predefinita a 2 quel tetto
|
|
442
|
+
* non morde più su NIENTE — misurato il 12/09 sui tre spazi veri, camminata nativa a 2:
|
|
443
|
+
* `harness-ui/` 229 token, `AVM-harness-desktop` 825, `Desktop` 841. Un tetto che non morde mai
|
|
444
|
+
* non è una guardia: è una riga morta, ed è il difetto che il 27/8 ha lasciato il cancello
|
|
445
|
+
* semantico inerte per mesi ([[il-cancello-semantico-era-spento-da-sempre]]).
|
|
446
|
+
* ⇒ 1.200: sopra tutti e tre (il più grosso misurato è 841, quindi c'è il 43% di margine per una
|
|
447
|
+
* cartella più larga di qualunque cosa abbiamo), e sotto l'albero patologico — dove morde e
|
|
448
|
+
* scende a profondità 1, dichiarandolo.
|
|
449
|
+
* ⛔ SCARTATO 800 (la proposta di partenza), e si scrive perché: a 800 token il tetto taglia a
|
|
450
|
+
* profondità 1 sia `AVM-harness-desktop` (825) sia `Desktop` (841), e a profondità 1 sul repo
|
|
451
|
+
* **`harness-ui/src` non compare** — cioè proprio la cartella su cui il modello aveva detto
|
|
452
|
+
* «non esiste». Risparmiare 640 token riaprendo quel buco non è un affare.
|
|
453
|
+
* ⛔ E il confronto esterno dice lo stesso ordine di grandezza: aider, «Repository map» (letto
|
|
454
|
+
* 12/09/2026, <https://aider.chat/docs/repomap.html>), *«The token budget is influenced by the
|
|
455
|
+
* --map-tokens switch, which defaults to 1k tokens»*.
|
|
456
|
+
*/
|
|
457
|
+
export const TETTO_TOKEN_MAPPA_PREDEFINITO = 1200;
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Rende la mappa dentro un tetto di token, togliendo un livello di profondita' alla volta.
|
|
461
|
+
*
|
|
462
|
+
* ⭐ NON ricammina l'albero: filtra le cartelle gia' raccolte. Sul repo intero la camminata costa
|
|
463
|
+
* 3,5 s, e rifarla quattro volte per trovare la profondita' giusta sarebbe 14 s prima del primo
|
|
464
|
+
* token — il contrario di cio' che BC-07 sta cercando di ottenere.
|
|
465
|
+
*
|
|
466
|
+
* @returns {{testo:string, profonditaUsata:number, tagliataInProfondita:boolean, token:number}}
|
|
467
|
+
*/
|
|
468
|
+
export function mappaEntroIlTetto(mappa, { radice = '', tettoToken = TETTO_TOKEN_MAPPA_PREDEFINITO, stima } = {}) {
|
|
469
|
+
const conta = typeof stima === 'function' ? stima : (t) => costoDelTesto(t, { metodo: 'stimato' }).token;
|
|
470
|
+
const profonditaPiena = mappa?.profonditaRaggiunta ?? 0;
|
|
471
|
+
|
|
472
|
+
for (let profondita = profonditaPiena; profondita >= 1; profondita -= 1) {
|
|
473
|
+
const tagliata = profondita < profonditaPiena;
|
|
474
|
+
const voci = tagliata ? mappa.cartelle.filter((c) => c.livello <= profondita) : mappa.cartelle;
|
|
475
|
+
/* ⛔ BC-40 — un taglio in profondita' deciso dal TETTO e' la stessa cosa, per chi legge, di
|
|
476
|
+
una profondita' scelta in partenza: sotto c'e' altro e non l'ho guardato. Va nello stesso
|
|
477
|
+
campo (`fermatoInProfondita`), non in `troncato`: `troncato` vuol dire «mi sono fermato e
|
|
478
|
+
non so cosa mi manca», e qui invece si sa esattamente. */
|
|
479
|
+
const testo = testoMappaCartelle(
|
|
480
|
+
{ ...mappa, cartelle: voci, troncato: Boolean(mappa.troncato), fermatoInProfondita: Boolean(mappa.fermatoInProfondita) || tagliata, profonditaRaggiunta: profondita },
|
|
481
|
+
{ radice },
|
|
482
|
+
);
|
|
483
|
+
const token = conta(testo);
|
|
484
|
+
if (token <= tettoToken || profondita === 1) {
|
|
485
|
+
return { testo, profonditaUsata: profondita, tagliataInProfondita: tagliata, token };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/* Nessuna cartella affatto: la mappa e' una riga sola, e non c'e' niente da tagliare. */
|
|
489
|
+
const testo = testoMappaCartelle(mappa, { radice });
|
|
490
|
+
return { testo, profonditaUsata: 0, tagliataInProfondita: false, token: conta(testo) };
|
|
491
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FASE E — MCP client. Prima fetta: connessione stdio a UN server MCP
|
|
3
|
+
* reale, elenco dei suoi tool, chiamata di un tool. Il server (FASE
|
|
4
|
+
* successiva) resta fuori da questo file.
|
|
5
|
+
*
|
|
6
|
+
* ⛔ `@modelcontextprotocol/client` (v2), NON `@modelcontextprotocol/sdk`
|
|
7
|
+
* (v1) — verificato con ctx7 il 29/8 PRIMA di scrivere una riga: l'SDK
|
|
8
|
+
* si è diviso in pacchetti scoped a metà 2026, e il pacchetto v1 resta
|
|
9
|
+
* pubblicato ma è esplicitamente "legacy support... should not be
|
|
10
|
+
* included in new projects" (doc ufficiale). Il primo ledger di questa
|
|
11
|
+
* fase (piano, sezione FASE E) nominava `@modelcontextprotocol/sdk` —
|
|
12
|
+
* scritto A MEMORIA prima che questa fase si aprisse per davvero, mai
|
|
13
|
+
* verificato alla fonte. Corretto qui, non lì: il piano resta la
|
|
14
|
+
* cronaca di quando è stato scritto, questo file è quello che conta.
|
|
15
|
+
*
|
|
16
|
+
* Filtro di sicurezza per-server (già ricercato,
|
|
17
|
+
* `elegant-spinning-dongarra.md` §2.2): un server MCP collegato non
|
|
18
|
+
* espone MAI i suoi tool al modello senza una allowlist esplicita —
|
|
19
|
+
* vedi `filtraToolMcp`.
|
|
20
|
+
*/
|
|
21
|
+
import { Client } from '@modelcontextprotocol/client'
|
|
22
|
+
import { StdioClientTransport } from '@modelcontextprotocol/client/stdio'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Apre una connessione stdio verso un server MCP e ne fa l'handshake.
|
|
26
|
+
*
|
|
27
|
+
* `config`: `{ comando, argomenti?, nome?, versione? }` — `comando` è
|
|
28
|
+
* l'eseguibile del server (es. `npx`), `argomenti` i suoi parametri
|
|
29
|
+
* (es. `['-y', '@modelcontextprotocol/server-filesystem', cartella]`).
|
|
30
|
+
*
|
|
31
|
+
* `deps.ClientCls`/`deps.TransportCls` — stesso pattern DI già in uso
|
|
32
|
+
* per `spawnPtyFn` in `pty-terminal.mjs`: default alle classi vere
|
|
33
|
+
* dell'SDK, iniettabili nei test per non spawnare mai un processo reale
|
|
34
|
+
* a unità (la verifica CON un processo reale vive in uno script a parte,
|
|
35
|
+
* non nella suite `node --test`).
|
|
36
|
+
*
|
|
37
|
+
* Torna `{ client, chiudi }` — `chiudi()` chiude il transport e il
|
|
38
|
+
* processo figlio che lo serviva. Non cattura errori di connect: un
|
|
39
|
+
* server che non parte deve fallire in modo visibile a chi chiama, non
|
|
40
|
+
* sparire in un `null`.
|
|
41
|
+
*/
|
|
42
|
+
export async function connettiServerMcp(config, deps = {}) {
|
|
43
|
+
const ClientCls = deps.ClientCls ?? Client
|
|
44
|
+
const TransportCls = deps.TransportCls ?? StdioClientTransport
|
|
45
|
+
const client = new ClientCls({
|
|
46
|
+
name: config.nome ?? 'talos-harness-desktop',
|
|
47
|
+
version: config.versione ?? '1.0.0',
|
|
48
|
+
})
|
|
49
|
+
const transport = new TransportCls({
|
|
50
|
+
command: config.comando,
|
|
51
|
+
args: config.argomenti ?? [],
|
|
52
|
+
})
|
|
53
|
+
await client.connect(transport)
|
|
54
|
+
return {
|
|
55
|
+
client,
|
|
56
|
+
chiudi: () => client.close(),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* L'elenco dei tool che il server offre, con paginazione automatica
|
|
62
|
+
* (il client la fa da solo — vedi doc ufficiale `listTools()`).
|
|
63
|
+
* Torna l'array grezzo `{name, description, inputSchema}[]`: la
|
|
64
|
+
* traduzione verso lo schema attrezzi di `talosHarness.mjs` è compito
|
|
65
|
+
* di chi chiama, non di questo modulo (stesso confine già usato per
|
|
66
|
+
* `document-generator.mjs`: qui solo il protocollo, mai la policy).
|
|
67
|
+
*/
|
|
68
|
+
export async function elencaToolMcp({ client }) {
|
|
69
|
+
const { tools } = await client.listTools()
|
|
70
|
+
return tools
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* ⛔⛔⛔ IL FILTRO DI SICUREZZA — mai un tool MCP remoto esposto al
|
|
75
|
+
* modello senza revisione esplicita. Lo stato dell'arte lo fa per-server
|
|
76
|
+
* prima di esporre i tool remoti (ricerca già fatta); stesso principio qui, più
|
|
77
|
+
* semplice: un'allowlist di nomi. Un nome non nell'allowlist non
|
|
78
|
+
* compare mai nell'elenco filtrato — non un "nascosto ma chiamabile",
|
|
79
|
+
* proprio assente.
|
|
80
|
+
*/
|
|
81
|
+
export function filtraToolMcp(tool, allowlist) {
|
|
82
|
+
return tool.filter((t) => allowlist.includes(t.name))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Chiama un tool del server. `argomenti` è l'oggetto già validato
|
|
87
|
+
* contro `inputSchema` da chi chiama (questo modulo non valida: non
|
|
88
|
+
* conosce lo schema di un tool arbitrario meglio del server stesso).
|
|
89
|
+
*
|
|
90
|
+
* Torna il risultato grezzo del protocollo — `content` (blocchi
|
|
91
|
+
* testo/immagine) e `isError` se il tool stesso ha fallito (un
|
|
92
|
+
* `isError:true` NON lancia: è un esito del tool, non un guasto del
|
|
93
|
+
* trasporto — stessa distinzione già in uso in `corsaDiCoding.mjs` fra
|
|
94
|
+
* "fallito" e "erroreDellHarness").
|
|
95
|
+
*/
|
|
96
|
+
export async function chiamaToolMcp({ client }, nome, argomenti) {
|
|
97
|
+
return client.callTool({ name: nome, arguments: argomenti })
|
|
98
|
+
}
|