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,91 @@
|
|
|
1
|
+
import { KEYBINDINGS } from "./keybindings.js";
|
|
2
|
+
const SPECIAL = new Map([
|
|
3
|
+
['pageup', 'PageUp'], ['pagedown', 'PageDown'], ['home', 'Home'], ['end', 'End'],
|
|
4
|
+
['left', 'Left'], ['right', 'Right'], ['up', 'Up'], ['down', 'Down'],
|
|
5
|
+
['enter', 'Enter'], ['tab', 'Tab'], ['esc', 'Esc'], ['escape', 'Esc'],
|
|
6
|
+
['backspace', 'Backspace'], ['delete', 'Delete'],
|
|
7
|
+
]);
|
|
8
|
+
const SAFETY = [
|
|
9
|
+
{ bindingId: 'interrupt', chord: 'Ctrl+C' },
|
|
10
|
+
{ bindingId: 'exit', chord: 'Ctrl+D' },
|
|
11
|
+
{ bindingId: 'approval-cancel', chord: 'Esc' },
|
|
12
|
+
];
|
|
13
|
+
export function canonicalKeyChord(value) {
|
|
14
|
+
if (typeof value !== 'string')
|
|
15
|
+
return null;
|
|
16
|
+
const raw = value.trim();
|
|
17
|
+
if (!raw || /[\x00-\x1f\x7f]/u.test(raw))
|
|
18
|
+
return null;
|
|
19
|
+
if ([...raw].length === 1 && raw !== '+')
|
|
20
|
+
return raw;
|
|
21
|
+
const special = SPECIAL.get(raw.toLowerCase());
|
|
22
|
+
if (special)
|
|
23
|
+
return special;
|
|
24
|
+
const shifted = /^shift\+(tab|enter)$/iu.exec(raw);
|
|
25
|
+
if (shifted)
|
|
26
|
+
return shifted[1].toLowerCase() === 'tab' ? 'Shift+Tab' : 'Shift+Enter';
|
|
27
|
+
const modified = /^(ctrl|alt)\+([^+\s])$/iu.exec(raw);
|
|
28
|
+
if (modified) {
|
|
29
|
+
const modifier = modified[1].toLowerCase() === 'ctrl' ? 'Ctrl' : 'Alt';
|
|
30
|
+
return modifier + '+' + modified[2].toUpperCase();
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function cloneDefaults() { return KEYBINDINGS.map(row => ({ ...row, keys: [...row.keys] })); }
|
|
35
|
+
export function resolveKeymap(overrides) {
|
|
36
|
+
const bindings = cloneDefaults();
|
|
37
|
+
const diagnostics = [];
|
|
38
|
+
if (overrides) {
|
|
39
|
+
for (const [bindingId, rawKeys] of Object.entries(overrides)) {
|
|
40
|
+
const targets = bindings.filter(row => row.id === bindingId);
|
|
41
|
+
if (targets.length === 0) {
|
|
42
|
+
diagnostics.push({ code: 'KEYMAP_UNKNOWN_BINDING', bindingId, message: 'Unknown key binding: ' + bindingId });
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const keys = [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
for (const raw of rawKeys) {
|
|
48
|
+
const chord = canonicalKeyChord(raw);
|
|
49
|
+
if (!chord) {
|
|
50
|
+
diagnostics.push({ code: 'KEYMAP_CHORD_INVALID', bindingId, chord: String(raw), message: 'Invalid key chord for ' + bindingId + ': ' + String(raw) });
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (seen.has(chord)) {
|
|
54
|
+
diagnostics.push({ code: 'KEYMAP_DUPLICATE_CHORD', bindingId, chord, message: 'Duplicate key chord for ' + bindingId + ': ' + chord });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
seen.add(chord);
|
|
58
|
+
keys.push(chord);
|
|
59
|
+
}
|
|
60
|
+
for (const binding of targets)
|
|
61
|
+
binding.keys = [...keys];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const byContext = new Map();
|
|
65
|
+
for (const binding of bindings) {
|
|
66
|
+
for (const chord of binding.keys) {
|
|
67
|
+
const key = binding.context + '\u0000' + chord;
|
|
68
|
+
const previous = byContext.get(key);
|
|
69
|
+
if (previous && previous.bindingId !== binding.id) {
|
|
70
|
+
diagnostics.push({ code: 'KEYMAP_CONFLICT', bindingId: binding.id, chord, conflictWith: previous.bindingId, message: 'Key chord ' + chord + ' conflicts in ' + binding.context + ': ' + previous.bindingId + ' vs ' + binding.id });
|
|
71
|
+
}
|
|
72
|
+
else
|
|
73
|
+
byContext.set(key, { bindingId: binding.id, chord });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
for (const reserved of SAFETY) {
|
|
77
|
+
const binding = bindings.find(row => row.id === reserved.bindingId);
|
|
78
|
+
if (!binding?.keys.includes(reserved.chord)) {
|
|
79
|
+
diagnostics.push({ code: 'KEYMAP_RESERVED_CHORD', bindingId: reserved.bindingId, chord: reserved.chord, message: 'Safety key ' + reserved.chord + ' must remain bound to ' + reserved.bindingId });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return { bindings, diagnostics };
|
|
83
|
+
}
|
|
84
|
+
export function assertValidKeymap(overrides) {
|
|
85
|
+
const resolved = resolveKeymap(overrides);
|
|
86
|
+
if (resolved.diagnostics.length) {
|
|
87
|
+
const first = resolved.diagnostics[0];
|
|
88
|
+
throw Object.assign(new Error(first.message), { code: 'KEYMAP_INVALID', details: { diagnostics: resolved.diagnostics } }); /* R2 (gate E6): code on error.code only */
|
|
89
|
+
}
|
|
90
|
+
return resolved.bindings;
|
|
91
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* R4 CONTRACT (owner: lane F; stub written by the coordinator so both lanes compile). Owner decision Q-R4-4: the full boot
|
|
3
|
+
* plays only on the first launch and after an update (a new version or build commit); every other launch gets the short
|
|
4
|
+
* boot (the final frame, at most 600 ms). The launch state lives under the data root, never the config root (gate E5), so
|
|
5
|
+
* a test profile starts empty and its first launch is a full boot. Lane N only CALLS bootPlan (app.ts) and passes `mode`
|
|
6
|
+
* to BootSequence; lane F owns the body (reading and recording the launch) and may not change the signature.
|
|
7
|
+
*
|
|
8
|
+
* R4 lane F (the body):
|
|
9
|
+
* - The record is `<dataRoot>/launch-state.json`, schema `talos.cli.launch-state.v1`: the version and build seen by the last
|
|
10
|
+
* launch, when TALOS first ran and when it last ran. Nothing else is kept (no path, no project, no person's data).
|
|
11
|
+
* - It fails TOWARD the full boot: a record that is missing, unreadable or of another shape is a first launch, and a record
|
|
12
|
+
* that cannot be written (read-only profile, a file where the folder should be) never throws — that launch just plays the
|
|
13
|
+
* full boot again, which is the harmless side.
|
|
14
|
+
* - A build that cannot be read (`build` null or absent) is not an update: development builds report no build, and a
|
|
15
|
+
* missing value must not replay the full boot at every start. A different non-empty build is an update.
|
|
16
|
+
* - Written through a temporary file and a rename, so a crash or a second TALOS starting at the same moment leaves either
|
|
17
|
+
* the old record or the new one, never half of one (the pattern of `security/project-trust.ts` atomicWriteText).
|
|
18
|
+
* - Synchronous on purpose: lane N calls it once while the first frame is prepared, and the file is a few hundred bytes.
|
|
19
|
+
*/
|
|
20
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { dirname, isAbsolute, join, resolve } from 'node:path';
|
|
22
|
+
import { fileURLToPath } from 'node:url';
|
|
23
|
+
const SCHEMA = 'talos.cli.launch-state.v1';
|
|
24
|
+
export function launchStatePath(dataRoot) { return join(dataRoot, 'launch-state.json'); }
|
|
25
|
+
function readRecord(file) {
|
|
26
|
+
try {
|
|
27
|
+
const value = JSON.parse(readFileSync(file, 'utf8'));
|
|
28
|
+
if (!value || value.schema !== SCHEMA || typeof value.version !== 'string' || (value.build !== null && typeof value.build !== 'string')
|
|
29
|
+
|| typeof value.firstLaunchAt !== 'string' || typeof value.lastLaunchAt !== 'string')
|
|
30
|
+
return null;
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function writeRecord(dataRoot, record) {
|
|
38
|
+
const file = launchStatePath(dataRoot);
|
|
39
|
+
const temporary = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
40
|
+
try {
|
|
41
|
+
mkdirSync(dataRoot, { recursive: true });
|
|
42
|
+
writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
|
|
43
|
+
renameSync(temporary, file);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
try {
|
|
47
|
+
rmSync(temporary, { force: true });
|
|
48
|
+
}
|
|
49
|
+
catch { /* the temporary file was never created */ }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function bootPlan(input) {
|
|
53
|
+
const previous = readRecord(launchStatePath(input.dataRoot));
|
|
54
|
+
const build = typeof input.build === 'string' && input.build !== '' ? input.build : null;
|
|
55
|
+
const firstLaunch = previous === null;
|
|
56
|
+
const updated = !firstLaunch && (previous.version !== input.version || (build !== null && previous.build !== null && previous.build !== build));
|
|
57
|
+
const at = new Date(input.now ?? Date.now()).toISOString();
|
|
58
|
+
writeRecord(input.dataRoot, { schema: SCHEMA, version: input.version, build: build ?? previous?.build ?? null, firstLaunchAt: previous?.firstLaunchAt ?? at, lastLaunchAt: at });
|
|
59
|
+
return { mode: firstLaunch || updated ? 'full' : 'short', firstLaunch };
|
|
60
|
+
}
|
|
61
|
+
/*
|
|
62
|
+
* R4 Q-R4-4 (i)(a): the build commit, so that "after an update" also means a new commit of a development checkout (whose
|
|
63
|
+
* version is always 1.0.0). It is the commit git records for the checkout the running CLI comes from, read the way git
|
|
64
|
+
* stores it — HEAD, a branch ref or packed-refs, and for a linked worktree its `.git` file and `commondir` — without spawning
|
|
65
|
+
* git. An installed copy has no checkout: null, and the version alone decides (a release has its own version). Read once
|
|
66
|
+
* per process; a checkout that cannot be read is null, never an error (the boot falls back to the version).
|
|
67
|
+
*/
|
|
68
|
+
const SHA = /^[0-9a-f]{40}$/u;
|
|
69
|
+
function readText(file) { try {
|
|
70
|
+
return readFileSync(file, 'utf8');
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
} }
|
|
75
|
+
function gitDirFrom(start) {
|
|
76
|
+
let cursor = resolve(start);
|
|
77
|
+
for (let depth = 0; depth < 12; depth++) {
|
|
78
|
+
const dotGit = join(cursor, '.git');
|
|
79
|
+
try {
|
|
80
|
+
const info = statSync(dotGit);
|
|
81
|
+
if (info.isDirectory())
|
|
82
|
+
return dotGit;
|
|
83
|
+
if (info.isFile()) {
|
|
84
|
+
const match = /^gitdir:\s*(.+?)\s*$/mu.exec(readText(dotGit) ?? '');
|
|
85
|
+
if (!match)
|
|
86
|
+
return null;
|
|
87
|
+
return isAbsolute(match[1]) ? match[1] : resolve(cursor, match[1]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch { /* no .git here: go up */ }
|
|
91
|
+
const parent = dirname(cursor);
|
|
92
|
+
if (parent === cursor)
|
|
93
|
+
return null;
|
|
94
|
+
cursor = parent;
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
function refCommit(gitDir, commonDir, ref) {
|
|
99
|
+
if (!/^refs\/[\w./-]+$/u.test(ref) || ref.includes('..'))
|
|
100
|
+
return null;
|
|
101
|
+
for (const dir of [gitDir, commonDir]) {
|
|
102
|
+
const loose = readText(join(dir, ...ref.split('/')))?.trim();
|
|
103
|
+
if (loose && SHA.test(loose))
|
|
104
|
+
return loose;
|
|
105
|
+
}
|
|
106
|
+
for (const line of (readText(join(commonDir, 'packed-refs')) ?? '').split(/\r?\n/u)) {
|
|
107
|
+
const [sha, name] = line.trim().split(' ');
|
|
108
|
+
if (name === ref && sha && SHA.test(sha))
|
|
109
|
+
return sha;
|
|
110
|
+
}
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
export function buildIdFrom(start) {
|
|
114
|
+
const gitDir = gitDirFrom(start);
|
|
115
|
+
if (!gitDir)
|
|
116
|
+
return null;
|
|
117
|
+
const common = readText(join(gitDir, 'commondir'))?.trim();
|
|
118
|
+
const commonDir = common ? resolve(gitDir, common) : gitDir;
|
|
119
|
+
const head = readText(join(gitDir, 'HEAD'))?.trim() ?? '';
|
|
120
|
+
if (SHA.test(head))
|
|
121
|
+
return head;
|
|
122
|
+
const match = /^ref:\s*(\S+)$/u.exec(head);
|
|
123
|
+
return match ? refCommit(gitDir, commonDir, match[1]) : null;
|
|
124
|
+
}
|
|
125
|
+
let cachedBuildId;
|
|
126
|
+
/** The running CLI's build commit (or that of the checkout holding `start`); null for an installed copy. */
|
|
127
|
+
export function currentBuildId(start) {
|
|
128
|
+
if (start !== undefined)
|
|
129
|
+
return buildIdFrom(start);
|
|
130
|
+
if (cachedBuildId === undefined) {
|
|
131
|
+
try {
|
|
132
|
+
cachedBuildId = buildIdFrom(dirname(fileURLToPath(import.meta.url)));
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
cachedBuildId = null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return cachedBuildId;
|
|
139
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* R3 CONTRACT (owner: lane R+D; stub written by the coordinator so both lanes compile). The ONE line diff (Q-R3-11: jsdiff
|
|
3
|
+
* 9.0.0, pinned) feeding the write/edit row summary, its Ctrl+O diff and the approval preview (gate E10). It returns the
|
|
4
|
+
* existing DiffModel so components/diff.ts renders it. Lane S+P only CALLS lineDiff; lane R+D owns the body and may not
|
|
5
|
+
* change the signature.
|
|
6
|
+
*
|
|
7
|
+
* R3 lane RD body. jsdiff's `structuredPatch` (Myers, the library Gemini 8.0.3, Pi 8.0.4, Qwen ^7 and OpenCode diff with,
|
|
8
|
+
* gate §3.2) with 3 context lines, the same as `/diff` (`git diff --unified=3`). The counts are the `+`/`-` lines of the
|
|
9
|
+
* hunks, so a moved line and an edit of one of two identical lines are seen — the multiset count it replaces saw neither.
|
|
10
|
+
* Lines are compared exactly as written (a CRLF → LF change is a change); a trailing `\r` never reaches the screen.
|
|
11
|
+
* A change too large to compare in time (jsdiff `timeout`, and a line cap that keeps a huge file off the event loop)
|
|
12
|
+
* returns the whole replacement flagged `approximate` with one plain row saying so: never a fake minimal diff.
|
|
13
|
+
*/
|
|
14
|
+
import { structuredPatch } from 'diff';
|
|
15
|
+
import { t } from "../i18n/index.js";
|
|
16
|
+
/* Beyond these the diff is not attempted (Myers is O((N+M)·D)); the row then states the sizes, not a count it cannot know. */
|
|
17
|
+
const MAX_LINES = 50_000;
|
|
18
|
+
const TIMEOUT_MS = 250;
|
|
19
|
+
const CONTEXT = 3;
|
|
20
|
+
function linesOf(text) { if (text === '')
|
|
21
|
+
return 0; const n = text.split('\n').length; return text.endsWith('\n') ? n - 1 : n; }
|
|
22
|
+
function row(kind, text, oldLine = null, newLine = null) { return { kind, text: text.endsWith('\r') ? text.slice(0, -1) : text, oldLine, newLine }; }
|
|
23
|
+
export function lineDiff(before, after, { path = 'file' } = {}) {
|
|
24
|
+
const old = before ?? '', next = String(after ?? '');
|
|
25
|
+
if (old === next)
|
|
26
|
+
return { added: 0, removed: 0, model: { files: [], plain: [], empty: true } };
|
|
27
|
+
const oldCount = linesOf(old), newCount = linesOf(next);
|
|
28
|
+
const patch = oldCount + newCount > MAX_LINES ? undefined : structuredPatch(path, path, old, next, undefined, undefined, { context: CONTEXT, timeout: TIMEOUT_MS });
|
|
29
|
+
if (!patch) {
|
|
30
|
+
const note = row('plain', t('tool.diff.tooLarge', { before: oldCount, after: newCount }));
|
|
31
|
+
return { added: newCount, removed: oldCount, approximate: true, model: { files: [], plain: [note], empty: false } };
|
|
32
|
+
}
|
|
33
|
+
const rows = [];
|
|
34
|
+
let added = 0, removed = 0;
|
|
35
|
+
for (const hunk of patch.hunks) {
|
|
36
|
+
/* The header the way `git diff` and jsdiff's own formatPatch print it: an empty side starts one line earlier. */
|
|
37
|
+
const oldStart = hunk.oldLines === 0 ? hunk.oldStart - 1 : hunk.oldStart, newStart = hunk.newLines === 0 ? hunk.newStart - 1 : hunk.newStart;
|
|
38
|
+
rows.push(row('hunk', `@@ -${oldStart},${hunk.oldLines} +${newStart},${hunk.newLines} @@`));
|
|
39
|
+
let oldLine = hunk.oldStart, newLine = hunk.newStart;
|
|
40
|
+
for (const text of hunk.lines) {
|
|
41
|
+
const mark = text[0], body = text.slice(1);
|
|
42
|
+
if (mark === '+') {
|
|
43
|
+
rows.push(row('add', body, null, newLine++));
|
|
44
|
+
added++;
|
|
45
|
+
}
|
|
46
|
+
else if (mark === '-') {
|
|
47
|
+
rows.push(row('remove', body, oldLine++, null));
|
|
48
|
+
removed++;
|
|
49
|
+
}
|
|
50
|
+
else if (mark === '\\')
|
|
51
|
+
rows.push(row('no-newline', text));
|
|
52
|
+
else
|
|
53
|
+
rows.push(row('context', body, oldLine++, newLine++));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { added, removed, model: { files: [{ oldPath: path, newPath: path, rows }], plain: [], empty: rows.length === 0 } };
|
|
57
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { t } from "../i18n/index.js";
|
|
2
|
+
import { toolDisplay } from "./tool-display.js";
|
|
3
|
+
const ACTIVE_TOOL = new Set(['running', 'streaming']);
|
|
4
|
+
export function deriveLiveActivity(state, busySince) {
|
|
5
|
+
if (!state.running) {
|
|
6
|
+
if (typeof state.sentAt === 'number')
|
|
7
|
+
return { kind: 'starting', startedAt: state.sentAt };
|
|
8
|
+
if (state.lastTurn)
|
|
9
|
+
return { kind: state.lastTurn.outcome, startedAt: null, frozenMs: state.lastTurn.durationMs };
|
|
10
|
+
return { kind: 'idle', startedAt: null };
|
|
11
|
+
}
|
|
12
|
+
if (state.waitingForPerson === true)
|
|
13
|
+
return { kind: 'waiting', startedAt: busySince };
|
|
14
|
+
/* R5a S4 (E5): a retry is its own state, never "thinking": attempt 2 and later of a request the provider refused. */
|
|
15
|
+
if (isRetry(state.retry))
|
|
16
|
+
return { kind: 'retrying', startedAt: state.retry.at };
|
|
17
|
+
const tool = [...state.tools].reverse().find(row => ACTIVE_TOOL.has(row.status));
|
|
18
|
+
if (tool)
|
|
19
|
+
return tool.name ? { kind: 'tool', startedAt: busySince, tool: toolDisplay(tool.name).label } : { kind: 'tool', startedAt: busySince };
|
|
20
|
+
if (state.streamingAssistant && state.streamingAssistant.text.length > 0)
|
|
21
|
+
return { kind: 'writing', startedAt: busySince };
|
|
22
|
+
return { kind: 'thinking', startedAt: busySince };
|
|
23
|
+
}
|
|
24
|
+
/*
|
|
25
|
+
* R5a S4 (owner decision Q-R5-6; gate E5): the retry line, `Retrying · attempt 2 of 4 · HTTP 429 · in 7 s` (Pi
|
|
26
|
+
* `status-indicator.ts:51-81`, 2026-09-07; OpenCode PR #47145, 2026-09-04). The countdown is shown only while the wait is
|
|
27
|
+
* known and not over; with no known wait the provider's own `Retry-After` is stated as asked, never counted down; with
|
|
28
|
+
* neither, no time at all. Esc interrupts it like any turn (the kernel's wait is abortable).
|
|
29
|
+
*/
|
|
30
|
+
export function isRetry(retry) { return Boolean(retry && retry.attempt >= 2); }
|
|
31
|
+
export function retryCountdownEnd(retry) { return isRetry(retry) && retry.waitMs !== null ? retry.at + retry.waitMs : null; }
|
|
32
|
+
export function retryLine(retry, nowMs) {
|
|
33
|
+
const parts = [t('screen.retry.title'), t('screen.retry.attempt', { attempt: retry.attempt, max: retry.maxAttempts }), retry.status === null ? t('screen.retry.noResponse') : t('screen.retry.http', { status: retry.status })];
|
|
34
|
+
const left = retry.waitMs === null ? null : retry.at + retry.waitMs - nowMs;
|
|
35
|
+
if (left !== null && left > 0)
|
|
36
|
+
parts.push(t('screen.retry.in', { seconds: Math.ceil(left / 1000) }));
|
|
37
|
+
else if (retry.waitMs === null && retry.retryAfterMs !== null)
|
|
38
|
+
parts.push(t('screen.retry.asked', { seconds: Math.ceil(retry.retryAfterMs / 1000) }));
|
|
39
|
+
return parts.join(' · ');
|
|
40
|
+
}
|
|
41
|
+
/** Kinds that change while you look at them: the only ones that start the motion clock (idle writes nothing, R1 E5).
|
|
42
|
+
* `retrying` is not one of them: its countdown ticks once a second, and only while the wait lasts (app.ts). */
|
|
43
|
+
export function isAnimatedActivity(kind) {
|
|
44
|
+
return kind === 'starting' || kind === 'thinking' || kind === 'writing' || kind === 'tool' || kind === 'waiting' || kind === 'compacting' || kind === 'connecting' || kind === 'updating';
|
|
45
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export const NOOP_TUI_METRICS_SINK = Object.freeze({ record(_metric) { } });
|
|
2
|
+
export function createTuiMetricsCollector() {
|
|
3
|
+
const metrics = [];
|
|
4
|
+
return {
|
|
5
|
+
sink: { record(metric) { metrics.push(structuredClone(metric)); } },
|
|
6
|
+
snapshot: () => structuredClone(metrics),
|
|
7
|
+
reset: () => { metrics.length = 0; }
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function createTuiMetricsProfiler(options = {}) {
|
|
11
|
+
const sink = options.sink ?? NOOP_TUI_METRICS_SINK;
|
|
12
|
+
const clock = options.clock ?? (() => performance.now());
|
|
13
|
+
const receivedAt = new Map();
|
|
14
|
+
return {
|
|
15
|
+
eventReceived(eventId) { receivedAt.set(eventId, clock()); },
|
|
16
|
+
renderCoalesced(waitMs) { sink.record({ name: 'tui.render.coalesce_ms', value: Math.max(0, waitMs), unit: 'milliseconds' }); },
|
|
17
|
+
renderCommitted({ eventIds = [], reason }) {
|
|
18
|
+
sink.record({ name: 'tui.render.committed', value: 1, unit: 'count', tags: { reason } });
|
|
19
|
+
const visibleAt = clock();
|
|
20
|
+
for (const eventId of eventIds) {
|
|
21
|
+
const received = receivedAt.get(eventId);
|
|
22
|
+
if (received === undefined)
|
|
23
|
+
continue;
|
|
24
|
+
sink.record({ name: 'tui.event_to_visible_ms', value: Math.max(0, visibleAt - received), unit: 'milliseconds' });
|
|
25
|
+
receivedAt.delete(eventId);
|
|
26
|
+
}
|
|
27
|
+
if (reason === 'idle')
|
|
28
|
+
sink.record({ name: 'tui.render.idle', value: 1, unit: 'count' });
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function percentile(values, fraction) {
|
|
33
|
+
if (values.length === 0)
|
|
34
|
+
return 0;
|
|
35
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
36
|
+
const rank = Math.max(1, Math.ceil(sorted.length * fraction));
|
|
37
|
+
return sorted[Math.min(sorted.length - 1, rank - 1)];
|
|
38
|
+
}
|
|
39
|
+
export function summarizeTuiMetrics(metrics) {
|
|
40
|
+
const visible = metrics.filter(row => row.name === 'tui.event_to_visible_ms').map(row => row.value);
|
|
41
|
+
const coalesced = metrics.filter(row => row.name === 'tui.render.coalesce_ms').map(row => row.value);
|
|
42
|
+
const renders = metrics.filter(row => row.name === 'tui.ink.render_ms').map(row => row.value);
|
|
43
|
+
const total = (name, kind) => metrics.reduce((sum, row) => row.name === name && (kind === undefined || row.tags?.kind === kind) ? sum + row.value : sum, 0);
|
|
44
|
+
return {
|
|
45
|
+
committed: metrics.filter(row => row.name === 'tui.render.committed').length,
|
|
46
|
+
idle: metrics.filter(row => row.name === 'tui.render.idle').length,
|
|
47
|
+
coalesceMaxMs: coalesced.length ? Math.max(...coalesced) : 0,
|
|
48
|
+
eventToVisible: {
|
|
49
|
+
count: visible.length,
|
|
50
|
+
p50: percentile(visible, 0.50),
|
|
51
|
+
p95: percentile(visible, 0.95),
|
|
52
|
+
max: visible.length ? Math.max(...visible) : 0,
|
|
53
|
+
},
|
|
54
|
+
/* Gate E6: what TALOS itself wrote (before ConPTY), and how often Ink rendered. Zero while idle is the target (E5). */
|
|
55
|
+
inkRenders: { count: renders.length, maxMs: renders.length ? Math.max(...renders) : 0 },
|
|
56
|
+
frames: total('tui.output.frame'),
|
|
57
|
+
bytes: total('tui.output.bytes'),
|
|
58
|
+
clears: { display: total('tui.output.clear', 'display'), scrollback: total('tui.output.clear', 'scrollback') },
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Gate E6, in-process: counts what TALOS writes to the terminal — bytes, DEC 2026 frames (one BSU per Ink frame) and
|
|
63
|
+
* the full clears `ESC[2J` / `ESC[3J` that Ink 7.1.1 writes on win32 for every commit of a frame at least as tall as the
|
|
64
|
+
* terminal (`ink/build/ink.js:83-112, 748-772`). A sequence split across two writes counts once: a match counts only
|
|
65
|
+
* when it ends in the newest chunk.
|
|
66
|
+
*/
|
|
67
|
+
const OUTPUT_SEQUENCES = [
|
|
68
|
+
['\u001B[?2026h', 'tui.output.frame', undefined],
|
|
69
|
+
['\u001B[2J', 'tui.output.clear', { kind: 'display' }],
|
|
70
|
+
['\u001B[3J', 'tui.output.clear', { kind: 'scrollback' }],
|
|
71
|
+
];
|
|
72
|
+
export function createTerminalOutputMeter(sink) {
|
|
73
|
+
let carry = '';
|
|
74
|
+
return { observe(chunk) {
|
|
75
|
+
const text = typeof chunk === 'string' ? chunk : chunk instanceof Uint8Array ? Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength).toString('latin1') : '';
|
|
76
|
+
const bytes = typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk instanceof Uint8Array ? chunk.byteLength : 0;
|
|
77
|
+
if (bytes === 0)
|
|
78
|
+
return;
|
|
79
|
+
sink.record({ name: 'tui.output.bytes', value: bytes, unit: 'count' });
|
|
80
|
+
const scan = carry + text;
|
|
81
|
+
for (const [sequence, name, tags] of OUTPUT_SEQUENCES) {
|
|
82
|
+
let found = 0;
|
|
83
|
+
for (let at = scan.indexOf(sequence); at >= 0; at = scan.indexOf(sequence, at + 1))
|
|
84
|
+
if (at + sequence.length > carry.length)
|
|
85
|
+
found++;
|
|
86
|
+
if (found)
|
|
87
|
+
sink.record({ name, value: found, unit: 'count', ...(tags ? { tags } : {}) });
|
|
88
|
+
}
|
|
89
|
+
carry = scan.slice(-7);
|
|
90
|
+
} };
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Wires the E6 counters for `TALOS_TUI_RENDER_METRICS=1`: wraps `stdout.write` (every chunk still reaches the terminal)
|
|
94
|
+
* and gives the `onRender` option of Ink's `render()` (Ink 7.1.1 calls it once per rendered frame, after throttling;
|
|
95
|
+
* `ink/build/ink.js:349`), so Ink renders are counted in-process. `dispose()` restores the original `write`.
|
|
96
|
+
*/
|
|
97
|
+
export function createTuiRenderMetricsTap({ stdout, sink }) {
|
|
98
|
+
const meter = createTerminalOutputMeter(sink);
|
|
99
|
+
const original = stdout.write;
|
|
100
|
+
const tapped = function (...args) { meter.observe(args[0]); return original.apply(this ?? stdout, args); };
|
|
101
|
+
stdout.write = tapped;
|
|
102
|
+
return {
|
|
103
|
+
inkOptions: { onRender({ renderTime }) { sink.record({ name: 'tui.ink.render_ms', value: Math.max(0, renderTime), unit: 'milliseconds' }); } },
|
|
104
|
+
dispose() { if (stdout.write === tapped)
|
|
105
|
+
stdout.write = original; },
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export function currentProcessPerformanceSnapshot() {
|
|
109
|
+
const memory = process.memoryUsage();
|
|
110
|
+
const usage = process.resourceUsage();
|
|
111
|
+
let eventLoopUtilization = null;
|
|
112
|
+
try {
|
|
113
|
+
const p = performance;
|
|
114
|
+
const row = typeof p.eventLoopUtilization === 'function' ? p.eventLoopUtilization() : null;
|
|
115
|
+
eventLoopUtilization = typeof row?.utilization === 'number' && Number.isFinite(row.utilization) ? row.utilization : null;
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
return {
|
|
119
|
+
rssBytes: memory.rss, heapUsedBytes: memory.heapUsed, externalBytes: memory.external,
|
|
120
|
+
userCPUTimeMicros: usage.userCPUTime, systemCPUTimeMicros: usage.systemCPUTime,
|
|
121
|
+
maxRSSKiB: usage.maxRSS, eventLoopUtilization,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { t } from "../i18n/index.js";
|
|
2
|
+
import { missingKeyText } from "../provider/missing-key.js";
|
|
3
|
+
import { inertProjectTrustText } from "./project-trust-prompt.js";
|
|
4
|
+
/* The words live in the dictionary (R4: moved from here to i18n/en/firstrun.ts, same words). */
|
|
5
|
+
export const ONBOARDING_COPY = Object.freeze({
|
|
6
|
+
en: Object.freeze({
|
|
7
|
+
trust: Object.freeze({ text: t('firstrun.setup.trust'), command: 'talos project trust --include-nested' }),
|
|
8
|
+
provider: Object.freeze({ text: t('firstrun.setup.provider'), command: '/provider' }),
|
|
9
|
+
model: Object.freeze({ text: t('firstrun.setup.model'), command: '/model' }),
|
|
10
|
+
}),
|
|
11
|
+
});
|
|
12
|
+
/*
|
|
13
|
+
* R4 CONTRACT (owner: lane F; lane N calls it from app.ts), owner decision Q-R4-13:
|
|
14
|
+
* - `keyMissing`: the chosen provider has no key usable now. `true` says so in general; a provider id says it with the one
|
|
15
|
+
* missing-key sentence (R2 E8, the same as -p, `provider test` and doctor), which names the provider's own variable. Either
|
|
16
|
+
* way the command is /provider. It follows the provider step (with no provider chosen, choosing one asks for the key).
|
|
17
|
+
* - `loadedFiles`: the instruction files TALOS loaded for this folder (main.ts, after trust), named relative to the project
|
|
18
|
+
* root; one `info` line first — `Loaded AGENTS.md` — even when nothing else is missing.
|
|
19
|
+
*/
|
|
20
|
+
export function onboardingSteps(input, locale = 'en') {
|
|
21
|
+
const copy = ONBOARDING_COPY[locale] ?? ONBOARDING_COPY.en;
|
|
22
|
+
const out = [];
|
|
23
|
+
const files = (input.loadedFiles ?? []).filter(file => typeof file === 'string' && file.length > 0);
|
|
24
|
+
if (files.length)
|
|
25
|
+
out.push({ id: 'loaded', kind: 'info', text: t('firstrun.setup.loaded', { files: files.map(inertProjectTrustText).join(', ') }), command: '' });
|
|
26
|
+
const step = (id) => out.push({ id, kind: 'step', text: copy[id].text, command: copy[id].command });
|
|
27
|
+
if (!input.projectTrusted)
|
|
28
|
+
step('trust');
|
|
29
|
+
if (!input.providerConfigured)
|
|
30
|
+
step('provider');
|
|
31
|
+
else if (input.keyMissing)
|
|
32
|
+
out.push({ id: 'key', kind: 'step', text: typeof input.keyMissing === 'string' ? missingKeyText(input.keyMissing) : t('firstrun.setup.key'), command: '/provider' });
|
|
33
|
+
if (!input.modelConfigured)
|
|
34
|
+
step('model');
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { t } from "../../i18n/index.js";
|
|
2
|
+
import { agentRosterLine, shellSafeText } from "../components/terminal-shell.js";
|
|
3
|
+
import { truncateDisplay } from "../text-width.js";
|
|
4
|
+
import { listOverlay } from "./scroll-window.js";
|
|
5
|
+
export function createAgentTreeOverlayModel(rows, selectedId) {
|
|
6
|
+
const copied = rows.map(row => ({ ...row, usage: row.usage ? { ...row.usage } : null, delegationEvidence: row.delegationEvidence ? { ...row.delegationEvidence } : null }));
|
|
7
|
+
const index = selectedId ? copied.findIndex(row => row.id === selectedId) : -1;
|
|
8
|
+
return { rows: copied, selected: index >= 0 ? index : 0 };
|
|
9
|
+
}
|
|
10
|
+
export function moveAgentTreeSelection(model, delta) {
|
|
11
|
+
if (model.rows.length === 0)
|
|
12
|
+
return { ...model, selected: 0 };
|
|
13
|
+
const next = Math.max(0, Math.min(model.rows.length - 1, model.selected + Math.trunc(delta)));
|
|
14
|
+
return next === model.selected ? model : { ...model, selected: next };
|
|
15
|
+
}
|
|
16
|
+
export function selectedAgentRow(model) {
|
|
17
|
+
return model.rows[model.selected] ?? null;
|
|
18
|
+
}
|
|
19
|
+
export function agentTreeOverlayLines(model, input) {
|
|
20
|
+
const width = Math.max(1, Math.floor(input.width));
|
|
21
|
+
const fit = (value) => truncateDisplay(shellSafeText(value), width);
|
|
22
|
+
const lines = [fit(t('screen.agents.title'))];
|
|
23
|
+
model.rows.forEach((row, index) => {
|
|
24
|
+
const display = { ...row, focused: index === model.selected };
|
|
25
|
+
lines.push(agentRosterLine({ row: display, width, nowMs: input.nowMs, marker: index === model.selected ? '›' : ' ' }));
|
|
26
|
+
});
|
|
27
|
+
const selected = selectedAgentRow(model);
|
|
28
|
+
if (!selected)
|
|
29
|
+
return [...lines, fit(t('screen.agents.empty'))];
|
|
30
|
+
const detail = selected.id === input.currentSessionId && input.running
|
|
31
|
+
? t('screen.agents.steerHint')
|
|
32
|
+
: t('screen.agents.inspectOnly');
|
|
33
|
+
lines.push(fit(detail));
|
|
34
|
+
return lines;
|
|
35
|
+
}
|
|
36
|
+
// R1 Q-R1-3: the same lines as a capped, scrolling overlay (overlays/scroll-window.ts).
|
|
37
|
+
export function agentTreeOverlay(model, input) {
|
|
38
|
+
return listOverlay(agentTreeOverlayLines(model, input), { head: 1, list: model.rows.length, selected: model.selected, foot: 1, tones: (line, index) => index === 0 ? 'accent' : /COLLISION/u.test(line) ? 'strong' : 'dim' });
|
|
39
|
+
}
|