apex-code 0.0.1-alpha.1 → 0.0.1-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +482 -0
- package/README.md +75 -680
- package/dist/cli/args.d.ts +21 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +119 -15
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +3 -3
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/cli/concurrent-session.d.ts +3 -0
- package/dist/cli/concurrent-session.d.ts.map +1 -0
- package/dist/cli/concurrent-session.js +13 -0
- package/dist/cli/concurrent-session.js.map +1 -0
- package/dist/cli/cost-command.d.ts +19 -0
- package/dist/cli/cost-command.d.ts.map +1 -0
- package/dist/cli/cost-command.js +107 -0
- package/dist/cli/cost-command.js.map +1 -0
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/startup-ui.d.ts +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +1 -2
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +99 -16
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +58 -24
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +2 -0
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session-services.d.ts +28 -0
- package/dist/core/agent-session-services.d.ts.map +1 -1
- package/dist/core/agent-session-services.js +140 -4
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +147 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +440 -123
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +4 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +17 -14
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +18 -10
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/checkpoints/git-checkpoints.d.ts +33 -0
- package/dist/core/checkpoints/git-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/git-checkpoints.js +230 -0
- package/dist/core/checkpoints/git-checkpoints.js.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts +23 -0
- package/dist/core/checkpoints/session-checkpoints.d.ts.map +1 -0
- package/dist/core/checkpoints/session-checkpoints.js +29 -0
- package/dist/core/checkpoints/session-checkpoints.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/context/deferred-schemas.d.ts +92 -0
- package/dist/core/context/deferred-schemas.d.ts.map +1 -0
- package/dist/core/context/deferred-schemas.js +74 -0
- package/dist/core/context/deferred-schemas.js.map +1 -0
- package/dist/core/context/eviction.d.ts +47 -0
- package/dist/core/context/eviction.d.ts.map +1 -0
- package/dist/core/context/eviction.js +83 -0
- package/dist/core/context/eviction.js.map +1 -0
- package/dist/core/context/pipeline.d.ts +90 -0
- package/dist/core/context/pipeline.d.ts.map +1 -0
- package/dist/core/context/pipeline.js +109 -0
- package/dist/core/context/pipeline.js.map +1 -0
- package/dist/core/credential-failover.d.ts +30 -0
- package/dist/core/credential-failover.d.ts.map +1 -0
- package/dist/core/credential-failover.js +46 -0
- package/dist/core/credential-failover.js.map +1 -0
- package/dist/core/credential-pool.d.ts +60 -0
- package/dist/core/credential-pool.d.ts.map +1 -0
- package/dist/core/credential-pool.js +84 -0
- package/dist/core/credential-pool.js.map +1 -0
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/delegation/agents.d.ts +15 -0
- package/dist/core/delegation/agents.d.ts.map +1 -0
- package/dist/core/delegation/agents.js +55 -0
- package/dist/core/delegation/agents.js.map +1 -0
- package/dist/core/delegation/ceiling.d.ts +29 -0
- package/dist/core/delegation/ceiling.d.ts.map +1 -0
- package/dist/core/delegation/ceiling.js +34 -0
- package/dist/core/delegation/ceiling.js.map +1 -0
- package/dist/core/delegation/runtime.d.ts +94 -0
- package/dist/core/delegation/runtime.d.ts.map +1 -0
- package/dist/core/delegation/runtime.js +117 -0
- package/dist/core/delegation/runtime.js.map +1 -0
- package/dist/core/durable-state/daemon.d.ts +37 -0
- package/dist/core/durable-state/daemon.d.ts.map +1 -0
- package/dist/core/durable-state/daemon.js +62 -0
- package/dist/core/durable-state/daemon.js.map +1 -0
- package/dist/core/durable-state/provenance.d.ts +7 -0
- package/dist/core/durable-state/provenance.d.ts.map +1 -0
- package/dist/core/durable-state/provenance.js +24 -0
- package/dist/core/durable-state/provenance.js.map +1 -0
- package/dist/core/durable-state/sqlite.d.ts +84 -0
- package/dist/core/durable-state/sqlite.d.ts.map +1 -0
- package/dist/core/durable-state/sqlite.js +355 -0
- package/dist/core/durable-state/sqlite.js.map +1 -0
- package/dist/core/environment.d.ts +12 -0
- package/dist/core/environment.d.ts.map +1 -0
- package/dist/core/environment.js +69 -0
- package/dist/core/environment.js.map +1 -0
- package/dist/core/evidence-policy.d.ts +17 -0
- package/dist/core/evidence-policy.d.ts.map +1 -0
- package/dist/core/evidence-policy.js +25 -0
- package/dist/core/evidence-policy.js.map +1 -0
- package/dist/core/evidence.d.ts +16 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +51 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +5 -2
- package/dist/core/exec.js.map +1 -1
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +6 -1
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +122 -49
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +44 -9
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +1 -1
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/http-dispatcher.d.ts +1 -2
- package/dist/core/http-dispatcher.d.ts.map +1 -1
- package/dist/core/http-dispatcher.js +2 -17
- package/dist/core/http-dispatcher.js.map +1 -1
- package/dist/core/http-idle-timeout.d.ts +12 -0
- package/dist/core/http-idle-timeout.d.ts.map +1 -0
- package/dist/core/http-idle-timeout.js +27 -0
- package/dist/core/http-idle-timeout.js.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/keybindings.d.ts +38 -13
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +26 -5
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/lsp/client.d.ts +78 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +494 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/diagnostics.d.ts +47 -0
- package/dist/core/lsp/diagnostics.d.ts.map +1 -0
- package/dist/core/lsp/diagnostics.js +224 -0
- package/dist/core/lsp/diagnostics.js.map +1 -0
- package/dist/core/lsp/pool.d.ts +48 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +150 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/registry.d.ts +49 -0
- package/dist/core/lsp/registry.d.ts.map +1 -0
- package/dist/core/lsp/registry.js +215 -0
- package/dist/core/lsp/registry.js.map +1 -0
- package/dist/core/lsp/status.d.ts +23 -0
- package/dist/core/lsp/status.d.ts.map +1 -0
- package/dist/core/lsp/status.js +35 -0
- package/dist/core/lsp/status.js.map +1 -0
- package/dist/core/lsp/sync.d.ts +13 -0
- package/dist/core/lsp/sync.d.ts.map +1 -0
- package/dist/core/lsp/sync.js +71 -0
- package/dist/core/lsp/sync.js.map +1 -0
- package/dist/core/mcp/config.d.ts +16 -0
- package/dist/core/mcp/config.d.ts.map +1 -0
- package/dist/core/mcp/config.js +152 -0
- package/dist/core/mcp/config.js.map +1 -0
- package/dist/core/mcp/connector.d.ts +10 -0
- package/dist/core/mcp/connector.d.ts.map +1 -0
- package/dist/core/mcp/connector.js +70 -0
- package/dist/core/mcp/connector.js.map +1 -0
- package/dist/core/mcp/contract.d.ts +19 -0
- package/dist/core/mcp/contract.d.ts.map +1 -0
- package/dist/core/mcp/contract.js +89 -0
- package/dist/core/mcp/contract.js.map +1 -0
- package/dist/core/mcp/mcp-tool.d.ts +32 -0
- package/dist/core/mcp/mcp-tool.d.ts.map +1 -0
- package/dist/core/mcp/mcp-tool.js +165 -0
- package/dist/core/mcp/mcp-tool.js.map +1 -0
- package/dist/core/mcp/metadata-cache.d.ts +29 -0
- package/dist/core/mcp/metadata-cache.d.ts.map +1 -0
- package/dist/core/mcp/metadata-cache.js +99 -0
- package/dist/core/mcp/metadata-cache.js.map +1 -0
- package/dist/core/mcp/runtime.d.ts +18 -0
- package/dist/core/mcp/runtime.d.ts.map +1 -0
- package/dist/core/mcp/runtime.js +35 -0
- package/dist/core/mcp/runtime.js.map +1 -0
- package/dist/core/mcp/schema.d.ts +22 -0
- package/dist/core/mcp/schema.d.ts.map +1 -0
- package/dist/core/mcp/schema.js +20 -0
- package/dist/core/mcp/schema.js.map +1 -0
- package/dist/core/mcp/server-manager.d.ts +43 -0
- package/dist/core/mcp/server-manager.d.ts.map +1 -0
- package/dist/core/mcp/server-manager.js +142 -0
- package/dist/core/mcp/server-manager.js.map +1 -0
- package/dist/core/mcp/types.d.ts +98 -0
- package/dist/core/mcp/types.d.ts.map +1 -0
- package/dist/core/mcp/types.js +8 -0
- package/dist/core/mcp/types.js.map +1 -0
- package/dist/core/model-config.d.ts +18 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +25 -6
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +21 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +73 -8
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +73 -0
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +225 -8
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/observability/aggregate.d.ts +21 -0
- package/dist/core/observability/aggregate.d.ts.map +1 -0
- package/dist/core/observability/aggregate.js +36 -0
- package/dist/core/observability/aggregate.js.map +1 -0
- package/dist/core/observability/otlp.d.ts +66 -0
- package/dist/core/observability/otlp.d.ts.map +1 -0
- package/dist/core/observability/otlp.js +95 -0
- package/dist/core/observability/otlp.js.map +1 -0
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +40 -16
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/permissions/gate.d.ts +42 -0
- package/dist/core/permissions/gate.d.ts.map +1 -0
- package/dist/core/permissions/gate.js +72 -0
- package/dist/core/permissions/gate.js.map +1 -0
- package/dist/core/permissions/modes.d.ts +18 -0
- package/dist/core/permissions/modes.d.ts.map +1 -0
- package/dist/core/permissions/modes.js +57 -0
- package/dist/core/permissions/modes.js.map +1 -0
- package/dist/core/permissions/responder.d.ts +30 -0
- package/dist/core/permissions/responder.d.ts.map +1 -0
- package/dist/core/permissions/responder.js +32 -0
- package/dist/core/permissions/responder.js.map +1 -0
- package/dist/core/permissions/rules.d.ts +34 -0
- package/dist/core/permissions/rules.d.ts.map +1 -0
- package/dist/core/permissions/rules.js +74 -0
- package/dist/core/permissions/rules.js.map +1 -0
- package/dist/core/permissions/startup.d.ts +45 -0
- package/dist/core/permissions/startup.d.ts.map +1 -0
- package/dist/core/permissions/startup.js +63 -0
- package/dist/core/permissions/startup.js.map +1 -0
- package/dist/core/permissions/store.d.ts +114 -0
- package/dist/core/permissions/store.d.ts.map +1 -0
- package/dist/core/permissions/store.js +229 -0
- package/dist/core/permissions/store.js.map +1 -0
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/project-trust.d.ts.map +1 -1
- package/dist/core/project-trust.js +1 -1
- package/dist/core/project-trust.js.map +1 -1
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +6 -7
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +2 -2
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +22 -17
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +3 -2
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sandbox/bwrap-arguments.d.ts +52 -0
- package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
- package/dist/core/sandbox/bwrap-arguments.js +86 -0
- package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
- package/dist/core/sandbox/child-entry.d.ts +2 -0
- package/dist/core/sandbox/child-entry.d.ts.map +1 -0
- package/dist/core/sandbox/child-entry.js +22 -0
- package/dist/core/sandbox/child-entry.js.map +1 -0
- package/dist/core/sandbox/cli-launch.d.ts +88 -0
- package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
- package/dist/core/sandbox/cli-launch.js +272 -0
- package/dist/core/sandbox/cli-launch.js.map +1 -0
- package/dist/core/sandbox/cli-supervisor.d.ts +30 -0
- package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/cli-supervisor.js +123 -0
- package/dist/core/sandbox/cli-supervisor.js.map +1 -0
- package/dist/core/sandbox/default-hosts.d.ts +28 -0
- package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
- package/dist/core/sandbox/default-hosts.js +69 -0
- package/dist/core/sandbox/default-hosts.js.map +1 -0
- package/dist/core/sandbox/full-access.d.ts +28 -0
- package/dist/core/sandbox/full-access.d.ts.map +1 -0
- package/dist/core/sandbox/full-access.js +63 -0
- package/dist/core/sandbox/full-access.js.map +1 -0
- package/dist/core/sandbox/git-identity.d.ts +57 -0
- package/dist/core/sandbox/git-identity.d.ts.map +1 -0
- package/dist/core/sandbox/git-identity.js +78 -0
- package/dist/core/sandbox/git-identity.js.map +1 -0
- package/dist/core/sandbox/host-approval.d.ts +42 -0
- package/dist/core/sandbox/host-approval.d.ts.map +1 -0
- package/dist/core/sandbox/host-approval.js +99 -0
- package/dist/core/sandbox/host-approval.js.map +1 -0
- package/dist/core/sandbox/linux-backend.d.ts +46 -0
- package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
- package/dist/core/sandbox/linux-backend.js +304 -0
- package/dist/core/sandbox/linux-backend.js.map +1 -0
- package/dist/core/sandbox/macos-backend.d.ts +28 -0
- package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
- package/dist/core/sandbox/macos-backend.js +313 -0
- package/dist/core/sandbox/macos-backend.js.map +1 -0
- package/dist/core/sandbox/network-proxy.d.ts +38 -0
- package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/network-proxy.js +153 -0
- package/dist/core/sandbox/network-proxy.js.map +1 -0
- package/dist/core/sandbox/network-refusal.d.ts +24 -0
- package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
- package/dist/core/sandbox/network-refusal.js +72 -0
- package/dist/core/sandbox/network-refusal.js.map +1 -0
- package/dist/core/sandbox/policy.d.ts +40 -0
- package/dist/core/sandbox/policy.d.ts.map +1 -0
- package/dist/core/sandbox/policy.js +55 -0
- package/dist/core/sandbox/policy.js.map +1 -0
- package/dist/core/sandbox/profiles.d.ts +25 -0
- package/dist/core/sandbox/profiles.d.ts.map +1 -0
- package/dist/core/sandbox/profiles.js +23 -0
- package/dist/core/sandbox/profiles.js.map +1 -0
- package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
- package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-client.js +86 -0
- package/dist/core/sandbox/rpc/command-client.js.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
- package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/command-proxy.js +120 -0
- package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
- package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-client.js +170 -0
- package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
- package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/credential-proxy.js +287 -0
- package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
- package/dist/core/sandbox/rpc/framing.d.ts +35 -0
- package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/framing.js +114 -0
- package/dist/core/sandbox/rpc/framing.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts +40 -0
- package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js +152 -0
- package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +48 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js +142 -0
- package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
- package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
- package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor-temp.js +21 -0
- package/dist/core/sandbox/supervisor-temp.js.map +1 -0
- package/dist/core/sandbox/supervisor.d.ts +51 -0
- package/dist/core/sandbox/supervisor.d.ts.map +1 -0
- package/dist/core/sandbox/supervisor.js +18 -0
- package/dist/core/sandbox/supervisor.js.map +1 -0
- package/dist/core/sandbox/terminal-handoff.d.ts +45 -0
- package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-handoff.js +176 -0
- package/dist/core/sandbox/terminal-handoff.js.map +1 -0
- package/dist/core/sandbox/terminal-size.d.ts +40 -0
- package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
- package/dist/core/sandbox/terminal-size.js +105 -0
- package/dist/core/sandbox/terminal-size.js.map +1 -0
- package/dist/core/sandbox/violations.d.ts +23 -0
- package/dist/core/sandbox/violations.d.ts.map +1 -0
- package/dist/core/sandbox/violations.js +30 -0
- package/dist/core/sandbox/violations.js.map +1 -0
- package/dist/core/sdk.d.ts +32 -41
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +174 -6
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-export.d.ts +4 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +32 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-lease.d.ts +31 -0
- package/dist/core/session-lease.d.ts.map +1 -0
- package/dist/core/session-lease.js +138 -0
- package/dist/core/session-lease.js.map +1 -0
- package/dist/core/session-manager.d.ts +58 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +92 -7
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-share.d.ts +31 -0
- package/dist/core/session-share.d.ts.map +1 -0
- package/dist/core/session-share.js +45 -0
- package/dist/core/session-share.js.map +1 -0
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +99 -9
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +141 -36
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts +19 -7
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +128 -52
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +6 -2
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts +2 -2
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +59 -68
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/timings.d.ts +1 -1
- package/dist/core/timings.d.ts.map +1 -1
- package/dist/core/timings.js +3 -2
- package/dist/core/timings.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +25 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +49 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/bash-command-segments.d.ts +22 -0
- package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
- package/dist/core/tools/bash-command-segments.js +128 -0
- package/dist/core/tools/bash-command-segments.js.map +1 -0
- package/dist/core/tools/bash.d.ts +40 -4
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +183 -32
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/contract-snapshot.d.ts +58 -0
- package/dist/core/tools/contract-snapshot.d.ts.map +1 -0
- package/dist/core/tools/contract-snapshot.js +51 -0
- package/dist/core/tools/contract-snapshot.js.map +1 -0
- package/dist/core/tools/contract.d.ts +185 -0
- package/dist/core/tools/contract.d.ts.map +1 -0
- package/dist/core/tools/contract.js +50 -0
- package/dist/core/tools/contract.js.map +1 -0
- package/dist/core/tools/delegate.d.ts +38 -0
- package/dist/core/tools/delegate.d.ts.map +1 -0
- package/dist/core/tools/delegate.js +90 -0
- package/dist/core/tools/delegate.js.map +1 -0
- package/dist/core/tools/diagnostics.d.ts +39 -0
- package/dist/core/tools/diagnostics.d.ts.map +1 -0
- package/dist/core/tools/diagnostics.js +62 -0
- package/dist/core/tools/diagnostics.js.map +1 -0
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts +12 -2
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +61 -10
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.d.ts +2 -2
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +13 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +4 -2
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +47 -10
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +60 -6
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +148 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +4 -2
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js +17 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/lsp.d.ts +62 -0
- package/dist/core/tools/lsp.d.ts.map +1 -0
- package/dist/core/tools/lsp.js +216 -0
- package/dist/core/tools/lsp.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +1 -1
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/path-permission.d.ts +18 -0
- package/dist/core/tools/path-permission.d.ts.map +1 -0
- package/dist/core/tools/path-permission.js +28 -0
- package/dist/core/tools/path-permission.js.map +1 -0
- package/dist/core/tools/plan-present.d.ts +27 -0
- package/dist/core/tools/plan-present.d.ts.map +1 -0
- package/dist/core/tools/plan-present.js +55 -0
- package/dist/core/tools/plan-present.js.map +1 -0
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts +2 -2
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +14 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/skill-search.d.ts +32 -0
- package/dist/core/tools/skill-search.d.ts.map +1 -0
- package/dist/core/tools/skill-search.js +56 -0
- package/dist/core/tools/skill-search.js.map +1 -0
- package/dist/core/tools/test.d.ts +34 -0
- package/dist/core/tools/test.d.ts.map +1 -0
- package/dist/core/tools/test.js +75 -0
- package/dist/core/tools/test.js.map +1 -0
- package/dist/core/tools/todo-write.d.ts +31 -0
- package/dist/core/tools/todo-write.d.ts.map +1 -0
- package/dist/core/tools/todo-write.js +51 -0
- package/dist/core/tools/todo-write.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
- package/dist/core/tools/tool-definition-wrapper.js +11 -1
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
- package/dist/core/tools/tool-schema.d.ts +22 -0
- package/dist/core/tools/tool-schema.d.ts.map +1 -0
- package/dist/core/tools/tool-schema.js +45 -0
- package/dist/core/tools/tool-schema.js.map +1 -0
- package/dist/core/tools/web-fetch.d.ts +26 -0
- package/dist/core/tools/web-fetch.d.ts.map +1 -0
- package/dist/core/tools/web-fetch.js +62 -0
- package/dist/core/tools/web-fetch.js.map +1 -0
- package/dist/core/tools/web-search-exa.d.ts +33 -0
- package/dist/core/tools/web-search-exa.d.ts.map +1 -0
- package/dist/core/tools/web-search-exa.js +128 -0
- package/dist/core/tools/web-search-exa.js.map +1 -0
- package/dist/core/tools/web-search.d.ts +26 -0
- package/dist/core/tools/web-search.d.ts.map +1 -0
- package/dist/core/tools/web-search.js +53 -0
- package/dist/core/tools/web-search.js.map +1 -0
- package/dist/core/tools/write.d.ts +16 -2
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +48 -2
- package/dist/core/tools/write.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/usage-performance-store.d.ts +56 -0
- package/dist/core/usage-performance-store.d.ts.map +1 -0
- package/dist/core/usage-performance-store.js +85 -0
- package/dist/core/usage-performance-store.js.map +1 -0
- package/dist/core/web-search-provider.d.ts +64 -0
- package/dist/core/web-search-provider.d.ts.map +1 -0
- package/dist/core/web-search-provider.js +149 -0
- package/dist/core/web-search-provider.js.map +1 -0
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +5 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +87 -22
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +6 -5
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +29 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +182 -23
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +7 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +20 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +9 -6
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +72 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +338 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js +11 -2
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +24 -6
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
- package/dist/modes/interactive/components/first-time-setup.js +17 -41
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
- package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
- package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-use-hints.js +22 -0
- package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +31 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +205 -24
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +2 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +2 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +31 -9
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +236 -96
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +25 -5
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +226 -58
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/splash-header.d.ts +66 -0
- package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
- package/dist/modes/interactive/components/splash-header.js +180 -0
- package/dist/modes/interactive/components/splash-header.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +74 -17
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +28 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +144 -35
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
- package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-panel.js +130 -0
- package/dist/modes/interactive/components/tool-panel.js.map +1 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +60 -10
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +90 -5
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +729 -206
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +34 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/apex.json +93 -0
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/light.json +3 -1
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +25 -11
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +11 -3
- package/dist/modes/interactive/theme/theme.d.ts +14 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +65 -10
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +18 -12
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +22 -5
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +2 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/package-manager-cli.d.ts +1 -0
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +210 -19
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/rpc-entry.d.ts.map +1 -1
- package/dist/rpc-entry.js +2 -1
- package/dist/rpc-entry.js.map +1 -1
- package/dist/testing/replay/metrics.d.ts +24 -0
- package/dist/testing/replay/metrics.d.ts.map +1 -0
- package/dist/testing/replay/metrics.js +35 -0
- package/dist/testing/replay/metrics.js.map +1 -0
- package/dist/testing/replay/recorded-provider.d.ts +10 -0
- package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
- package/dist/testing/replay/recorded-provider.js +108 -0
- package/dist/testing/replay/recorded-provider.js.map +1 -0
- package/dist/testing/replay/runner.d.ts +56 -0
- package/dist/testing/replay/runner.d.ts.map +1 -0
- package/dist/testing/replay/runner.js +406 -0
- package/dist/testing/replay/runner.js.map +1 -0
- package/dist/themes/apex-logo.d.ts +41 -0
- package/dist/themes/apex-logo.d.ts.map +1 -0
- package/dist/themes/apex-logo.js +35 -0
- package/dist/themes/apex-logo.js.map +1 -0
- package/dist/utils/apex-code-user-agent.d.ts +2 -0
- package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
- package/dist/utils/apex-code-user-agent.js +5 -0
- package/dist/utils/apex-code-user-agent.js.map +1 -0
- package/dist/utils/changelog.d.ts +7 -2
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +82 -13
- package/dist/utils/changelog.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +1 -1
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/shell.d.ts +3 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +17 -5
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/syntax-highlight.d.ts +1 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -1
- package/dist/utils/syntax-highlight.js +60 -1
- package/dist/utils/syntax-highlight.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +70 -2
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +280 -124
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/version-check.d.ts +7 -5
- package/dist/utils/version-check.d.ts.map +1 -1
- package/dist/utils/version-check.js +27 -14
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/windows-self-update.d.ts.map +1 -1
- package/dist/utils/windows-self-update.js +1 -1
- package/dist/utils/windows-self-update.js.map +1 -1
- package/docs/compaction.md +41 -24
- package/docs/containerization.md +24 -21
- package/docs/custom-provider.md +15 -12
- package/docs/development.md +11 -11
- package/docs/environment-variables.md +37 -77
- package/docs/extensions.md +68 -54
- package/docs/index.md +12 -24
- package/docs/json.md +18 -11
- package/docs/keybindings.md +18 -12
- package/docs/llama-cpp.md +9 -7
- package/docs/models.md +17 -11
- package/docs/packages.md +47 -43
- package/docs/prompt-templates.md +3 -3
- package/docs/providers.md +10 -10
- package/docs/quickstart.md +49 -32
- package/docs/rpc.md +35 -18
- package/docs/sdk.md +20 -6
- package/docs/security.md +18 -18
- package/docs/session-format.md +9 -9
- package/docs/sessions.md +13 -13
- package/docs/settings.md +83 -16
- package/docs/shell-aliases.md +1 -1
- package/docs/skills.md +66 -15
- package/docs/terminal-setup.md +44 -19
- package/docs/termux.md +4 -4
- package/docs/themes.md +32 -11
- package/docs/tmux.md +4 -4
- package/docs/tui.md +5 -5
- package/docs/usage.md +61 -53
- package/docs/windows.md +24 -2
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +2 -2
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/git-checkpoint.ts +24 -36
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/README.md +3 -1
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +27 -4
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +435 -452
- package/package.json +17 -18
- package/dist/core/telemetry.d.ts +0 -3
- package/dist/core/telemetry.d.ts.map +0 -1
- package/dist/core/telemetry.js +0 -9
- package/dist/core/telemetry.js.map +0 -1
- package/dist/server/create-harness.d.ts +0 -26
- package/dist/server/create-harness.d.ts.map +0 -1
- package/dist/server/create-harness.js +0 -104
- package/dist/server/create-harness.js.map +0 -1
- package/dist/utils/pi-user-agent.d.ts +0 -2
- package/dist/utils/pi-user-agent.d.ts.map +0 -1
- package/dist/utils/pi-user-agent.js +0 -5
- package/dist/utils/pi-user-agent.js.map +0 -1
|
@@ -14,17 +14,22 @@ import { APP_NAME, APP_TITLE, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebu
|
|
|
14
14
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
15
15
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
16
16
|
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
17
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
18
|
+
import { getApexEnvironment } from "../../core/environment.js";
|
|
17
19
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
18
20
|
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
19
21
|
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
20
22
|
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
21
23
|
import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScopeFromModels, } from "../../core/model-resolver.js";
|
|
22
24
|
import { CredentialSynchronizationError } from "../../core/model-runtime.js";
|
|
25
|
+
import { aggregateUsagePerformance } from "../../core/observability/aggregate.js";
|
|
23
26
|
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
27
|
+
import { observeTerminalHandoff, TERMINAL_HANDOFF_PATH_VARIABLE } from "../../core/sandbox/terminal-handoff.js";
|
|
24
28
|
import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
|
|
25
29
|
import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
|
|
30
|
+
import { formatShareUnavailableMessage, publishSessionShare } from "../../core/session-share.js";
|
|
31
|
+
import { slugifySkillCommandName } from "../../core/skills.js";
|
|
26
32
|
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
27
|
-
import { isInstallTelemetryEnabled } from "../../core/telemetry.js";
|
|
28
33
|
import { hasTrustRequiringProjectResources, ProjectTrustStore } from "../../core/trust-manager.js";
|
|
29
34
|
import { getUsageCostBreakdown } from "../../core/usage-totals.js";
|
|
30
35
|
import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
|
|
@@ -33,10 +38,10 @@ import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipb
|
|
|
33
38
|
import { parseGitUrl } from "../../utils/git.js";
|
|
34
39
|
import { openBrowser } from "../../utils/open-browser.js";
|
|
35
40
|
import { getCwdRelativePath } from "../../utils/paths.js";
|
|
36
|
-
import { getPiUserAgent } from "../../utils/pi-user-agent.js";
|
|
37
41
|
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
42
|
+
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
38
43
|
import { ensureTool } from "../../utils/tools-manager.js";
|
|
39
|
-
import {
|
|
44
|
+
import { checkForNewApexCodeVersion } from "../../utils/version-check.js";
|
|
40
45
|
import { ArminComponent } from "./components/armin.js";
|
|
41
46
|
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
42
47
|
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
@@ -52,6 +57,7 @@ import { EarendilAnnouncementComponent } from "./components/earendil-announcemen
|
|
|
52
57
|
import { ExtensionEditorComponent } from "./components/extension-editor.js";
|
|
53
58
|
import { ExtensionInputComponent } from "./components/extension-input.js";
|
|
54
59
|
import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
60
|
+
import { FirstUseHints } from "./components/first-use-hints.js";
|
|
55
61
|
import { FooterComponent, formatTokens } from "./components/footer.js";
|
|
56
62
|
import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
57
63
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
@@ -60,15 +66,18 @@ import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
|
60
66
|
import { formatAuthSelectorProviderType, OAuthSelectorComponent, } from "./components/oauth-selector.js";
|
|
61
67
|
import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
|
|
62
68
|
import { SessionSelectorComponent } from "./components/session-selector.js";
|
|
63
|
-
import { SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
69
|
+
import { PERMISSION_MODE_OVERRIDE_HINTS, SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
64
70
|
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
71
|
+
import { ApexSplashHeader } from "./components/splash-header.js";
|
|
65
72
|
import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
|
|
73
|
+
import { ThinkingSelectorComponent } from "./components/thinking-selector.js";
|
|
66
74
|
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
67
75
|
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
68
76
|
import { TrustSelectorComponent } from "./components/trust-selector.js";
|
|
69
77
|
import { UserMessageComponent } from "./components/user-message.js";
|
|
70
78
|
import { UserMessageSelectorComponent } from "./components/user-message-selector.js";
|
|
71
79
|
import { editInExternalEditor } from "./external-editor.js";
|
|
80
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
72
81
|
import { getModelSearchText } from "./model-search.js";
|
|
73
82
|
import { getAvailableThemes, getAvailableThemesWithPaths, getEditorTheme, getMarkdownTheme, getThemeByName, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
74
83
|
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
@@ -90,6 +99,9 @@ class ExpandableText extends Text {
|
|
|
90
99
|
function isCustomSessionEntry(item) {
|
|
91
100
|
return "type" in item && item.type === "custom";
|
|
92
101
|
}
|
|
102
|
+
function isCompactionCostNotice(item) {
|
|
103
|
+
return "type" in item && item.type === "compaction_cost";
|
|
104
|
+
}
|
|
93
105
|
const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
|
|
94
106
|
function isDeadTerminalError(error) {
|
|
95
107
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
@@ -129,6 +141,11 @@ export function formatResumeCommand(sessionManager) {
|
|
|
129
141
|
function hasDefaultModelProvider(providerId) {
|
|
130
142
|
return providerId in defaultModelPerProvider;
|
|
131
143
|
}
|
|
144
|
+
function llamaCppPostLoginGuidance(actionLabel, loadedModelCount) {
|
|
145
|
+
return loadedModelCount === 0
|
|
146
|
+
? `${actionLabel}. No llama.cpp models are loaded. Use /llama to load a model, then /model to select it.`
|
|
147
|
+
: `${actionLabel}. Use /model to select a loaded llama.cpp model, or /llama to manage models.`;
|
|
148
|
+
}
|
|
132
149
|
const AUTH_TYPE_ORDER = { oauth: 0, api_key: 1 };
|
|
133
150
|
function createFuzzyAutocompleteItems(items, prefix, getSearchText, toAutocompleteItem) {
|
|
134
151
|
const filtered = fuzzyFilter(items, prefix, getSearchText);
|
|
@@ -169,9 +186,21 @@ function formatLoginProviderCompletionDescription(provider) {
|
|
|
169
186
|
export function createInteractiveTui(options) {
|
|
170
187
|
const terminal = options.terminal ?? new ProcessTerminal();
|
|
171
188
|
if (options.tuiMode === "fullscreen") {
|
|
189
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
172
190
|
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
191
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
192
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
173
193
|
openUrl: openBrowser,
|
|
174
194
|
onRightClickPaste: options.onRightClickPaste,
|
|
195
|
+
copySelection: async (text) => {
|
|
196
|
+
try {
|
|
197
|
+
await copyToClipboard(text);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
},
|
|
175
204
|
});
|
|
176
205
|
}
|
|
177
206
|
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
@@ -209,6 +238,8 @@ export function createInteractiveTuiReference(getTui) {
|
|
|
209
238
|
}
|
|
210
239
|
export class InteractiveMode {
|
|
211
240
|
runtimeHost;
|
|
241
|
+
/** Settled once the most recent /settings permission-mode write has hit disk. */
|
|
242
|
+
pendingPermissionModeWrite;
|
|
212
243
|
renderer;
|
|
213
244
|
ui;
|
|
214
245
|
mainScreenRenderState;
|
|
@@ -231,6 +262,14 @@ export class InteractiveMode {
|
|
|
231
262
|
footer;
|
|
232
263
|
footerContainer;
|
|
233
264
|
footerDataProvider;
|
|
265
|
+
/**
|
|
266
|
+
* Whether the startup screen is showing its counted inventory line.
|
|
267
|
+
*
|
|
268
|
+
* Warnings are only allowed to collapse into that count when the count is
|
|
269
|
+
* actually on screen. Under a quiet startup there is no splash header, so
|
|
270
|
+
* collapsing them would hide conflicts entirely rather than summarise them.
|
|
271
|
+
*/
|
|
272
|
+
startupInventoryVisible = false;
|
|
234
273
|
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
235
274
|
keybindings;
|
|
236
275
|
version;
|
|
@@ -253,6 +292,7 @@ export class InteractiveMode {
|
|
|
253
292
|
// Status line tracking (for mutating immediately-sequential status updates)
|
|
254
293
|
lastStatusSpacer = undefined;
|
|
255
294
|
lastStatusText = undefined;
|
|
295
|
+
managedToolStatusStarted = false;
|
|
256
296
|
// Streaming message tracking
|
|
257
297
|
streamingComponent = undefined;
|
|
258
298
|
streamingMessage = undefined;
|
|
@@ -274,8 +314,14 @@ export class InteractiveMode {
|
|
|
274
314
|
signalCleanupHandlers = [];
|
|
275
315
|
// Track if editor is in bash mode (text starts with !)
|
|
276
316
|
isBashMode = false;
|
|
317
|
+
firstUseHints;
|
|
277
318
|
// Track current bash execution component
|
|
278
319
|
bashComponent = undefined;
|
|
320
|
+
// Reentrancy guard: session.isBashRunning only turns true inside
|
|
321
|
+
// executeBash, so a second `!cmd` submitted during the extension
|
|
322
|
+
// emitUserBash await would otherwise overwrite the in-flight component
|
|
323
|
+
// and orphan the first command's output callback.
|
|
324
|
+
isHandlingBashCommand = false;
|
|
279
325
|
// Track pending bash components (shown in pending area, moved to chat on submit)
|
|
280
326
|
pendingBashComponents = [];
|
|
281
327
|
// Auto-compaction state
|
|
@@ -323,6 +369,14 @@ export class InteractiveMode {
|
|
|
323
369
|
get settingsManager() {
|
|
324
370
|
return this.session.settingsManager;
|
|
325
371
|
}
|
|
372
|
+
offerFirstUseHint(id) {
|
|
373
|
+
this.firstUseHints ??= new FirstUseHints(this.settingsManager.getFirstUseHints());
|
|
374
|
+
const hint = this.firstUseHints.offer(id);
|
|
375
|
+
if (!hint)
|
|
376
|
+
return;
|
|
377
|
+
this.settingsManager.setFirstUseHints(this.firstUseHints.getSeen());
|
|
378
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", hint), this.outputPad, 0));
|
|
379
|
+
}
|
|
326
380
|
constructor(runtimeHost, options = {}) {
|
|
327
381
|
this.runtimeHost = runtimeHost;
|
|
328
382
|
const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
|
|
@@ -333,6 +387,7 @@ export class InteractiveMode {
|
|
|
333
387
|
});
|
|
334
388
|
this.runtimeHost.setRebindSession(async () => {
|
|
335
389
|
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
390
|
+
await this.themeController.applyFromSettings();
|
|
336
391
|
});
|
|
337
392
|
this.version = VERSION;
|
|
338
393
|
this.renderer = createInteractiveTui({
|
|
@@ -361,12 +416,20 @@ export class InteractiveMode {
|
|
|
361
416
|
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
362
417
|
paddingX: editorPaddingX,
|
|
363
418
|
autocompleteMaxVisible,
|
|
419
|
+
promptPrefix: "› ",
|
|
420
|
+
promptColor: (text) => theme.fg("accent", text),
|
|
421
|
+
placeholder: "Ask anything",
|
|
422
|
+
placeholderColor: (text) => theme.fg("dim", text),
|
|
423
|
+
commandColor: (text) => theme.fg("accent", text),
|
|
424
|
+
surfaceColor: (text) => theme.bg("userMessageBg", text),
|
|
425
|
+
autocompleteRule: (width) => theme.fg("borderMuted", (this.settingsManager.getSymbolPreset() === "ascii" ? "-" : "┄").repeat(Math.max(1, width))),
|
|
426
|
+
autocompleteFooter: () => this.buildAutocompleteFooter(),
|
|
364
427
|
});
|
|
365
428
|
this.editor = this.defaultEditor;
|
|
366
429
|
this.editorContainer = new Container();
|
|
367
430
|
this.editorContainer.addChild(this.editor);
|
|
368
431
|
this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
|
|
369
|
-
this.footer = new FooterComponent(this.session, this.footerDataProvider);
|
|
432
|
+
this.footer = new FooterComponent(this.session, this.footerDataProvider, this.settingsManager);
|
|
370
433
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
371
434
|
this.footerContainer = new Container();
|
|
372
435
|
this.footerContainer.addChild(this.footer);
|
|
@@ -375,7 +438,12 @@ export class InteractiveMode {
|
|
|
375
438
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
376
439
|
// Register themes from resource loader and initialize
|
|
377
440
|
setRegisteredThemes(this.session.resourceLoader.getThemes().themes);
|
|
378
|
-
this.themeController = new InteractiveThemeController(this.ui,
|
|
441
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
442
|
+
getSettingsManager: () => this.settingsManager,
|
|
443
|
+
showError: (message) => this.showError(message),
|
|
444
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
445
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
446
|
+
});
|
|
379
447
|
}
|
|
380
448
|
getAutocompleteSourceTag(sourceInfo) {
|
|
381
449
|
if (!sourceInfo) {
|
|
@@ -416,8 +484,21 @@ export class InteractiveMode {
|
|
|
416
484
|
path: command.sourceInfo.path,
|
|
417
485
|
}));
|
|
418
486
|
}
|
|
419
|
-
|
|
420
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Every *registered* command: builtin, prompt template, extension, and skill.
|
|
489
|
+
*
|
|
490
|
+
* One function rather than two because `/help` renders this list and autocomplete
|
|
491
|
+
* completes it. Built in two places they would disagree the first time either grew a
|
|
492
|
+
* source, and the disagreement would show up as a command a user can complete but not
|
|
493
|
+
* find, or find but not run.
|
|
494
|
+
*
|
|
495
|
+
* A few commands are dispatched without being registered -- `/debug`, and the
|
|
496
|
+
* `/arminsayshi` and `/dementedelves` easter eggs. They are absent here on purpose,
|
|
497
|
+
* which is also why autocomplete has never offered them. "Registered" is the boundary
|
|
498
|
+
* both surfaces share; listing them in `/help` would advertise what completion cannot
|
|
499
|
+
* complete.
|
|
500
|
+
*/
|
|
501
|
+
collectSessionCommands() {
|
|
421
502
|
const slashCommands = BUILTIN_SLASH_COMMANDS.map((command) => ({
|
|
422
503
|
name: command.name,
|
|
423
504
|
description: command.description,
|
|
@@ -445,6 +526,15 @@ export class InteractiveMode {
|
|
|
445
526
|
}));
|
|
446
527
|
};
|
|
447
528
|
}
|
|
529
|
+
const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
|
|
530
|
+
if (thinkingCommand) {
|
|
531
|
+
thinkingCommand.getArgumentCompletions = (prefix) => {
|
|
532
|
+
return createFuzzyAutocompleteItems(this.session.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
|
|
533
|
+
value: level,
|
|
534
|
+
label: level,
|
|
535
|
+
}));
|
|
536
|
+
};
|
|
537
|
+
}
|
|
448
538
|
const loginCommand = slashCommands.find((command) => command.name === "login");
|
|
449
539
|
if (loginCommand) {
|
|
450
540
|
loginCommand.getArgumentCompletions = (prefix) => {
|
|
@@ -477,7 +567,7 @@ export class InteractiveMode {
|
|
|
477
567
|
const skillCommandList = [];
|
|
478
568
|
if (this.settingsManager.getEnableSkillCommands()) {
|
|
479
569
|
for (const skill of this.session.resourceLoader.getSkills().skills) {
|
|
480
|
-
const commandName = `skill:${skill.name}`;
|
|
570
|
+
const commandName = `skill:${slugifySkillCommandName(skill.name)}`;
|
|
481
571
|
this.skillCommands.set(commandName, skill.filePath);
|
|
482
572
|
skillCommandList.push({
|
|
483
573
|
name: commandName,
|
|
@@ -485,7 +575,10 @@ export class InteractiveMode {
|
|
|
485
575
|
});
|
|
486
576
|
}
|
|
487
577
|
}
|
|
488
|
-
return
|
|
578
|
+
return [...slashCommands, ...templateCommands, ...extensionCommands, ...skillCommandList];
|
|
579
|
+
}
|
|
580
|
+
createBaseAutocompleteProvider() {
|
|
581
|
+
return new CombinedAutocompleteProvider(this.collectSessionCommands(), this.sessionManager.getCwd(), this.fdPath);
|
|
489
582
|
}
|
|
490
583
|
setupAutocompleteProvider() {
|
|
491
584
|
let provider = this.createBaseAutocompleteProvider();
|
|
@@ -516,7 +609,7 @@ export class InteractiveMode {
|
|
|
516
609
|
}
|
|
517
610
|
this.chatContainer.addChild(new DynamicBorder());
|
|
518
611
|
if (this.settingsManager.getCollapseChangelog()) {
|
|
519
|
-
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
|
612
|
+
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?)\]?/);
|
|
520
613
|
const latestVersion = versionMatch ? versionMatch[1] : this.version;
|
|
521
614
|
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
|
|
522
615
|
this.chatContainer.addChild(new Text(condensedText, 1, 0));
|
|
@@ -538,14 +631,14 @@ export class InteractiveMode {
|
|
|
538
631
|
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
539
632
|
}
|
|
540
633
|
}
|
|
541
|
-
stopInteractiveTui() {
|
|
542
|
-
if (this.renderer.mode === "fullscreen") {
|
|
634
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
635
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
543
636
|
while (this.renderer.hasOverlayEntries)
|
|
544
637
|
this.renderer.hideOverlay();
|
|
545
638
|
this.switchTuiMode("regular", false, false);
|
|
546
639
|
this.renderer.renderNow();
|
|
547
640
|
}
|
|
548
|
-
this.ui.stop();
|
|
641
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
549
642
|
}
|
|
550
643
|
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
551
644
|
const previousUi = this.renderer;
|
|
@@ -602,10 +695,9 @@ export class InteractiveMode {
|
|
|
602
695
|
this.registerSignalHandlers();
|
|
603
696
|
// Load changelog (only show new entries, skip for resumed sessions)
|
|
604
697
|
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
605
|
-
//
|
|
606
|
-
//
|
|
607
|
-
|
|
608
|
-
this.fdPath = fdPath;
|
|
698
|
+
// Resolve the effective mode before mounting or focusing components. Both
|
|
699
|
+
// operations can schedule a frame even before the renderer is started.
|
|
700
|
+
await this.refreshFooterPermissionMode();
|
|
609
701
|
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
610
702
|
const modelList = this.session.scopedModels
|
|
611
703
|
.map((sm) => {
|
|
@@ -649,18 +741,23 @@ export class InteractiveMode {
|
|
|
649
741
|
this.widgetContainerBelow,
|
|
650
742
|
this.footerContainer,
|
|
651
743
|
]);
|
|
744
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
745
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
746
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
747
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
652
748
|
this.ui.setFocus(this.editor);
|
|
653
|
-
this.setupKeyHandlers();
|
|
654
|
-
this.setupEditorSubmitHandler();
|
|
655
749
|
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
656
750
|
this.ui.start();
|
|
657
751
|
this.isInitialized = true;
|
|
658
752
|
await this.themeController.applyFromSettings();
|
|
659
|
-
//
|
|
753
|
+
// Brand splash: mark on the left, runtime metadata on the right. The model
|
|
754
|
+
// and cwd are read through live getters, so they fill in once the session
|
|
755
|
+
// binds in rebindCurrentSession below.
|
|
660
756
|
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
|
|
661
|
-
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
|
|
662
757
|
// Build startup instructions using keybinding hint helpers
|
|
663
758
|
const hint = (keybinding, description) => keyHint(keybinding, description);
|
|
759
|
+
// The full cheatsheet is verbose-only. The default screen leads with the
|
|
760
|
+
// mark and the runtime facts that actually differ between launches.
|
|
664
761
|
const expandedInstructions = [
|
|
665
762
|
hint("app.interrupt", "to interrupt"),
|
|
666
763
|
hint("app.clear", "to clear"),
|
|
@@ -682,27 +779,45 @@ export class InteractiveMode {
|
|
|
682
779
|
hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
|
|
683
780
|
rawKeyHint("drop files", "to attach"),
|
|
684
781
|
].join("\n");
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
782
|
+
this.builtInHeader = new ApexSplashHeader(this.version, () => this.session.state.model?.id, () => this.session.sessionManager.getCwd(), this.options.verbose ? expandedInstructions : undefined, {
|
|
783
|
+
topPadding: true,
|
|
784
|
+
getSymbolPreset: () => this.settingsManager.getSymbolPreset(),
|
|
785
|
+
getExtraMetadata: () => {
|
|
786
|
+
// Which branch the session is rooted on differs between launches
|
|
787
|
+
// and is not derivable from the prompt, so it belongs here. The
|
|
788
|
+
// tray carries it too, but drops it first when space is tight.
|
|
789
|
+
const branch = this.footerDataProvider.getGitBranch();
|
|
790
|
+
return branch ? [{ label: "branch", value: branch }] : [];
|
|
791
|
+
},
|
|
792
|
+
getInventory: () => this.buildStartupInventory(),
|
|
793
|
+
inventoryHint: "/resources",
|
|
794
|
+
getShortcuts: () => this.buildStartupShortcuts(),
|
|
795
|
+
getHint: () => "Apex Code can explain its own features and look up its docs.",
|
|
796
|
+
});
|
|
695
797
|
// Setup UI layout
|
|
696
|
-
this.
|
|
798
|
+
this.startupInventoryVisible = true;
|
|
697
799
|
this.headerContainer.addChild(this.builtInHeader);
|
|
698
800
|
this.headerContainer.addChild(new Spacer(1));
|
|
699
801
|
}
|
|
700
802
|
else {
|
|
701
803
|
// Minimal header when silenced
|
|
804
|
+
this.startupInventoryVisible = false;
|
|
702
805
|
this.builtInHeader = new Text("", 0, 0);
|
|
703
806
|
this.headerContainer.addChild(this.builtInHeader);
|
|
704
807
|
}
|
|
705
808
|
this.ui.requestRender();
|
|
809
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
810
|
+
// so slow downloads do not make startup appear frozen.
|
|
811
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
812
|
+
const [fdPath] = await Promise.all([
|
|
813
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
814
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
815
|
+
]);
|
|
816
|
+
this.fdPath = fdPath;
|
|
817
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
818
|
+
this.setupKeyHandlers();
|
|
819
|
+
this.setupEditorSubmitHandler();
|
|
820
|
+
this.ui.requestRender();
|
|
706
821
|
// Initialize extensions first so resources are shown before messages
|
|
707
822
|
await this.rebindCurrentSession();
|
|
708
823
|
// Render initial messages AFTER showing loaded resources
|
|
@@ -719,6 +834,14 @@ export class InteractiveMode {
|
|
|
719
834
|
});
|
|
720
835
|
// Initialize available provider count for footer display
|
|
721
836
|
await this.updateAvailableProviderCount();
|
|
837
|
+
// Flush the completed startup state before loading the remaining syntax grammars.
|
|
838
|
+
this.ui.renderNow();
|
|
839
|
+
void loadAllHighlightLanguages().then(() => {
|
|
840
|
+
if (!this.isInitialized)
|
|
841
|
+
return;
|
|
842
|
+
this.ui.invalidate();
|
|
843
|
+
this.ui.requestRender();
|
|
844
|
+
});
|
|
722
845
|
}
|
|
723
846
|
/**
|
|
724
847
|
* Update terminal title with session name and cwd.
|
|
@@ -739,17 +862,16 @@ export class InteractiveMode {
|
|
|
739
862
|
*/
|
|
740
863
|
async run() {
|
|
741
864
|
await this.init();
|
|
742
|
-
if (!
|
|
865
|
+
if (!getApexEnvironment("APEX_CODE_OFFLINE")) {
|
|
743
866
|
const controller = new AbortController();
|
|
744
867
|
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
745
|
-
void this.session.modelRuntime
|
|
746
|
-
.refresh({ signal: controller.signal })
|
|
868
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
747
869
|
.then(() => this.updateAvailableProviderCount())
|
|
748
870
|
.catch(() => { })
|
|
749
871
|
.finally(() => clearTimeout(timeout));
|
|
750
872
|
}
|
|
751
873
|
// Start version check asynchronously
|
|
752
|
-
|
|
874
|
+
checkForNewApexCodeVersion(this.version).then((newRelease) => {
|
|
753
875
|
if (newRelease) {
|
|
754
876
|
this.showNewVersionNotification(newRelease);
|
|
755
877
|
}
|
|
@@ -775,7 +897,18 @@ export class InteractiveMode {
|
|
|
775
897
|
}
|
|
776
898
|
});
|
|
777
899
|
// Show startup warnings
|
|
778
|
-
const { migratedProviders, modelFallbackMessage, initialMessage, initialImages, initialMessages } = this.options;
|
|
900
|
+
const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
|
|
901
|
+
for (const diagnostic of startupDiagnostics ?? []) {
|
|
902
|
+
if (diagnostic.type === "error") {
|
|
903
|
+
this.showError(diagnostic.message);
|
|
904
|
+
}
|
|
905
|
+
else if (diagnostic.type === "warning") {
|
|
906
|
+
this.showWarning(diagnostic.message);
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
this.showStatus(diagnostic.message);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
779
912
|
if (migratedProviders && migratedProviders.length > 0) {
|
|
780
913
|
this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
|
781
914
|
}
|
|
@@ -821,7 +954,7 @@ export class InteractiveMode {
|
|
|
821
954
|
}
|
|
822
955
|
}
|
|
823
956
|
async checkForPackageUpdates() {
|
|
824
|
-
if (
|
|
957
|
+
if (getApexEnvironment("APEX_CODE_OFFLINE")) {
|
|
825
958
|
return [];
|
|
826
959
|
}
|
|
827
960
|
try {
|
|
@@ -874,7 +1007,7 @@ export class InteractiveMode {
|
|
|
874
1007
|
return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
|
|
875
1008
|
}
|
|
876
1009
|
if (extendedKeysFormat === "xterm") {
|
|
877
|
-
return "tmux extended-keys-format is xterm.
|
|
1010
|
+
return "tmux extended-keys-format is xterm. Apex Code works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
|
|
878
1011
|
}
|
|
879
1012
|
return undefined;
|
|
880
1013
|
}
|
|
@@ -891,35 +1024,17 @@ export class InteractiveMode {
|
|
|
891
1024
|
const changelogPath = getChangelogPath();
|
|
892
1025
|
const entries = parseChangelog(changelogPath);
|
|
893
1026
|
if (!lastVersion) {
|
|
894
|
-
// Fresh install - record the version,
|
|
1027
|
+
// Fresh install - record the version, don't show changelog
|
|
895
1028
|
this.settingsManager.setLastChangelogVersion(VERSION);
|
|
896
|
-
this.reportInstallTelemetry(VERSION);
|
|
897
1029
|
return undefined;
|
|
898
1030
|
}
|
|
899
1031
|
const newEntries = getNewEntries(entries, lastVersion);
|
|
900
1032
|
if (newEntries.length > 0) {
|
|
901
1033
|
this.settingsManager.setLastChangelogVersion(VERSION);
|
|
902
|
-
this.reportInstallTelemetry(VERSION);
|
|
903
1034
|
return newEntries.map((e) => normalizeChangelogLinks(e.content, e)).join("\n\n");
|
|
904
1035
|
}
|
|
905
1036
|
return undefined;
|
|
906
1037
|
}
|
|
907
|
-
reportInstallTelemetry(version) {
|
|
908
|
-
if (process.env.PI_OFFLINE) {
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
if (!isInstallTelemetryEnabled(this.settingsManager)) {
|
|
912
|
-
return;
|
|
913
|
-
}
|
|
914
|
-
void fetch(`https://pi.dev/api/report-install?version=${encodeURIComponent(version)}`, {
|
|
915
|
-
headers: {
|
|
916
|
-
"User-Agent": getPiUserAgent(version),
|
|
917
|
-
},
|
|
918
|
-
signal: AbortSignal.timeout(5000),
|
|
919
|
-
})
|
|
920
|
-
.then(() => undefined)
|
|
921
|
-
.catch(() => undefined);
|
|
922
|
-
}
|
|
923
1038
|
getMarkdownThemeWithSettings() {
|
|
924
1039
|
return {
|
|
925
1040
|
...getMarkdownTheme(),
|
|
@@ -1216,7 +1331,7 @@ export class InteractiveMode {
|
|
|
1216
1331
|
showLoadedResources(options) {
|
|
1217
1332
|
// Resource rendering is idempotent; chat clears no longer clear this separate container.
|
|
1218
1333
|
this.loadedResourcesContainer.clear();
|
|
1219
|
-
const showListing = options?.force || this.options.verbose
|
|
1334
|
+
const showListing = options?.force || this.options.verbose;
|
|
1220
1335
|
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
|
1221
1336
|
if (!showListing && !showDiagnostics) {
|
|
1222
1337
|
return;
|
|
@@ -1333,43 +1448,105 @@ export class InteractiveMode {
|
|
|
1333
1448
|
addLoadedSection("Themes", themeCompactList, themeList);
|
|
1334
1449
|
}
|
|
1335
1450
|
}
|
|
1451
|
+
// Warnings collapse into the counted line when that line is on screen; the
|
|
1452
|
+
// full trace lives behind /resources. Errors always surface inline,
|
|
1453
|
+
// because an error means a resource did not load at all.
|
|
1336
1454
|
if (showDiagnostics) {
|
|
1337
|
-
const
|
|
1338
|
-
|
|
1339
|
-
const
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
const
|
|
1346
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(
|
|
1347
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1348
|
-
}
|
|
1349
|
-
const extensionDiagnostics = [];
|
|
1350
|
-
const extensionErrors = this.session.resourceLoader.getExtensions().errors;
|
|
1351
|
-
if (extensionErrors.length > 0) {
|
|
1352
|
-
for (const error of extensionErrors) {
|
|
1353
|
-
extensionDiagnostics.push({ type: "error", message: error.error, path: error.path });
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
const commandDiagnostics = this.session.extensionRunner.getCommandDiagnostics();
|
|
1357
|
-
extensionDiagnostics.push(...commandDiagnostics);
|
|
1358
|
-
extensionDiagnostics.push(...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner));
|
|
1359
|
-
const shortcutDiagnostics = this.session.extensionRunner.getShortcutDiagnostics();
|
|
1360
|
-
extensionDiagnostics.push(...shortcutDiagnostics);
|
|
1361
|
-
if (extensionDiagnostics.length > 0) {
|
|
1362
|
-
const warningLines = this.formatDiagnostics(extensionDiagnostics, sourceInfos);
|
|
1363
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Extension issues]")}\n${warningLines}`, 0, 0));
|
|
1364
|
-
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1365
|
-
}
|
|
1366
|
-
const themeDiagnostics = themesResult.diagnostics;
|
|
1367
|
-
if (themeDiagnostics.length > 0) {
|
|
1368
|
-
const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
|
|
1369
|
-
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
|
1455
|
+
const collapseWarnings = !showListing && this.startupInventoryVisible;
|
|
1456
|
+
for (const group of this.collectDiagnosticGroups()) {
|
|
1457
|
+
const visible = collapseWarnings
|
|
1458
|
+
? group.diagnostics.filter((diagnostic) => diagnostic.type === "error")
|
|
1459
|
+
: group.diagnostics;
|
|
1460
|
+
if (visible.length === 0)
|
|
1461
|
+
continue;
|
|
1462
|
+
const severity = visible.some((diagnostic) => diagnostic.type === "error") ? "error" : "warning";
|
|
1463
|
+
const body = this.formatDiagnostics(visible, sourceInfos);
|
|
1464
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg(severity, `[${group.label}]`)}\n${body}`, 0, 0));
|
|
1370
1465
|
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1371
1466
|
}
|
|
1372
1467
|
}
|
|
1468
|
+
// Counts on the startup screen are read at render time, so they fill in
|
|
1469
|
+
// once resources finish loading without an explicit refresh here.
|
|
1470
|
+
this.builtInHeader?.invalidate?.();
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Every startup diagnostic, grouped by the resource kind that produced it.
|
|
1474
|
+
*
|
|
1475
|
+
* One list feeds both the counted line in the header and the detailed
|
|
1476
|
+
* listing, so the two can never disagree about how many issues exist.
|
|
1477
|
+
*/
|
|
1478
|
+
collectDiagnosticGroups() {
|
|
1479
|
+
const extensionDiagnostics = [
|
|
1480
|
+
...this.session.resourceLoader
|
|
1481
|
+
.getExtensions()
|
|
1482
|
+
.errors.map((error) => ({ type: "error", message: error.error, path: error.path })),
|
|
1483
|
+
...this.session.extensionRunner.getCommandDiagnostics(),
|
|
1484
|
+
...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner),
|
|
1485
|
+
...this.session.extensionRunner.getShortcutDiagnostics(),
|
|
1486
|
+
];
|
|
1487
|
+
return [
|
|
1488
|
+
{ label: "Skill conflicts", diagnostics: this.session.resourceLoader.getSkills().diagnostics },
|
|
1489
|
+
{ label: "Prompt conflicts", diagnostics: this.session.resourceLoader.getPrompts().diagnostics },
|
|
1490
|
+
{ label: "Extension issues", diagnostics: extensionDiagnostics },
|
|
1491
|
+
{ label: "Theme conflicts", diagnostics: this.session.resourceLoader.getThemes().diagnostics },
|
|
1492
|
+
].filter((group) => group.diagnostics.length > 0);
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* The counted line in the ruled band under the mark.
|
|
1496
|
+
*
|
|
1497
|
+
* The startup screen used to print all 152 skill names across fifteen rows.
|
|
1498
|
+
* A count answers the only question that screen can usefully answer, which
|
|
1499
|
+
* is what got loaded; the names live behind /resources now.
|
|
1500
|
+
*/
|
|
1501
|
+
/**
|
|
1502
|
+
* The sigil row under the hint: how to reach commands, bash, and files.
|
|
1503
|
+
*
|
|
1504
|
+
* These are the only three characters that change what the composer does, so
|
|
1505
|
+
* they take the accent and everything around them stays quiet.
|
|
1506
|
+
*/
|
|
1507
|
+
/** Live keybindings under the autocomplete dropdown. */
|
|
1508
|
+
buildAutocompleteFooter() {
|
|
1509
|
+
const separator = theme.fg("borderMuted", this.settingsManager.getSymbolPreset() === "ascii" ? " - " : " · ");
|
|
1510
|
+
return [
|
|
1511
|
+
rawKeyHint(`${keyText("tui.select.up")}/${keyText("tui.select.down")}`, "move"),
|
|
1512
|
+
keyHint("tui.select.confirm", "select"),
|
|
1513
|
+
keyHint("tui.input.tab", "complete"),
|
|
1514
|
+
keyHint("tui.select.cancel", "dismiss"),
|
|
1515
|
+
].join(separator);
|
|
1516
|
+
}
|
|
1517
|
+
buildStartupShortcuts() {
|
|
1518
|
+
const entry = (sigil, label) => `${theme.fg("accent", sigil)} ${theme.fg("muted", label)}`;
|
|
1519
|
+
return [
|
|
1520
|
+
entry("/", "commands"),
|
|
1521
|
+
entry("!", "bash"),
|
|
1522
|
+
entry("@", "files"),
|
|
1523
|
+
`${theme.fg("dim", keyText("app.interrupt"))} ${theme.fg("muted", "interrupt")}`,
|
|
1524
|
+
].join(" ");
|
|
1525
|
+
}
|
|
1526
|
+
buildStartupInventory() {
|
|
1527
|
+
const counted = (total, singular) => `${total} ${singular}${total === 1 ? "" : "s"}`;
|
|
1528
|
+
const parts = [];
|
|
1529
|
+
const skills = this.session.resourceLoader.getSkills().skills.length;
|
|
1530
|
+
const extensions = this.session.resourceLoader.getExtensions().extensions.filter((e) => !e.hidden).length;
|
|
1531
|
+
const prompts = this.session.resourceLoader.getPrompts().prompts.length;
|
|
1532
|
+
if (skills > 0)
|
|
1533
|
+
parts.push(theme.fg("muted", counted(skills, "skill")));
|
|
1534
|
+
if (extensions > 0)
|
|
1535
|
+
parts.push(theme.fg("muted", counted(extensions, "extension")));
|
|
1536
|
+
if (prompts > 0)
|
|
1537
|
+
parts.push(theme.fg("muted", counted(prompts, "prompt")));
|
|
1538
|
+
const diagnostics = this.collectDiagnosticGroups().flatMap((group) => group.diagnostics);
|
|
1539
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.type === "error").length;
|
|
1540
|
+
const conflicts = diagnostics.length - errors;
|
|
1541
|
+
if (errors > 0)
|
|
1542
|
+
parts.push(theme.fg("error", counted(errors, "error")));
|
|
1543
|
+
if (conflicts > 0)
|
|
1544
|
+
parts.push(theme.fg("warning", counted(conflicts, "conflict")));
|
|
1545
|
+
if (parts.length === 0)
|
|
1546
|
+
return undefined;
|
|
1547
|
+
const ascii = this.settingsManager.getSymbolPreset() === "ascii";
|
|
1548
|
+
const separator = theme.fg("borderMuted", ascii ? " - " : " · ");
|
|
1549
|
+
return parts.join(separator);
|
|
1373
1550
|
}
|
|
1374
1551
|
/**
|
|
1375
1552
|
* Initialize the extension system with TUI-based UI context.
|
|
@@ -1457,6 +1634,7 @@ export class InteractiveMode {
|
|
|
1457
1634
|
this.applyFullscreenScrollbarSetting();
|
|
1458
1635
|
this.footer.setSession(this.session);
|
|
1459
1636
|
this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
|
|
1637
|
+
void this.refreshFooterPermissionMode();
|
|
1460
1638
|
this.footerDataProvider.setCwd(this.sessionManager.getCwd());
|
|
1461
1639
|
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
1462
1640
|
this.outputPad = this.settingsManager.getOutputPad();
|
|
@@ -1499,7 +1677,7 @@ export class InteractiveMode {
|
|
|
1499
1677
|
const message = error instanceof Error ? error.message : String(error);
|
|
1500
1678
|
this.showError(`${prefix}: ${message}`);
|
|
1501
1679
|
stopThemeWatcher();
|
|
1502
|
-
this.stop();
|
|
1680
|
+
this.stop("transcript");
|
|
1503
1681
|
process.exit(1);
|
|
1504
1682
|
}
|
|
1505
1683
|
renderCurrentSessionState() {
|
|
@@ -1509,9 +1687,23 @@ export class InteractiveMode {
|
|
|
1509
1687
|
this.compactionQueuedMessages = [];
|
|
1510
1688
|
this.streamingComponent = undefined;
|
|
1511
1689
|
this.streamingMessage = undefined;
|
|
1512
|
-
|
|
1690
|
+
// The clears above detach children without stopping their animation
|
|
1691
|
+
// timers. Bash components embed an animated loader; dispose them
|
|
1692
|
+
// explicitly so an in-flight `!cmd` cannot keep rendering (or get
|
|
1693
|
+
// flushed back into a fresh session by flushPendingBashComponents).
|
|
1694
|
+
for (const component of this.pendingBashComponents)
|
|
1695
|
+
component.dispose();
|
|
1696
|
+
this.pendingBashComponents = [];
|
|
1697
|
+
this.bashComponent?.dispose();
|
|
1698
|
+
this.bashComponent = undefined;
|
|
1699
|
+
this.clearPendingTools();
|
|
1513
1700
|
this.renderInitialMessages();
|
|
1514
1701
|
}
|
|
1702
|
+
clearPendingTools() {
|
|
1703
|
+
for (const component of this.pendingTools.values())
|
|
1704
|
+
component.dispose();
|
|
1705
|
+
this.pendingTools.clear();
|
|
1706
|
+
}
|
|
1515
1707
|
/**
|
|
1516
1708
|
* Get a registered tool definition by name (for custom rendering).
|
|
1517
1709
|
*/
|
|
@@ -1894,7 +2086,7 @@ export class InteractiveMode {
|
|
|
1894
2086
|
/**
|
|
1895
2087
|
* Show a selector for extensions.
|
|
1896
2088
|
*/
|
|
1897
|
-
showExtensionSelector(title, options, opts) {
|
|
2089
|
+
showExtensionSelector(title, options, opts, enableSearch = false) {
|
|
1898
2090
|
return new Promise((resolve) => {
|
|
1899
2091
|
if (opts?.signal?.aborted) {
|
|
1900
2092
|
resolve(undefined);
|
|
@@ -1913,7 +2105,12 @@ export class InteractiveMode {
|
|
|
1913
2105
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
1914
2106
|
this.hideExtensionSelector();
|
|
1915
2107
|
resolve(undefined);
|
|
1916
|
-
}, {
|
|
2108
|
+
}, {
|
|
2109
|
+
tui: this.ui,
|
|
2110
|
+
timeout: opts?.timeout,
|
|
2111
|
+
onToggleToolsExpanded: () => this.toggleToolOutputExpansion(),
|
|
2112
|
+
enableSearch,
|
|
2113
|
+
});
|
|
1917
2114
|
this.disposeActiveSelector();
|
|
1918
2115
|
this.editorContainer.clear();
|
|
1919
2116
|
this.editorContainer.addChild(this.extensionSelector);
|
|
@@ -1932,6 +2129,54 @@ export class InteractiveMode {
|
|
|
1932
2129
|
this.ui.setFocus(this.editor);
|
|
1933
2130
|
this.ui.requestRender();
|
|
1934
2131
|
}
|
|
2132
|
+
async showConfigurationIndex() {
|
|
2133
|
+
const choice = await this.showExtensionSelector("Configuration", [
|
|
2134
|
+
"Settings",
|
|
2135
|
+
"Model",
|
|
2136
|
+
"Model cycling",
|
|
2137
|
+
"Provider login",
|
|
2138
|
+
"Provider logout",
|
|
2139
|
+
"Project trust",
|
|
2140
|
+
"Resources, extensions, and MCP adapters",
|
|
2141
|
+
], undefined, true);
|
|
2142
|
+
switch (choice) {
|
|
2143
|
+
case "Settings":
|
|
2144
|
+
await this.showSettingsSelector();
|
|
2145
|
+
break;
|
|
2146
|
+
case "Model":
|
|
2147
|
+
this.showModelSelector();
|
|
2148
|
+
break;
|
|
2149
|
+
case "Model cycling":
|
|
2150
|
+
await this.showModelsSelector();
|
|
2151
|
+
break;
|
|
2152
|
+
case "Provider login":
|
|
2153
|
+
await this.handleLoginCommand();
|
|
2154
|
+
break;
|
|
2155
|
+
case "Provider logout":
|
|
2156
|
+
this.showOAuthSelector("logout");
|
|
2157
|
+
break;
|
|
2158
|
+
case "Project trust":
|
|
2159
|
+
this.showTrustSelector();
|
|
2160
|
+
break;
|
|
2161
|
+
case "Resources, extensions, and MCP adapters":
|
|
2162
|
+
this.showStatus(this.describeMcpServers());
|
|
2163
|
+
break;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
/**
|
|
2167
|
+
* Report configured MCP servers and their live connection state. Replaces a string
|
|
2168
|
+
* that offered MCP management from `apex-code config`, which manages package
|
|
2169
|
+
* resources and has never been able to configure a server.
|
|
2170
|
+
*/
|
|
2171
|
+
describeMcpServers() {
|
|
2172
|
+
const runtime = this.session.mcpRuntime;
|
|
2173
|
+
const servers = runtime ? [...runtime.servers.keys()] : [];
|
|
2174
|
+
if (servers.length === 0) {
|
|
2175
|
+
return "Run apex-code config to manage resources and extensions. No MCP server is configured; add one to .mcp.json.";
|
|
2176
|
+
}
|
|
2177
|
+
const states = servers.map((name) => `${name} (${runtime?.manager.state(name).kind ?? "disconnected"})`);
|
|
2178
|
+
return `Run apex-code config to manage resources and extensions. MCP servers: ${states.join(", ")}.`;
|
|
2179
|
+
}
|
|
1935
2180
|
/**
|
|
1936
2181
|
* Show a confirmation dialog for extensions.
|
|
1937
2182
|
*/
|
|
@@ -2243,6 +2488,8 @@ export class InteractiveMode {
|
|
|
2243
2488
|
const wasBashMode = this.isBashMode;
|
|
2244
2489
|
this.isBashMode = text.trimStart().startsWith("!");
|
|
2245
2490
|
if (wasBashMode !== this.isBashMode) {
|
|
2491
|
+
if (this.isBashMode)
|
|
2492
|
+
this.offerFirstUseHint("bash");
|
|
2246
2493
|
this.updateEditorBorderColor();
|
|
2247
2494
|
}
|
|
2248
2495
|
};
|
|
@@ -2274,7 +2521,7 @@ export class InteractiveMode {
|
|
|
2274
2521
|
if (image) {
|
|
2275
2522
|
const tmpDir = os.tmpdir();
|
|
2276
2523
|
const ext = extensionForImageMimeType(image.mimeType) ?? "png";
|
|
2277
|
-
const fileName = `
|
|
2524
|
+
const fileName = `apex-code-clipboard-${crypto.randomUUID()}.${ext}`;
|
|
2278
2525
|
const filePath = path.join(tmpDir, fileName);
|
|
2279
2526
|
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
|
2280
2527
|
this.editor.insertTextAtCursor?.(filePath);
|
|
@@ -2291,15 +2538,31 @@ export class InteractiveMode {
|
|
|
2291
2538
|
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
2292
2539
|
}
|
|
2293
2540
|
}
|
|
2541
|
+
handleStartupSubmit(text) {
|
|
2542
|
+
this.editor.setText(text);
|
|
2543
|
+
this.showStatus("Startup is still in progress");
|
|
2544
|
+
}
|
|
2294
2545
|
setupEditorSubmitHandler() {
|
|
2295
2546
|
this.defaultEditor.onSubmit = async (text) => {
|
|
2296
2547
|
text = text.trim();
|
|
2297
2548
|
if (!text)
|
|
2298
2549
|
return;
|
|
2550
|
+
// A mode chosen in /settings is written asynchronously. Awaiting it here
|
|
2551
|
+
// keeps the store authoritative before any tool call can read it, so a
|
|
2552
|
+
// switch *out* of bypassPermissions can never be raced by the next turn.
|
|
2553
|
+
if (this.pendingPermissionModeWrite) {
|
|
2554
|
+
await this.pendingPermissionModeWrite;
|
|
2555
|
+
this.pendingPermissionModeWrite = undefined;
|
|
2556
|
+
}
|
|
2299
2557
|
// Handle commands
|
|
2558
|
+
if (text === "/config") {
|
|
2559
|
+
this.editor.setText("");
|
|
2560
|
+
await this.showConfigurationIndex();
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2300
2563
|
if (text === "/settings") {
|
|
2301
|
-
this.showSettingsSelector();
|
|
2302
2564
|
this.editor.setText("");
|
|
2565
|
+
await this.showSettingsSelector();
|
|
2303
2566
|
return;
|
|
2304
2567
|
}
|
|
2305
2568
|
if (text === "/scoped-models") {
|
|
@@ -2313,6 +2576,12 @@ export class InteractiveMode {
|
|
|
2313
2576
|
await this.handleModelCommand(searchTerm);
|
|
2314
2577
|
return;
|
|
2315
2578
|
}
|
|
2579
|
+
if (text === "/thinking" || text.startsWith("/thinking ")) {
|
|
2580
|
+
const searchTerm = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
|
|
2581
|
+
this.editor.setText("");
|
|
2582
|
+
this.handleThinkingCommand(searchTerm);
|
|
2583
|
+
return;
|
|
2584
|
+
}
|
|
2316
2585
|
if (text === "/export" || text.startsWith("/export ")) {
|
|
2317
2586
|
await this.handleExportCommand(text);
|
|
2318
2587
|
this.editor.setText("");
|
|
@@ -2339,7 +2608,7 @@ export class InteractiveMode {
|
|
|
2339
2608
|
return;
|
|
2340
2609
|
}
|
|
2341
2610
|
if (text === "/session") {
|
|
2342
|
-
this.handleSessionCommand();
|
|
2611
|
+
await this.handleSessionCommand();
|
|
2343
2612
|
this.editor.setText("");
|
|
2344
2613
|
return;
|
|
2345
2614
|
}
|
|
@@ -2348,11 +2617,21 @@ export class InteractiveMode {
|
|
|
2348
2617
|
this.editor.setText("");
|
|
2349
2618
|
return;
|
|
2350
2619
|
}
|
|
2620
|
+
if (text === "/resources") {
|
|
2621
|
+
this.showLoadedResources({ force: true });
|
|
2622
|
+
this.editor.setText("");
|
|
2623
|
+
return;
|
|
2624
|
+
}
|
|
2351
2625
|
if (text === "/hotkeys") {
|
|
2352
2626
|
this.handleHotkeysCommand();
|
|
2353
2627
|
this.editor.setText("");
|
|
2354
2628
|
return;
|
|
2355
2629
|
}
|
|
2630
|
+
if (text === "/help") {
|
|
2631
|
+
this.handleHelpCommand();
|
|
2632
|
+
this.editor.setText("");
|
|
2633
|
+
return;
|
|
2634
|
+
}
|
|
2356
2635
|
if (text === "/fork") {
|
|
2357
2636
|
this.showUserMessageSelector();
|
|
2358
2637
|
this.editor.setText("");
|
|
@@ -2488,7 +2767,7 @@ export class InteractiveMode {
|
|
|
2488
2767
|
this.footer.invalidate();
|
|
2489
2768
|
switch (event.type) {
|
|
2490
2769
|
case "agent_start":
|
|
2491
|
-
this.
|
|
2770
|
+
this.clearPendingTools();
|
|
2492
2771
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
2493
2772
|
this.ui.terminal.setProgress(true);
|
|
2494
2773
|
}
|
|
@@ -2540,6 +2819,7 @@ export class InteractiveMode {
|
|
|
2540
2819
|
this.streamingMessage = event.message;
|
|
2541
2820
|
this.chatContainer.addChild(this.streamingComponent);
|
|
2542
2821
|
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
2822
|
+
this.updateEditorBorderColor();
|
|
2543
2823
|
this.ui.requestRender();
|
|
2544
2824
|
}
|
|
2545
2825
|
break;
|
|
@@ -2557,6 +2837,7 @@ export class InteractiveMode {
|
|
|
2557
2837
|
component.setExpanded(this.toolOutputExpanded);
|
|
2558
2838
|
this.chatContainer.addChild(component);
|
|
2559
2839
|
this.pendingTools.set(content.id, component);
|
|
2840
|
+
this.offerFirstUseHint("tool-expand");
|
|
2560
2841
|
}
|
|
2561
2842
|
else {
|
|
2562
2843
|
const component = this.pendingTools.get(content.id);
|
|
@@ -2594,7 +2875,7 @@ export class InteractiveMode {
|
|
|
2594
2875
|
isError: true,
|
|
2595
2876
|
});
|
|
2596
2877
|
}
|
|
2597
|
-
this.
|
|
2878
|
+
this.clearPendingTools();
|
|
2598
2879
|
}
|
|
2599
2880
|
else {
|
|
2600
2881
|
// Args are now complete - trigger diff computation for edit tools
|
|
@@ -2606,6 +2887,7 @@ export class InteractiveMode {
|
|
|
2606
2887
|
this.streamingComponent = undefined;
|
|
2607
2888
|
this.streamingMessage = undefined;
|
|
2608
2889
|
this.footer.invalidate();
|
|
2890
|
+
this.updateEditorBorderColor();
|
|
2609
2891
|
}
|
|
2610
2892
|
this.ui.requestRender();
|
|
2611
2893
|
break;
|
|
@@ -2654,7 +2936,7 @@ export class InteractiveMode {
|
|
|
2654
2936
|
this.streamingComponent = undefined;
|
|
2655
2937
|
this.streamingMessage = undefined;
|
|
2656
2938
|
}
|
|
2657
|
-
this.
|
|
2939
|
+
this.clearPendingTools();
|
|
2658
2940
|
this.ui.requestRender();
|
|
2659
2941
|
break;
|
|
2660
2942
|
case "agent_settled":
|
|
@@ -2691,9 +2973,21 @@ export class InteractiveMode {
|
|
|
2691
2973
|
}
|
|
2692
2974
|
}
|
|
2693
2975
|
else if (event.result) {
|
|
2976
|
+
const entries = this.sessionManager.buildContextEntries();
|
|
2977
|
+
if (entries[0]?.type !== "compaction") {
|
|
2978
|
+
throw new Error("Completed compaction is missing from the session context");
|
|
2979
|
+
}
|
|
2694
2980
|
this.chatContainer.clear();
|
|
2695
|
-
|
|
2981
|
+
// The latest compaction is prepended for model context; append it below at its chronological position.
|
|
2982
|
+
this.renderSessionEntries(entries.slice(1));
|
|
2696
2983
|
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
2984
|
+
if (event.result.usage) {
|
|
2985
|
+
this.addCompactionCostNotice({
|
|
2986
|
+
type: "compaction_cost",
|
|
2987
|
+
kind: "compaction",
|
|
2988
|
+
usage: event.result.usage,
|
|
2989
|
+
});
|
|
2990
|
+
}
|
|
2697
2991
|
this.footer.invalidate();
|
|
2698
2992
|
}
|
|
2699
2993
|
else if (event.errorMessage) {
|
|
@@ -2766,6 +3060,19 @@ export class InteractiveMode {
|
|
|
2766
3060
|
: message.content.filter((c) => c.type === "text");
|
|
2767
3061
|
return textBlocks.map((c) => c.text).join("");
|
|
2768
3062
|
}
|
|
3063
|
+
/** Show a managed-tool status update in the chat. */
|
|
3064
|
+
showManagedToolStatus(status) {
|
|
3065
|
+
if (!this.managedToolStatusStarted) {
|
|
3066
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3067
|
+
this.managedToolStatusStarted = true;
|
|
3068
|
+
}
|
|
3069
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
3070
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
3071
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
3072
|
+
this.lastStatusSpacer = undefined;
|
|
3073
|
+
this.lastStatusText = undefined;
|
|
3074
|
+
this.ui.requestRender();
|
|
3075
|
+
}
|
|
2769
3076
|
/**
|
|
2770
3077
|
* Show a status message in the chat.
|
|
2771
3078
|
*
|
|
@@ -2886,7 +3193,7 @@ export class InteractiveMode {
|
|
|
2886
3193
|
}
|
|
2887
3194
|
}
|
|
2888
3195
|
renderSessionItems(items, options = {}) {
|
|
2889
|
-
this.
|
|
3196
|
+
this.clearPendingTools();
|
|
2890
3197
|
const renderedPendingTools = new Map();
|
|
2891
3198
|
// Cache-miss notices are not persisted; re-derive them from the full entry
|
|
2892
3199
|
// list and re-inject them after the assistant messages that paid for them.
|
|
@@ -2902,6 +3209,10 @@ export class InteractiveMode {
|
|
|
2902
3209
|
this.addCustomEntryToChat(item);
|
|
2903
3210
|
continue;
|
|
2904
3211
|
}
|
|
3212
|
+
if (isCompactionCostNotice(item)) {
|
|
3213
|
+
this.addCompactionCostNotice(item);
|
|
3214
|
+
continue;
|
|
3215
|
+
}
|
|
2905
3216
|
const message = item;
|
|
2906
3217
|
// Assistant messages need special handling for tool calls
|
|
2907
3218
|
if (message.role === "assistant") {
|
|
@@ -2969,10 +3280,28 @@ export class InteractiveMode {
|
|
|
2969
3280
|
if (entry.type === "custom") {
|
|
2970
3281
|
return [entry];
|
|
2971
3282
|
}
|
|
2972
|
-
|
|
3283
|
+
const messages = sessionEntryToContextMessages(entry);
|
|
3284
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
|
|
3285
|
+
return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
|
|
3286
|
+
}
|
|
3287
|
+
return messages;
|
|
2973
3288
|
});
|
|
2974
3289
|
this.renderSessionItems(items, options);
|
|
2975
3290
|
}
|
|
3291
|
+
/**
|
|
3292
|
+
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
3293
|
+
* from persisted summary usage and is not stored as a separate session entry.
|
|
3294
|
+
*/
|
|
3295
|
+
addCompactionCostNotice(notice) {
|
|
3296
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
3297
|
+
return;
|
|
3298
|
+
const { usage } = notice;
|
|
3299
|
+
const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
3300
|
+
const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
|
|
3301
|
+
const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
|
|
3302
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3303
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
3304
|
+
}
|
|
2976
3305
|
/**
|
|
2977
3306
|
* Show a transcript notice when a completed assistant message paid for a
|
|
2978
3307
|
* significant cache miss. Only states observable facts: the miss itself,
|
|
@@ -3024,7 +3353,7 @@ export class InteractiveMode {
|
|
|
3024
3353
|
if (this.chatContainer.children.length > 0) {
|
|
3025
3354
|
this.chatContainer.addChild(new Spacer(1));
|
|
3026
3355
|
}
|
|
3027
|
-
this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart
|
|
3356
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `This project is not trusted. Project ${CONFIG_DIR_NAME} resources and packages are ignored. Use /trust to save a trust decision, then restart apex-code.`), 1, 0));
|
|
3028
3357
|
}
|
|
3029
3358
|
async getUserInput() {
|
|
3030
3359
|
const queuedInput = this.pendingUserInputs.shift();
|
|
@@ -3137,7 +3466,7 @@ export class InteractiveMode {
|
|
|
3137
3466
|
this.ui.stop();
|
|
3138
3467
|
}
|
|
3139
3468
|
catch { }
|
|
3140
|
-
console.error("
|
|
3469
|
+
console.error("apex-code exiting due to uncaughtException:");
|
|
3141
3470
|
console.error(error);
|
|
3142
3471
|
process.exit(1);
|
|
3143
3472
|
}
|
|
@@ -3183,6 +3512,34 @@ export class InteractiveMode {
|
|
|
3183
3512
|
const uncaughtExceptionHandler = (error) => this.uncaughtCrash(error);
|
|
3184
3513
|
process.prependListener("uncaughtException", uncaughtExceptionHandler);
|
|
3185
3514
|
this.signalCleanupHandlers.push(() => process.off("uncaughtException", uncaughtExceptionHandler));
|
|
3515
|
+
// Lend the terminal back to the supervisor when it needs to draw a sandbox
|
|
3516
|
+
// escalation prompt. The supervisor owns that decision (ADR 0023) and this
|
|
3517
|
+
// process cannot read the answer anyway -- bwrap --new-session leaves it with no
|
|
3518
|
+
// controlling terminal. `ui.stop()` is the same restore Ctrl+Z already performs,
|
|
3519
|
+
// so the terminal is in cooked mode with a visible cursor while the human answers.
|
|
3520
|
+
const handoffDirectory = process.env[TERMINAL_HANDOFF_PATH_VARIABLE];
|
|
3521
|
+
if (handoffDirectory) {
|
|
3522
|
+
const observer = observeTerminalHandoff(handoffDirectory, {
|
|
3523
|
+
suspend: () => {
|
|
3524
|
+
try {
|
|
3525
|
+
this.ui.stop();
|
|
3526
|
+
}
|
|
3527
|
+
catch {
|
|
3528
|
+
// A terminal we cannot restore still leaves the prompt readable.
|
|
3529
|
+
}
|
|
3530
|
+
},
|
|
3531
|
+
resume: () => {
|
|
3532
|
+
try {
|
|
3533
|
+
this.ui.start();
|
|
3534
|
+
this.ui.requestRender(true);
|
|
3535
|
+
}
|
|
3536
|
+
catch {
|
|
3537
|
+
// Nothing further to try; the next render request recovers.
|
|
3538
|
+
}
|
|
3539
|
+
},
|
|
3540
|
+
});
|
|
3541
|
+
this.signalCleanupHandlers.push(() => observer.stop());
|
|
3542
|
+
}
|
|
3186
3543
|
}
|
|
3187
3544
|
unregisterSignalHandlers() {
|
|
3188
3545
|
for (const cleanup of this.signalCleanupHandlers) {
|
|
@@ -3244,6 +3601,7 @@ export class InteractiveMode {
|
|
|
3244
3601
|
this.editor.addToHistory?.(text);
|
|
3245
3602
|
this.editor.setText("");
|
|
3246
3603
|
await this.session.prompt(text, { streamingBehavior: "followUp" });
|
|
3604
|
+
this.offerFirstUseHint("queue");
|
|
3247
3605
|
this.updatePendingMessagesDisplay();
|
|
3248
3606
|
this.ui.requestRender();
|
|
3249
3607
|
}
|
|
@@ -3263,6 +3621,7 @@ export class InteractiveMode {
|
|
|
3263
3621
|
}
|
|
3264
3622
|
}
|
|
3265
3623
|
updateEditorBorderColor() {
|
|
3624
|
+
this.defaultEditor.setModeLabel(this.isBashMode ? "bash" : this.session.isStreaming ? "busy" : undefined);
|
|
3266
3625
|
if (this.isBashMode) {
|
|
3267
3626
|
this.editor.borderColor = theme.getBashModeBorderColor();
|
|
3268
3627
|
}
|
|
@@ -3323,6 +3682,7 @@ export class InteractiveMode {
|
|
|
3323
3682
|
this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
|
|
3324
3683
|
}
|
|
3325
3684
|
toggleThinkingBlockVisibility() {
|
|
3685
|
+
this.offerFirstUseHint("thinking");
|
|
3326
3686
|
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
3327
3687
|
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
3328
3688
|
// Rebuild chat from session messages
|
|
@@ -3374,7 +3734,7 @@ export class InteractiveMode {
|
|
|
3374
3734
|
showNewVersionNotification(release) {
|
|
3375
3735
|
const action = theme.fg("accent", `${APP_NAME} update`);
|
|
3376
3736
|
const updateInstruction = theme.fg("muted", `New version ${release.version} is available. Run `) + action;
|
|
3377
|
-
const changelogUrl = "https://
|
|
3737
|
+
const changelogUrl = "https://github.com/Fchery87/apex-code/blob/main/packages/coding-agent/CHANGELOG.md";
|
|
3378
3738
|
const changelogLink = getCapabilities().hyperlinks
|
|
3379
3739
|
? hyperlink(theme.fg("accent", changelogUrl), changelogUrl)
|
|
3380
3740
|
: theme.fg("accent", changelogUrl);
|
|
@@ -3606,11 +3966,63 @@ export class InteractiveMode {
|
|
|
3606
3966
|
this.ui.setFocus(created.focus);
|
|
3607
3967
|
this.ui.requestRender();
|
|
3608
3968
|
}
|
|
3609
|
-
|
|
3969
|
+
/**
|
|
3970
|
+
* Resyncs the footer from the store. Called on every session swap, not just at
|
|
3971
|
+
* startup: `applyRuntimeSettings` also repoints the cwd, and cwd selects which
|
|
3972
|
+
* project-scope permission file applies, so the mode genuinely can differ
|
|
3973
|
+
* between two sessions in one process. A footer left showing the old value
|
|
3974
|
+
* would under-report bypassPermissions, which is the one direction that matters.
|
|
3975
|
+
*/
|
|
3976
|
+
async refreshFooterPermissionMode() {
|
|
3977
|
+
const resolution = await this.session.getPermissionMode();
|
|
3978
|
+
this.footer.setPermissionMode(resolution?.mode ?? "default");
|
|
3979
|
+
if (this.isInitialized) {
|
|
3980
|
+
this.ui.requestRender();
|
|
3981
|
+
}
|
|
3982
|
+
}
|
|
3983
|
+
/**
|
|
3984
|
+
* Writes the mode to user scope, then reports what is actually in force. The
|
|
3985
|
+
* write can be outranked by `--permission-mode` or a project file, and a
|
|
3986
|
+
* settings row that silently does nothing is worse than no row at all.
|
|
3987
|
+
*/
|
|
3988
|
+
async applyPermissionMode(mode) {
|
|
3989
|
+
let resolution;
|
|
3990
|
+
try {
|
|
3991
|
+
const write = this.session.setPermissionMode(mode);
|
|
3992
|
+
this.pendingPermissionModeWrite = write.then(() => { }, () => { });
|
|
3993
|
+
resolution = await write;
|
|
3994
|
+
}
|
|
3995
|
+
catch (error) {
|
|
3996
|
+
// Never swallow this. A permission mode the user believes they set, and
|
|
3997
|
+
// did not, is the one outcome worse than the prompt they were escaping.
|
|
3998
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
3999
|
+
this.chatContainer.addChild(new Text(theme.fg("error", `Permission mode could not be saved: ${detail}`), 1, 0));
|
|
4000
|
+
this.ui.requestRender();
|
|
4001
|
+
return;
|
|
4002
|
+
}
|
|
4003
|
+
if (!resolution)
|
|
4004
|
+
return;
|
|
4005
|
+
const outranked = resolution.mode !== mode;
|
|
4006
|
+
const source = PERMISSION_MODE_OVERRIDE_HINTS[resolution.origin] ?? resolution.origin;
|
|
4007
|
+
const line = outranked
|
|
4008
|
+
? `Permission mode saved as ${mode}, but ${source} sets ${resolution.mode} for this session.`
|
|
4009
|
+
: `Permission mode set to ${mode}.`;
|
|
4010
|
+
this.chatContainer.addChild(new Text(theme.fg(outranked ? "warning" : "muted", line), 1, 0));
|
|
4011
|
+
this.footer.setPermissionMode(resolution.mode);
|
|
4012
|
+
this.ui.requestRender();
|
|
4013
|
+
}
|
|
4014
|
+
async showSettingsSelector() {
|
|
4015
|
+
const permissionMode = await this.session.getPermissionMode();
|
|
3610
4016
|
this.showSelector((done) => {
|
|
3611
4017
|
let selector;
|
|
4018
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
4019
|
+
const defaultModelId = this.settingsManager.getDefaultModel();
|
|
4020
|
+
const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
|
|
3612
4021
|
selector = new SettingsSelectorComponent({
|
|
3613
4022
|
autoCompact: this.session.autoCompactionEnabled,
|
|
4023
|
+
defaultModel,
|
|
4024
|
+
currentModel: this.session.model,
|
|
4025
|
+
availableDefaultModels: this.session.modelRuntime.getAvailableSnapshot(),
|
|
3614
4026
|
showImages: this.settingsManager.getShowImages(),
|
|
3615
4027
|
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
3616
4028
|
autoResizeImages: this.settingsManager.getImageAutoResize(),
|
|
@@ -3620,20 +4032,22 @@ export class InteractiveMode {
|
|
|
3620
4032
|
followUpMode: this.session.followUpMode,
|
|
3621
4033
|
transport: this.settingsManager.getTransport(),
|
|
3622
4034
|
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
3623
|
-
thinkingLevel: this.
|
|
3624
|
-
availableThinkingLevels:
|
|
3625
|
-
|
|
4035
|
+
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
4036
|
+
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
4037
|
+
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
4038
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
3626
4039
|
terminalTheme: this.themeController.getTerminalTheme(),
|
|
3627
4040
|
availableThemes: getAvailableThemes(),
|
|
3628
4041
|
hideThinkingBlock: this.hideThinkingBlock,
|
|
3629
4042
|
mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
|
|
3630
4043
|
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
3631
|
-
|
|
4044
|
+
sendProviderAttribution: this.settingsManager.getSendProviderAttribution(),
|
|
3632
4045
|
doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
|
|
3633
4046
|
treeFilterMode: this.settingsManager.getTreeFilterMode(),
|
|
3634
4047
|
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
3635
4048
|
showCacheMissNotices: this.settingsManager.getShowCacheMissNotices(),
|
|
3636
4049
|
defaultProjectTrust: this.settingsManager.getDefaultProjectTrust(),
|
|
4050
|
+
permissionMode,
|
|
3637
4051
|
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
3638
4052
|
outputPad: this.settingsManager.getOutputPad(),
|
|
3639
4053
|
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
@@ -3641,6 +4055,7 @@ export class InteractiveMode {
|
|
|
3641
4055
|
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
3642
4056
|
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
3643
4057
|
tuiMode: this.ui.mode,
|
|
4058
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
3644
4059
|
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
3645
4060
|
warnings: this.settingsManager.getWarnings(),
|
|
3646
4061
|
}, {
|
|
@@ -3689,14 +4104,30 @@ export class InteractiveMode {
|
|
|
3689
4104
|
configureHttpDispatcher(timeoutMs);
|
|
3690
4105
|
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
3691
4106
|
},
|
|
3692
|
-
|
|
3693
|
-
this.
|
|
3694
|
-
|
|
3695
|
-
this.
|
|
4107
|
+
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
4108
|
+
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
4109
|
+
// If the override is for the current model, apply it to the session too
|
|
4110
|
+
const current = this.session.model;
|
|
4111
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
4112
|
+
this.session.setThinkingLevel(level);
|
|
4113
|
+
this.footer.invalidate();
|
|
4114
|
+
this.updateEditorBorderColor();
|
|
4115
|
+
}
|
|
4116
|
+
},
|
|
4117
|
+
onModelThinkingLevelRemove: (provider, modelId) => {
|
|
4118
|
+
this.settingsManager.removeModelThinkingLevel(provider, modelId);
|
|
4119
|
+
// If the override was for the current model, revert to global default
|
|
4120
|
+
const current = this.session.model;
|
|
4121
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
4122
|
+
const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
4123
|
+
this.session.setThinkingLevel(globalDefault);
|
|
4124
|
+
this.footer.invalidate();
|
|
4125
|
+
this.updateEditorBorderColor();
|
|
4126
|
+
}
|
|
3696
4127
|
},
|
|
3697
4128
|
onThemeChange: (themeSetting) => {
|
|
3698
4129
|
this.settingsManager.setTheme(themeSetting);
|
|
3699
|
-
void this.themeController.
|
|
4130
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
3700
4131
|
},
|
|
3701
4132
|
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
3702
4133
|
onHideThinkingBlockChange: (hidden) => {
|
|
@@ -3722,8 +4153,8 @@ export class InteractiveMode {
|
|
|
3722
4153
|
onCollapseChangelogChange: (collapsed) => {
|
|
3723
4154
|
this.settingsManager.setCollapseChangelog(collapsed);
|
|
3724
4155
|
},
|
|
3725
|
-
|
|
3726
|
-
this.settingsManager.
|
|
4156
|
+
onSendProviderAttributionChange: (enabled) => {
|
|
4157
|
+
this.settingsManager.setSendProviderAttribution(enabled);
|
|
3727
4158
|
},
|
|
3728
4159
|
onQuietStartupChange: (enabled) => {
|
|
3729
4160
|
this.settingsManager.setQuietStartup(enabled);
|
|
@@ -3731,6 +4162,9 @@ export class InteractiveMode {
|
|
|
3731
4162
|
onDefaultProjectTrustChange: (defaultProjectTrust) => {
|
|
3732
4163
|
this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
|
|
3733
4164
|
},
|
|
4165
|
+
onPermissionModeChange: (mode) => {
|
|
4166
|
+
void this.applyPermissionMode(mode);
|
|
4167
|
+
},
|
|
3734
4168
|
onDoubleEscapeActionChange: (action) => {
|
|
3735
4169
|
this.settingsManager.setDoubleEscapeAction(action);
|
|
3736
4170
|
},
|
|
@@ -3795,6 +4229,9 @@ export class InteractiveMode {
|
|
|
3795
4229
|
this.statusContainer.clear();
|
|
3796
4230
|
this.showStatus(`TUI mode: ${mode}`);
|
|
3797
4231
|
},
|
|
4232
|
+
onFullscreenExitOutputChange: (output) => {
|
|
4233
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
4234
|
+
},
|
|
3798
4235
|
onFullscreenScrollbarChange: (mode) => {
|
|
3799
4236
|
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3800
4237
|
this.applyFullscreenScrollbarSetting();
|
|
@@ -3810,6 +4247,44 @@ export class InteractiveMode {
|
|
|
3810
4247
|
return { component: selector, focus: selector.getSettingsList() };
|
|
3811
4248
|
});
|
|
3812
4249
|
}
|
|
4250
|
+
handleThinkingCommand(searchTerm) {
|
|
4251
|
+
const availableLevels = this.session.getAvailableThinkingLevels();
|
|
4252
|
+
if (!searchTerm) {
|
|
4253
|
+
this.showThinkingSelector();
|
|
4254
|
+
return;
|
|
4255
|
+
}
|
|
4256
|
+
const normalized = searchTerm.trim().toLowerCase();
|
|
4257
|
+
const level = availableLevels.find((candidate) => candidate.toLowerCase() === normalized);
|
|
4258
|
+
if (!level) {
|
|
4259
|
+
this.showError(`Unknown thinking level "${searchTerm}". Available levels: ${availableLevels.join(", ")}.`);
|
|
4260
|
+
return;
|
|
4261
|
+
}
|
|
4262
|
+
this.selectThinkingLevel(level, false);
|
|
4263
|
+
}
|
|
4264
|
+
selectThinkingLevel(level, persist) {
|
|
4265
|
+
try {
|
|
4266
|
+
this.session.setThinkingLevel(level, { persist });
|
|
4267
|
+
this.footer.invalidate();
|
|
4268
|
+
this.updateEditorBorderColor();
|
|
4269
|
+
this.showStatus(persist ? `Default thinking level: ${level}` : `Thinking level: ${level}`);
|
|
4270
|
+
}
|
|
4271
|
+
catch (error) {
|
|
4272
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
showThinkingSelector() {
|
|
4276
|
+
this.showSelector((done) => {
|
|
4277
|
+
const selectLevel = (level, persist) => {
|
|
4278
|
+
this.selectThinkingLevel(level, persist);
|
|
4279
|
+
done();
|
|
4280
|
+
};
|
|
4281
|
+
const selector = new ThinkingSelectorComponent(this.session.thinkingLevel ?? DEFAULT_THINKING_LEVEL, this.session.getAvailableThinkingLevels(), (level) => selectLevel(level, false), () => {
|
|
4282
|
+
done();
|
|
4283
|
+
this.ui.requestRender();
|
|
4284
|
+
}, (level) => selectLevel(level, true), this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL);
|
|
4285
|
+
return { component: selector, focus: selector };
|
|
4286
|
+
});
|
|
4287
|
+
}
|
|
3813
4288
|
async handleModelCommand(searchTerm) {
|
|
3814
4289
|
if (!searchTerm) {
|
|
3815
4290
|
this.showModelSelector();
|
|
@@ -3818,7 +4293,7 @@ export class InteractiveMode {
|
|
|
3818
4293
|
const model = await this.findExactModelMatch(searchTerm);
|
|
3819
4294
|
if (model) {
|
|
3820
4295
|
try {
|
|
3821
|
-
await this.session.setModel(model);
|
|
4296
|
+
await this.session.setModel(model, { persist: false });
|
|
3822
4297
|
this.footer.invalidate();
|
|
3823
4298
|
this.updateEditorBorderColor();
|
|
3824
4299
|
this.showStatus(`Model: ${model.id}`);
|
|
@@ -3847,7 +4322,7 @@ export class InteractiveMode {
|
|
|
3847
4322
|
controller.abort();
|
|
3848
4323
|
}, 15_000);
|
|
3849
4324
|
try {
|
|
3850
|
-
const result = await this.session.modelRuntime
|
|
4325
|
+
const result = await refreshModelCatalogs(this.session.modelRuntime, controller.signal);
|
|
3851
4326
|
if (result.aborted && timedOut) {
|
|
3852
4327
|
this.showWarning("Model refresh timed out; searching cached models.");
|
|
3853
4328
|
}
|
|
@@ -3935,7 +4410,7 @@ export class InteractiveMode {
|
|
|
3935
4410
|
onSelect: (selection) => {
|
|
3936
4411
|
trustStore.setMany(selection.updates);
|
|
3937
4412
|
done();
|
|
3938
|
-
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart
|
|
4413
|
+
this.showStatus(`Saved trust decision: ${selection.trusted ? "trusted" : "untrusted"}. Restart ${APP_NAME} for this to take effect.`);
|
|
3939
4414
|
},
|
|
3940
4415
|
onCancel: () => {
|
|
3941
4416
|
done();
|
|
@@ -3947,13 +4422,13 @@ export class InteractiveMode {
|
|
|
3947
4422
|
}
|
|
3948
4423
|
showModelSelector(initialSearchInput) {
|
|
3949
4424
|
this.showSelector((done) => {
|
|
3950
|
-
const
|
|
4425
|
+
const selectModel = async (model, persist) => {
|
|
3951
4426
|
try {
|
|
3952
|
-
await this.session.setModel(model);
|
|
4427
|
+
await this.session.setModel(model, { persist });
|
|
3953
4428
|
this.footer.invalidate();
|
|
3954
4429
|
this.updateEditorBorderColor();
|
|
3955
4430
|
done();
|
|
3956
|
-
this.showStatus(`Model: ${model.id}`);
|
|
4431
|
+
this.showStatus(persist ? `Default model: ${model.provider}/${model.id}` : `Model: ${model.id}`);
|
|
3957
4432
|
void this.maybeWarnAboutAnthropicSubscriptionAuth(model);
|
|
3958
4433
|
this.checkDaxnutsEasterEgg(model);
|
|
3959
4434
|
}
|
|
@@ -3961,10 +4436,13 @@ export class InteractiveMode {
|
|
|
3961
4436
|
done();
|
|
3962
4437
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
3963
4438
|
}
|
|
3964
|
-
}
|
|
4439
|
+
};
|
|
4440
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
4441
|
+
const defaultModel = this.settingsManager.getDefaultModel();
|
|
4442
|
+
const selector = new ModelSelectorComponent(this.ui, this.session.model, this.session.modelRuntime, this.session.scopedModels, (model) => selectModel(model, false), () => {
|
|
3965
4443
|
done();
|
|
3966
4444
|
this.ui.requestRender();
|
|
3967
|
-
}, initialSearchInput);
|
|
4445
|
+
}, initialSearchInput, (model) => selectModel(model, true), defaultProvider && defaultModel ? { provider: defaultProvider, id: defaultModel } : undefined);
|
|
3968
4446
|
return { component: selector, focus: selector, dispose: () => selector.dispose() };
|
|
3969
4447
|
});
|
|
3970
4448
|
}
|
|
@@ -4035,8 +4513,7 @@ export class InteractiveMode {
|
|
|
4035
4513
|
this.ui.requestRender();
|
|
4036
4514
|
},
|
|
4037
4515
|
});
|
|
4038
|
-
void this.session.modelRuntime
|
|
4039
|
-
.refresh({ signal: controller.signal })
|
|
4516
|
+
void refreshModelCatalogs(this.session.modelRuntime, controller.signal)
|
|
4040
4517
|
.then((result) => {
|
|
4041
4518
|
if (disposed)
|
|
4042
4519
|
return;
|
|
@@ -4521,7 +4998,11 @@ export class InteractiveMode {
|
|
|
4521
4998
|
if (isUnknownModel(previousModel)) {
|
|
4522
4999
|
const availableModels = this.session.modelRuntime.getAvailableSnapshot();
|
|
4523
5000
|
const providerModels = availableModels.filter((model) => model.provider === providerId);
|
|
4524
|
-
|
|
5001
|
+
// Matches LLAMA_PROVIDER_ID from extensions/llama/provider.ts; kept inline to avoid coupling interactive mode to the built-in extension.
|
|
5002
|
+
if (providerId === "llama.cpp") {
|
|
5003
|
+
selectionError = llamaCppPostLoginGuidance(actionLabel, providerModels.length);
|
|
5004
|
+
}
|
|
5005
|
+
else if (!hasDefaultModelProvider(providerId)) {
|
|
4525
5006
|
selectionError = `${actionLabel}, but no default model is configured for provider "${providerId}". Use /model to select a model.`;
|
|
4526
5007
|
}
|
|
4527
5008
|
else if (providerModels.length === 0) {
|
|
@@ -4535,7 +5016,7 @@ export class InteractiveMode {
|
|
|
4535
5016
|
}
|
|
4536
5017
|
else {
|
|
4537
5018
|
try {
|
|
4538
|
-
await this.session.setModel(selectedModel);
|
|
5019
|
+
await this.session.setModel(selectedModel, { persist: true });
|
|
4539
5020
|
}
|
|
4540
5021
|
catch (error) {
|
|
4541
5022
|
selectedModel = undefined;
|
|
@@ -4590,7 +5071,7 @@ export class InteractiveMode {
|
|
|
4590
5071
|
this.ui.requestRender();
|
|
4591
5072
|
};
|
|
4592
5073
|
const dialog = new LoginDialogComponent(this.ui, providerOption.id, () => restoreEditor(), providerOption.name, `${providerOption.name} setup`);
|
|
4593
|
-
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside
|
|
5074
|
+
dialog.showInfo(`${providerOption.method?.name ?? "Authentication"} is configured outside Apex Code.`, [], true);
|
|
4594
5075
|
this.editorContainer.clear();
|
|
4595
5076
|
this.editorContainer.addChild(dialog);
|
|
4596
5077
|
this.ui.setFocus(dialog);
|
|
@@ -4827,7 +5308,9 @@ export class InteractiveMode {
|
|
|
4827
5308
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4828
5309
|
}
|
|
4829
5310
|
else {
|
|
4830
|
-
const filePath = await this.session.exportToHtml(outputPath
|
|
5311
|
+
const filePath = await this.session.exportToHtml(outputPath, {
|
|
5312
|
+
themeName: theme.name,
|
|
5313
|
+
});
|
|
4831
5314
|
this.showStatus(`Session exported to: ${filePath}`);
|
|
4832
5315
|
}
|
|
4833
5316
|
}
|
|
@@ -4903,89 +5386,74 @@ export class InteractiveMode {
|
|
|
4903
5386
|
}
|
|
4904
5387
|
}
|
|
4905
5388
|
async handleShareCommand() {
|
|
4906
|
-
// Check
|
|
5389
|
+
// Check before confirmation so a user without a working GitHub CLI gets an actionable error.
|
|
4907
5390
|
try {
|
|
4908
5391
|
const authResult = spawnSync("gh", ["auth", "status"], { encoding: "utf-8" });
|
|
4909
5392
|
if (authResult.status !== 0) {
|
|
4910
|
-
this.showError("
|
|
5393
|
+
this.showError(formatShareUnavailableMessage("unauthenticated"));
|
|
4911
5394
|
return;
|
|
4912
5395
|
}
|
|
4913
5396
|
}
|
|
4914
5397
|
catch {
|
|
4915
|
-
this.showError(
|
|
5398
|
+
this.showError(formatShareUnavailableMessage("missing"));
|
|
4916
5399
|
return;
|
|
4917
5400
|
}
|
|
4918
|
-
|
|
4919
|
-
|
|
5401
|
+
let loader;
|
|
5402
|
+
let proc;
|
|
4920
5403
|
try {
|
|
4921
|
-
await
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
try {
|
|
4953
|
-
const result = await new Promise((resolve) => {
|
|
4954
|
-
proc = spawn("gh", ["gist", "create", "--public=false", tmpFile]);
|
|
4955
|
-
let stdout = "";
|
|
4956
|
-
let stderr = "";
|
|
4957
|
-
proc.stdout?.on("data", (data) => {
|
|
4958
|
-
stdout += data.toString();
|
|
4959
|
-
});
|
|
4960
|
-
proc.stderr?.on("data", (data) => {
|
|
4961
|
-
stderr += data.toString();
|
|
4962
|
-
});
|
|
4963
|
-
proc.on("close", (code) => resolve({ stdout, stderr, code }));
|
|
5404
|
+
const result = await publishSessionShare({
|
|
5405
|
+
confirm: () => this.showExtensionConfirm("Publish session to GitHub?", "This uploads a complete HTML export to a secret GitHub Gist. Secret Gists are unlisted, not private or access-controlled; anyone with the URL can read the transcript, prompts, tool activity, and included file content. Review a local /export first if needed."),
|
|
5406
|
+
exportToHtml: async (exportPath) => {
|
|
5407
|
+
await this.session.exportToHtml(exportPath);
|
|
5408
|
+
},
|
|
5409
|
+
publishGist: (exportPath) => new Promise((resolve, reject) => {
|
|
5410
|
+
loader = new BorderedLoader(this.ui, theme, "Creating secret GitHub Gist...");
|
|
5411
|
+
this.editorContainer.clear();
|
|
5412
|
+
this.editorContainer.addChild(loader);
|
|
5413
|
+
this.ui.setFocus(loader);
|
|
5414
|
+
this.ui.requestRender();
|
|
5415
|
+
loader.onAbort = () => proc?.kill();
|
|
5416
|
+
proc = spawn("gh", ["gist", "create", "--public=false", exportPath]);
|
|
5417
|
+
let stdout = "";
|
|
5418
|
+
let stderr = "";
|
|
5419
|
+
proc.stdout?.on("data", (data) => {
|
|
5420
|
+
stdout += data.toString();
|
|
5421
|
+
});
|
|
5422
|
+
proc.stderr?.on("data", (data) => {
|
|
5423
|
+
stderr += data.toString();
|
|
5424
|
+
});
|
|
5425
|
+
proc.on("error", reject);
|
|
5426
|
+
proc.on("close", (code) => {
|
|
5427
|
+
if (loader?.signal.aborted)
|
|
5428
|
+
reject(new Error("Share cancelled"));
|
|
5429
|
+
else if (code !== 0)
|
|
5430
|
+
reject(new Error(stderr.trim() || "Unknown GitHub CLI error"));
|
|
5431
|
+
else
|
|
5432
|
+
resolve(stdout);
|
|
5433
|
+
});
|
|
5434
|
+
}),
|
|
4964
5435
|
});
|
|
4965
|
-
if (
|
|
4966
|
-
|
|
4967
|
-
restoreEditor();
|
|
4968
|
-
if (result.code !== 0) {
|
|
4969
|
-
const errorMsg = result.stderr?.trim() || "Unknown error";
|
|
4970
|
-
this.showError(`Failed to create gist: ${errorMsg}`);
|
|
4971
|
-
return;
|
|
4972
|
-
}
|
|
4973
|
-
// Extract gist ID from the URL returned by gh
|
|
4974
|
-
// gh returns something like: https://gist.github.com/username/GIST_ID
|
|
4975
|
-
const gistUrl = result.stdout?.trim();
|
|
4976
|
-
const gistId = gistUrl?.split("/").pop();
|
|
4977
|
-
if (!gistId) {
|
|
4978
|
-
this.showError("Failed to parse gist ID from gh output");
|
|
5436
|
+
if (result.kind === "cancelled") {
|
|
5437
|
+
this.showStatus("Share cancelled");
|
|
4979
5438
|
return;
|
|
4980
5439
|
}
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
5440
|
+
const previewUrl = getShareViewerUrl(result.gistId);
|
|
5441
|
+
this.showStatus(previewUrl
|
|
5442
|
+
? `Secret GitHub Gist: ${result.gistUrl}\nConfigured preview: ${previewUrl}`
|
|
5443
|
+
: `Secret GitHub Gist: ${result.gistUrl}`);
|
|
4984
5444
|
}
|
|
4985
5445
|
catch (error) {
|
|
4986
|
-
if (
|
|
4987
|
-
|
|
4988
|
-
|
|
5446
|
+
if (error instanceof Error && error.message === "Share cancelled")
|
|
5447
|
+
this.showStatus("Share cancelled");
|
|
5448
|
+
else
|
|
5449
|
+
this.showError(`Failed to create secret GitHub Gist: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
5450
|
+
}
|
|
5451
|
+
finally {
|
|
5452
|
+
if (loader) {
|
|
5453
|
+
loader.dispose();
|
|
5454
|
+
this.editorContainer.clear();
|
|
5455
|
+
this.editorContainer.addChild(this.editor);
|
|
5456
|
+
this.ui.setFocus(this.editor);
|
|
4989
5457
|
}
|
|
4990
5458
|
}
|
|
4991
5459
|
}
|
|
@@ -5031,7 +5499,7 @@ export class InteractiveMode {
|
|
|
5031
5499
|
this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
|
|
5032
5500
|
this.ui.requestRender();
|
|
5033
5501
|
}
|
|
5034
|
-
handleSessionCommand() {
|
|
5502
|
+
async handleSessionCommand() {
|
|
5035
5503
|
const stats = this.session.getSessionStats();
|
|
5036
5504
|
const sessionName = this.sessionManager.getSessionName();
|
|
5037
5505
|
const entries = this.sessionManager.getEntries();
|
|
@@ -5084,6 +5552,26 @@ export class InteractiveMode {
|
|
|
5084
5552
|
: `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
|
|
5085
5553
|
}
|
|
5086
5554
|
}
|
|
5555
|
+
// Role and latency: the two dimensions the durable ledger (roadmap Phase 8)
|
|
5556
|
+
// carries that session entries never did. Scoped to this session's rows only
|
|
5557
|
+
// -- a ledger that also served other sessions must never leak their cost in.
|
|
5558
|
+
const usagePerformanceSamples = (await this.session.modelRuntime.listUsagePerformanceSamples()).filter((sample) => sample.sessionId === stats.sessionId);
|
|
5559
|
+
if (usagePerformanceSamples.length > 0) {
|
|
5560
|
+
const avgTtftMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.ttftMs, 0) / usagePerformanceSamples.length;
|
|
5561
|
+
const avgGenerationMs = usagePerformanceSamples.reduce((sum, sample) => sum + sample.generationMs, 0) /
|
|
5562
|
+
usagePerformanceSamples.length;
|
|
5563
|
+
info += `\n\n${theme.bold("Latency")}\n`;
|
|
5564
|
+
info += `${theme.fg("dim", "Avg TTFT:")} ${Math.round(avgTtftMs)}ms\n`;
|
|
5565
|
+
info += `${theme.fg("dim", "Avg Generation:")} ${Math.round(avgGenerationMs)}ms`;
|
|
5566
|
+
const roleRows = aggregateUsagePerformance(usagePerformanceSamples, "role");
|
|
5567
|
+
if (roleRows.length > 0) {
|
|
5568
|
+
info += `\n\n${theme.bold("Roles")}`;
|
|
5569
|
+
for (const row of roleRows) {
|
|
5570
|
+
const requestLabel = row.sampleCount === 1 ? "1 request" : `${row.sampleCount} requests`;
|
|
5571
|
+
info += `\n ${theme.fg("dim", `${row.key}:`)} $${row.cost.toFixed(3)} ${theme.fg("dim", `(${requestLabel})`)}`;
|
|
5572
|
+
}
|
|
5573
|
+
}
|
|
5574
|
+
}
|
|
5087
5575
|
this.chatContainer.addChild(new Spacer(1));
|
|
5088
5576
|
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
5089
5577
|
this.ui.requestRender();
|
|
@@ -5228,6 +5716,28 @@ export class InteractiveMode {
|
|
|
5228
5716
|
this.chatContainer.addChild(new DynamicBorder());
|
|
5229
5717
|
this.ui.requestRender();
|
|
5230
5718
|
}
|
|
5719
|
+
/**
|
|
5720
|
+
* Renders what `collectSessionCommands` returns, so the list a user reads and the list
|
|
5721
|
+
* autocomplete offers are the same list rather than two that agree today. Unregistered
|
|
5722
|
+
* dispatch-only commands are outside that boundary; see the note there.
|
|
5723
|
+
*/
|
|
5724
|
+
handleHelpCommand() {
|
|
5725
|
+
const commands = this.collectSessionCommands();
|
|
5726
|
+
const rows = commands
|
|
5727
|
+
.map((command) => {
|
|
5728
|
+
const invocation = command.argumentHint ? `/${command.name} ${command.argumentHint}` : `/${command.name}`;
|
|
5729
|
+
return `| \`${invocation}\` | ${command.description ?? ""} |`;
|
|
5730
|
+
})
|
|
5731
|
+
.join("\n");
|
|
5732
|
+
const help = `| Command | Description |\n|---------|-------------|\n${rows}\n\nType \`/\` to filter these, or \`/hotkeys\` for keyboard shortcuts.`;
|
|
5733
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
5734
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
5735
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Commands")), 1, 0));
|
|
5736
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
5737
|
+
this.chatContainer.addChild(new Markdown(help, 1, 1, this.getMarkdownThemeWithSettings()));
|
|
5738
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
5739
|
+
this.ui.requestRender();
|
|
5740
|
+
}
|
|
5231
5741
|
async handleClearCommand() {
|
|
5232
5742
|
this.clearStatusIndicator();
|
|
5233
5743
|
try {
|
|
@@ -5291,6 +5801,19 @@ export class InteractiveMode {
|
|
|
5291
5801
|
}
|
|
5292
5802
|
}
|
|
5293
5803
|
async handleBashCommand(command, excludeFromContext = false) {
|
|
5804
|
+
if (this.isHandlingBashCommand) {
|
|
5805
|
+
this.showWarning("A bash command is already running; wait for it to finish");
|
|
5806
|
+
return;
|
|
5807
|
+
}
|
|
5808
|
+
this.isHandlingBashCommand = true;
|
|
5809
|
+
try {
|
|
5810
|
+
await this.runBashCommand(command, excludeFromContext);
|
|
5811
|
+
}
|
|
5812
|
+
finally {
|
|
5813
|
+
this.isHandlingBashCommand = false;
|
|
5814
|
+
}
|
|
5815
|
+
}
|
|
5816
|
+
async runBashCommand(command, excludeFromContext = false) {
|
|
5294
5817
|
const extensionRunner = this.session.extensionRunner;
|
|
5295
5818
|
// Emit user_bash event to let extensions intercept
|
|
5296
5819
|
const eventResult = await extensionRunner.emitUserBash({
|
|
@@ -5364,7 +5887,7 @@ export class InteractiveMode {
|
|
|
5364
5887
|
// Ignore, will be emitted as an event
|
|
5365
5888
|
}
|
|
5366
5889
|
}
|
|
5367
|
-
stop() {
|
|
5890
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
5368
5891
|
this.disposeActiveSelector();
|
|
5369
5892
|
if (this.settingsManager.getShowTerminalProgress()) {
|
|
5370
5893
|
this.ui.terminal.setProgress(false);
|
|
@@ -5378,7 +5901,7 @@ export class InteractiveMode {
|
|
|
5378
5901
|
this.unsubscribe();
|
|
5379
5902
|
}
|
|
5380
5903
|
if (this.isInitialized) {
|
|
5381
|
-
this.stopInteractiveTui();
|
|
5904
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
5382
5905
|
this.isInitialized = false;
|
|
5383
5906
|
}
|
|
5384
5907
|
this.unregisterSignalHandlers();
|