@greppleai/grip 0.0.0-stage → 0.1.3
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 +115 -0
- package/README.md +731 -2
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +13 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/embedded-assets.d.ts +3 -0
- package/dist/bun/embedded-assets.d.ts.map +1 -0
- package/dist/bun/embedded-assets.js +52 -0
- package/dist/bun/embedded-assets.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/bun/restore-sandbox-env.d.ts +13 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
- package/dist/bun/restore-sandbox-env.js +32 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -0
- package/dist/cli/args.d.ts +70 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +442 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/config-selector.d.ts +14 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/file-processor.d.ts +15 -0
- package/dist/cli/file-processor.d.ts.map +1 -0
- package/dist/cli/file-processor.js +60 -0
- package/dist/cli/file-processor.js.map +1 -0
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/list-models.d.ts +9 -0
- package/dist/cli/list-models.d.ts.map +1 -0
- package/dist/cli/list-models.js +98 -0
- package/dist/cli/list-models.js.map +1 -0
- package/dist/cli/session-picker.d.ts +9 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +35 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +14 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +406 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-session.d.ts +970 -0
- package/dist/core/agent-session.d.ts.map +1 -0
- package/dist/core/agent-session.js +5191 -0
- package/dist/core/agent-session.js.map +1 -0
- package/dist/core/auth-guidance.d.ts +5 -0
- package/dist/core/auth-guidance.d.ts.map +1 -0
- package/dist/core/auth-guidance.js +21 -0
- package/dist/core/auth-guidance.js.map +1 -0
- package/dist/core/auth-storage.d.ts +150 -0
- package/dist/core/auth-storage.d.ts.map +1 -0
- package/dist/core/auth-storage.js +557 -0
- package/dist/core/auth-storage.js.map +1 -0
- package/dist/core/bash-executor.d.ts +26 -0
- package/dist/core/bash-executor.d.ts.map +1 -0
- package/dist/core/bash-executor.js +99 -0
- package/dist/core/bash-executor.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts +83 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +220 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +65 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +550 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/compression-detection-ledger.d.ts +34 -0
- package/dist/core/compaction/compression-detection-ledger.d.ts.map +1 -0
- package/dist/core/compaction/compression-detection-ledger.js +52 -0
- package/dist/core/compaction/compression-detection-ledger.js.map +1 -0
- package/dist/core/compaction/compression-detector.d.ts +71 -0
- package/dist/core/compaction/compression-detector.d.ts.map +1 -0
- package/dist/core/compaction/compression-detector.js +407 -0
- package/dist/core/compaction/compression-detector.js.map +1 -0
- package/dist/core/compaction/compression-economics.d.ts +55 -0
- package/dist/core/compaction/compression-economics.d.ts.map +1 -0
- package/dist/core/compaction/compression-economics.js +151 -0
- package/dist/core/compaction/compression-economics.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +28 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +165 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/defaults.d.ts +3 -0
- package/dist/core/defaults.d.ts.map +1 -0
- package/dist/core/defaults.js +2 -0
- package/dist/core/defaults.js.map +1 -0
- package/dist/core/diagnostics.d.ts +18 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +75 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
- package/dist/core/export-html/ansi-to-html.js +245 -0
- package/dist/core/export-html/ansi-to-html.js.map +1 -0
- package/dist/core/export-html/index.d.ts +21 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +230 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +1066 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1864 -0
- package/dist/core/export-html/tool-renderer.d.ts +32 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
- package/dist/core/export-html/tool-renderer.js +108 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +78 -0
- package/dist/core/extensions/loader.d.ts +24 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +613 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +160 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/extensions/runner.js +962 -0
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +1311 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/extensions/types.js +51 -0
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/extensions/wrapper.js +22 -0
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/footer-data-provider.d.ts +52 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -0
- package/dist/core/footer-data-provider.js +318 -0
- package/dist/core/footer-data-provider.js.map +1 -0
- package/dist/core/grip-agent.d.ts +240 -0
- package/dist/core/grip-agent.d.ts.map +1 -0
- package/dist/core/grip-agent.js +1243 -0
- package/dist/core/grip-agent.js.map +1 -0
- package/dist/core/grip-manifest.d.ts +9 -0
- package/dist/core/grip-manifest.d.ts.map +1 -0
- package/dist/core/grip-manifest.js +25 -0
- package/dist/core/grip-manifest.js.map +1 -0
- package/dist/core/hooks/aggregate.d.ts +5 -0
- package/dist/core/hooks/aggregate.d.ts.map +1 -0
- package/dist/core/hooks/aggregate.js +226 -0
- package/dist/core/hooks/aggregate.js.map +1 -0
- package/dist/core/hooks/config.d.ts +14 -0
- package/dist/core/hooks/config.d.ts.map +1 -0
- package/dist/core/hooks/config.js +372 -0
- package/dist/core/hooks/config.js.map +1 -0
- package/dist/core/hooks/executor.d.ts +12 -0
- package/dist/core/hooks/executor.d.ts.map +1 -0
- package/dist/core/hooks/executor.js +708 -0
- package/dist/core/hooks/executor.js.map +1 -0
- package/dist/core/hooks/index.d.ts +9 -0
- package/dist/core/hooks/index.d.ts.map +1 -0
- package/dist/core/hooks/index.js +14 -0
- package/dist/core/hooks/index.js.map +1 -0
- package/dist/core/hooks/matcher.d.ts +10 -0
- package/dist/core/hooks/matcher.d.ts.map +1 -0
- package/dist/core/hooks/matcher.js +70 -0
- package/dist/core/hooks/matcher.js.map +1 -0
- package/dist/core/hooks/trust-store.d.ts +38 -0
- package/dist/core/hooks/trust-store.d.ts.map +1 -0
- package/dist/core/hooks/trust-store.js +167 -0
- package/dist/core/hooks/trust-store.js.map +1 -0
- package/dist/core/hooks/types.d.ts +234 -0
- package/dist/core/hooks/types.d.ts.map +1 -0
- package/dist/core/hooks/types.js +52 -0
- package/dist/core/hooks/types.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +21 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +82 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/instruction-resource-loader.d.ts +81 -0
- package/dist/core/instruction-resource-loader.d.ts.map +1 -0
- package/dist/core/instruction-resource-loader.js +574 -0
- package/dist/core/instruction-resource-loader.js.map +1 -0
- package/dist/core/keybindings.d.ts +399 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/keybindings.js +299 -0
- package/dist/core/keybindings.js.map +1 -0
- package/dist/core/lsp/abort.d.ts +3 -0
- package/dist/core/lsp/abort.d.ts.map +1 -0
- package/dist/core/lsp/abort.js +28 -0
- package/dist/core/lsp/abort.js.map +1 -0
- package/dist/core/lsp/client.d.ts +108 -0
- package/dist/core/lsp/client.d.ts.map +1 -0
- package/dist/core/lsp/client.js +586 -0
- package/dist/core/lsp/client.js.map +1 -0
- package/dist/core/lsp/config-loader.d.ts +44 -0
- package/dist/core/lsp/config-loader.d.ts.map +1 -0
- package/dist/core/lsp/config-loader.js +223 -0
- package/dist/core/lsp/config-loader.js.map +1 -0
- package/dist/core/lsp/config.d.ts +134 -0
- package/dist/core/lsp/config.d.ts.map +1 -0
- package/dist/core/lsp/config.js +682 -0
- package/dist/core/lsp/config.js.map +1 -0
- package/dist/core/lsp/diagnostics.d.ts +18 -0
- package/dist/core/lsp/diagnostics.d.ts.map +1 -0
- package/dist/core/lsp/diagnostics.js +311 -0
- package/dist/core/lsp/diagnostics.js.map +1 -0
- package/dist/core/lsp/file-sync.d.ts +58 -0
- package/dist/core/lsp/file-sync.d.ts.map +1 -0
- package/dist/core/lsp/file-sync.js +432 -0
- package/dist/core/lsp/file-sync.js.map +1 -0
- package/dist/core/lsp/integration.d.ts +62 -0
- package/dist/core/lsp/integration.d.ts.map +1 -0
- package/dist/core/lsp/integration.js +145 -0
- package/dist/core/lsp/integration.js.map +1 -0
- package/dist/core/lsp/language-map.d.ts +54 -0
- package/dist/core/lsp/language-map.d.ts.map +1 -0
- package/dist/core/lsp/language-map.js +300 -0
- package/dist/core/lsp/language-map.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +120 -0
- package/dist/core/lsp/manager.d.ts.map +1 -0
- package/dist/core/lsp/manager.js +636 -0
- package/dist/core/lsp/manager.js.map +1 -0
- package/dist/core/lsp/navigation.d.ts +32 -0
- package/dist/core/lsp/navigation.d.ts.map +1 -0
- package/dist/core/lsp/navigation.js +327 -0
- package/dist/core/lsp/navigation.js.map +1 -0
- package/dist/core/lsp/portable-path.d.ts +18 -0
- package/dist/core/lsp/portable-path.d.ts.map +1 -0
- package/dist/core/lsp/portable-path.js +83 -0
- package/dist/core/lsp/portable-path.js.map +1 -0
- package/dist/core/lsp/refactor.d.ts +32 -0
- package/dist/core/lsp/refactor.d.ts.map +1 -0
- package/dist/core/lsp/refactor.js +519 -0
- package/dist/core/lsp/refactor.js.map +1 -0
- package/dist/core/lsp/transport.d.ts +39 -0
- package/dist/core/lsp/transport.d.ts.map +1 -0
- package/dist/core/lsp/transport.js +261 -0
- package/dist/core/lsp/transport.js.map +1 -0
- package/dist/core/messages.d.ts +34 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +140 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/model-registry.d.ts +148 -0
- package/dist/core/model-registry.d.ts.map +1 -0
- package/dist/core/model-registry.js +812 -0
- package/dist/core/model-registry.js.map +1 -0
- package/dist/core/model-resolver.d.ts +115 -0
- package/dist/core/model-resolver.d.ts.map +1 -0
- package/dist/core/model-resolver.js +498 -0
- package/dist/core/model-resolver.js.map +1 -0
- package/dist/core/output-guard.d.ts +7 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +89 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +209 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +2341 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +57 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/prompt-templates.js +353 -0
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/provider-display-names.d.ts +2 -0
- package/dist/core/provider-display-names.d.ts.map +1 -0
- package/dist/core/provider-display-names.js +33 -0
- package/dist/core/provider-display-names.js.map +1 -0
- package/dist/core/remote-workspace-protocol/contract.d.ts +465 -0
- package/dist/core/remote-workspace-protocol/contract.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/contract.js +825 -0
- package/dist/core/remote-workspace-protocol/contract.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts +16 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js +54 -0
- package/dist/core/remote-workspace-protocol/guest-shared-client.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts +23 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js +112 -0
- package/dist/core/remote-workspace-protocol/guest-shared-outbound-scheduler.js.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts +30 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js +243 -0
- package/dist/core/remote-workspace-protocol/guest-shared-uplink.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts +3 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js +31 -0
- package/dist/core/remote-workspace-protocol/lima-owner-directory.js.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts +9 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js +89 -0
- package/dist/core/remote-workspace-protocol/lima-owner-lock.js.map +1 -0
- package/dist/core/remote-workspace-protocol/session.d.ts +249 -0
- package/dist/core/remote-workspace-protocol/session.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/session.js +1842 -0
- package/dist/core/remote-workspace-protocol/session.js.map +1 -0
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts +71 -0
- package/dist/core/remote-workspace-protocol/shared-transport.d.ts.map +1 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js +68 -0
- package/dist/core/remote-workspace-protocol/shared-transport.js.map +1 -0
- package/dist/core/resolve-config-value.d.ts +32 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -0
- package/dist/core/resolve-config-value.js +257 -0
- package/dist/core/resolve-config-value.js.map +1 -0
- package/dist/core/resource-loader.d.ts +200 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +1209 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/rules.d.ts +40 -0
- package/dist/core/rules.d.ts.map +1 -0
- package/dist/core/rules.js +57 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/sandbox/command.d.ts +25 -0
- package/dist/core/sandbox/command.d.ts.map +1 -0
- package/dist/core/sandbox/command.js +102 -0
- package/dist/core/sandbox/command.js.map +1 -0
- package/dist/core/sandbox/config.d.ts +91 -0
- package/dist/core/sandbox/config.d.ts.map +1 -0
- package/dist/core/sandbox/config.js +88 -0
- package/dist/core/sandbox/config.js.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts +11 -0
- package/dist/core/sandbox/lima-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/lima-guest-binary.js +140 -0
- package/dist/core/sandbox/lima-guest-binary.js.map +1 -0
- package/dist/core/sandbox/lima.d.ts +57 -0
- package/dist/core/sandbox/lima.d.ts.map +1 -0
- package/dist/core/sandbox/lima.js +716 -0
- package/dist/core/sandbox/lima.js.map +1 -0
- package/dist/core/sandbox/shared-lima-roots.d.ts +11 -0
- package/dist/core/sandbox/shared-lima-roots.d.ts.map +1 -0
- package/dist/core/sandbox/shared-lima-roots.js +25 -0
- package/dist/core/sandbox/shared-lima-roots.js.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts +8 -0
- package/dist/core/sandbox/update-shared-guest-binary.d.ts.map +1 -0
- package/dist/core/sandbox/update-shared-guest-binary.js +100 -0
- package/dist/core/sandbox/update-shared-guest-binary.js.map +1 -0
- package/dist/core/session/constants.d.ts +2 -0
- package/dist/core/session/constants.d.ts.map +1 -0
- package/dist/core/session/constants.js +2 -0
- package/dist/core/session/constants.js.map +1 -0
- package/dist/core/session/context-metadata.d.ts +5 -0
- package/dist/core/session/context-metadata.d.ts.map +1 -0
- package/dist/core/session/context-metadata.js +55 -0
- package/dist/core/session/context-metadata.js.map +1 -0
- package/dist/core/session/context.d.ts +14 -0
- package/dist/core/session/context.d.ts.map +1 -0
- package/dist/core/session/context.js +117 -0
- package/dist/core/session/context.js.map +1 -0
- package/dist/core/session/ids.d.ts +9 -0
- package/dist/core/session/ids.d.ts.map +1 -0
- package/dist/core/session/ids.js +21 -0
- package/dist/core/session/ids.js.map +1 -0
- package/dist/core/session/in-memory-session-manager.d.ts +17 -0
- package/dist/core/session/in-memory-session-manager.d.ts.map +1 -0
- package/dist/core/session/in-memory-session-manager.js +40 -0
- package/dist/core/session/in-memory-session-manager.js.map +1 -0
- package/dist/core/session/in-memory-session.d.ts +7 -0
- package/dist/core/session/in-memory-session.d.ts.map +1 -0
- package/dist/core/session/in-memory-session.js +11 -0
- package/dist/core/session/in-memory-session.js.map +1 -0
- package/dist/core/session/jsonl-errors.d.ts +36 -0
- package/dist/core/session/jsonl-errors.d.ts.map +1 -0
- package/dist/core/session/jsonl-errors.js +49 -0
- package/dist/core/session/jsonl-errors.js.map +1 -0
- package/dist/core/session/jsonl-helpers.d.ts +20 -0
- package/dist/core/session/jsonl-helpers.d.ts.map +1 -0
- package/dist/core/session/jsonl-helpers.js +21 -0
- package/dist/core/session/jsonl-helpers.js.map +1 -0
- package/dist/core/session/local-session-manager.d.ts +30 -0
- package/dist/core/session/local-session-manager.d.ts.map +1 -0
- package/dist/core/session/local-session-manager.js +205 -0
- package/dist/core/session/local-session-manager.js.map +1 -0
- package/dist/core/session/local-session.d.ts +9 -0
- package/dist/core/session/local-session.d.ts.map +1 -0
- package/dist/core/session/local-session.js +15 -0
- package/dist/core/session/local-session.js.map +1 -0
- package/dist/core/session/migrations.d.ts +11 -0
- package/dist/core/session/migrations.d.ts.map +1 -0
- package/dist/core/session/migrations.js +78 -0
- package/dist/core/session/migrations.js.map +1 -0
- package/dist/core/session/remote-session-client.d.ts +83 -0
- package/dist/core/session/remote-session-client.d.ts.map +1 -0
- package/dist/core/session/remote-session-client.js +224 -0
- package/dist/core/session/remote-session-client.js.map +1 -0
- package/dist/core/session/remote-session-manager.d.ts +32 -0
- package/dist/core/session/remote-session-manager.d.ts.map +1 -0
- package/dist/core/session/remote-session-manager.js +124 -0
- package/dist/core/session/remote-session-manager.js.map +1 -0
- package/dist/core/session/remote-session.d.ts +20 -0
- package/dist/core/session/remote-session.d.ts.map +1 -0
- package/dist/core/session/remote-session.js +33 -0
- package/dist/core/session/remote-session.js.map +1 -0
- package/dist/core/session/session-manager.d.ts +18 -0
- package/dist/core/session/session-manager.d.ts.map +1 -0
- package/dist/core/session/session-manager.js +2 -0
- package/dist/core/session/session-manager.js.map +1 -0
- package/dist/core/session/session.d.ts +140 -0
- package/dist/core/session/session.d.ts.map +1 -0
- package/dist/core/session/session.js +461 -0
- package/dist/core/session/session.js.map +1 -0
- package/dist/core/session/stores/in-memory-session-store.d.ts +39 -0
- package/dist/core/session/stores/in-memory-session-store.d.ts.map +1 -0
- package/dist/core/session/stores/in-memory-session-store.js +168 -0
- package/dist/core/session/stores/in-memory-session-store.js.map +1 -0
- package/dist/core/session/stores/jsonl-session-store.d.ts +97 -0
- package/dist/core/session/stores/jsonl-session-store.d.ts.map +1 -0
- package/dist/core/session/stores/jsonl-session-store.js +1140 -0
- package/dist/core/session/stores/jsonl-session-store.js.map +1 -0
- package/dist/core/session/stores/remote-session-store.d.ts +44 -0
- package/dist/core/session/stores/remote-session-store.d.ts.map +1 -0
- package/dist/core/session/stores/remote-session-store.js +159 -0
- package/dist/core/session/stores/remote-session-store.js.map +1 -0
- package/dist/core/session/stores/session-store.d.ts +42 -0
- package/dist/core/session/stores/session-store.d.ts.map +1 -0
- package/dist/core/session/stores/session-store.js +2 -0
- package/dist/core/session/stores/session-store.js.map +1 -0
- package/dist/core/session/types.d.ts +141 -0
- package/dist/core/session/types.d.ts.map +1 -0
- package/dist/core/session/types.js +2 -0
- package/dist/core/session/types.js.map +1 -0
- package/dist/core/session/usage-ledger.d.ts +18 -0
- package/dist/core/session/usage-ledger.d.ts.map +1 -0
- package/dist/core/session/usage-ledger.js +68 -0
- package/dist/core/session/usage-ledger.js.map +1 -0
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/settings-manager.d.ts +314 -0
- package/dist/core/settings-manager.d.ts.map +1 -0
- package/dist/core/settings-manager.js +991 -0
- package/dist/core/settings-manager.js.map +1 -0
- package/dist/core/skills.d.ts +40 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +57 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/slash-commands.d.ts +15 -0
- package/dist/core/slash-commands.d.ts.map +1 -0
- package/dist/core/slash-commands.js +36 -0
- package/dist/core/slash-commands.js.map +1 -0
- package/dist/core/source-info.d.ts +26 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +27 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +32 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +131 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/telemetry.d.ts +3 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/core/telemetry.js +9 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/timings.d.ts +10 -0
- package/dist/core/timings.d.ts.map +1 -0
- package/dist/core/timings.js +41 -0
- package/dist/core/timings.js.map +1 -0
- package/dist/core/tools/bash.d.ts +51 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +321 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/compress-context.d.ts +27 -0
- package/dist/core/tools/compress-context.d.ts.map +1 -0
- package/dist/core/tools/compress-context.js +142 -0
- package/dist/core/tools/compress-context.js.map +1 -0
- package/dist/core/tools/edit-diff.d.ts +82 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +327 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +50 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +324 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +8 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +69 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +22 -0
- package/dist/core/tools/find.d.ts.map +1 -0
- package/dist/core/tools/find.js +268 -0
- package/dist/core/tools/find.js.map +1 -0
- package/dist/core/tools/grep.d.ts +27 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +332 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/hash-store.d.ts +12 -0
- package/dist/core/tools/hash-store.d.ts.map +1 -0
- package/dist/core/tools/hash-store.js +48 -0
- package/dist/core/tools/hash-store.js.map +1 -0
- package/dist/core/tools/hashline/apply.d.ts +40 -0
- package/dist/core/tools/hashline/apply.d.ts.map +1 -0
- package/dist/core/tools/hashline/apply.js +261 -0
- package/dist/core/tools/hashline/apply.js.map +1 -0
- package/dist/core/tools/hashline/hash.d.ts +56 -0
- package/dist/core/tools/hashline/hash.d.ts.map +1 -0
- package/dist/core/tools/hashline/hash.js +196 -0
- package/dist/core/tools/hashline/hash.js.map +1 -0
- package/dist/core/tools/hashline/parse.d.ts +8 -0
- package/dist/core/tools/hashline/parse.d.ts.map +1 -0
- package/dist/core/tools/hashline/parse.js +41 -0
- package/dist/core/tools/hashline/parse.js.map +1 -0
- package/dist/core/tools/hashline/resolve.d.ts +51 -0
- package/dist/core/tools/hashline/resolve.d.ts.map +1 -0
- package/dist/core/tools/hashline/resolve.js +187 -0
- package/dist/core/tools/hashline/resolve.js.map +1 -0
- package/dist/core/tools/hashline-utils.d.ts +6 -0
- package/dist/core/tools/hashline-utils.d.ts.map +1 -0
- package/dist/core/tools/hashline-utils.js +23 -0
- package/dist/core/tools/hashline-utils.js.map +1 -0
- package/dist/core/tools/index.d.ts +36 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +141 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/ls.d.ts +21 -0
- package/dist/core/tools/ls.d.ts.map +1 -0
- package/dist/core/tools/ls.js +156 -0
- package/dist/core/tools/ls.js.map +1 -0
- package/dist/core/tools/operations.d.ts +212 -0
- package/dist/core/tools/operations.d.ts.map +1 -0
- package/dist/core/tools/operations.js +793 -0
- package/dist/core/tools/operations.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts +54 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +184 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/path-utils.d.ts +9 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -0
- package/dist/core/tools/path-utils.js +99 -0
- package/dist/core/tools/path-utils.js.map +1 -0
- package/dist/core/tools/read.d.ts +32 -0
- package/dist/core/tools/read.d.ts.map +1 -0
- package/dist/core/tools/read.js +318 -0
- package/dist/core/tools/read.js.map +1 -0
- package/dist/core/tools/render-utils.d.ts +33 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +61 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/replace-diff.d.ts +16 -0
- package/dist/core/tools/replace-diff.d.ts.map +1 -0
- package/dist/core/tools/replace-diff.js +117 -0
- package/dist/core/tools/replace-diff.js.map +1 -0
- package/dist/core/tools/replace-normalize.d.ts +2 -0
- package/dist/core/tools/replace-normalize.d.ts.map +1 -0
- package/dist/core/tools/replace-normalize.js +73 -0
- package/dist/core/tools/replace-normalize.js.map +1 -0
- package/dist/core/tools/replace-render.d.ts +51 -0
- package/dist/core/tools/replace-render.d.ts.map +1 -0
- package/dist/core/tools/replace-render.js +162 -0
- package/dist/core/tools/replace-render.js.map +1 -0
- package/dist/core/tools/runtime.d.ts +2 -0
- package/dist/core/tools/runtime.d.ts.map +1 -0
- package/dist/core/tools/runtime.js +6 -0
- package/dist/core/tools/runtime.js.map +1 -0
- package/dist/core/tools/session-history.d.ts +55 -0
- package/dist/core/tools/session-history.d.ts.map +1 -0
- package/dist/core/tools/session-history.js +649 -0
- package/dist/core/tools/session-history.js.map +1 -0
- package/dist/core/tools/subagent-agents.d.ts +29 -0
- package/dist/core/tools/subagent-agents.d.ts.map +1 -0
- package/dist/core/tools/subagent-agents.js +223 -0
- package/dist/core/tools/subagent-agents.js.map +1 -0
- package/dist/core/tools/subagent.d.ts +185 -0
- package/dist/core/tools/subagent.d.ts.map +1 -0
- package/dist/core/tools/subagent.js +1309 -0
- package/dist/core/tools/subagent.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +34 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +39 -0
- package/dist/core/tools/truncate.d.ts.map +1 -0
- package/dist/core/tools/truncate.js +215 -0
- package/dist/core/tools/truncate.js.map +1 -0
- package/dist/core/tools/websearch.d.ts +33 -0
- package/dist/core/tools/websearch.d.ts.map +1 -0
- package/dist/core/tools/websearch.js +341 -0
- package/dist/core/tools/websearch.js.map +1 -0
- package/dist/core/tools/workspace-tool-host.d.ts +102 -0
- package/dist/core/tools/workspace-tool-host.d.ts.map +1 -0
- package/dist/core/tools/workspace-tool-host.js +348 -0
- package/dist/core/tools/workspace-tool-host.js.map +1 -0
- package/dist/core/tools/write.d.ts +16 -0
- package/dist/core/tools/write.d.ts.map +1 -0
- package/dist/core/tools/write.js +204 -0
- package/dist/core/tools/write.js.map +1 -0
- package/dist/core/workspace-identity.d.ts +7 -0
- package/dist/core/workspace-identity.d.ts.map +1 -0
- package/dist/core/workspace-identity.js +2 -0
- package/dist/core/workspace-identity.js.map +1 -0
- package/dist/daemon/activity-budget.d.ts +10 -0
- package/dist/daemon/activity-budget.d.ts.map +1 -0
- package/dist/daemon/activity-budget.js +34 -0
- package/dist/daemon/activity-budget.js.map +1 -0
- package/dist/daemon/command.d.ts +6 -0
- package/dist/daemon/command.d.ts.map +1 -0
- package/dist/daemon/command.js +58 -0
- package/dist/daemon/command.js.map +1 -0
- package/dist/daemon/config.d.ts +45 -0
- package/dist/daemon/config.d.ts.map +1 -0
- package/dist/daemon/config.js +319 -0
- package/dist/daemon/config.js.map +1 -0
- package/dist/daemon/help.d.ts +2 -0
- package/dist/daemon/help.d.ts.map +1 -0
- package/dist/daemon/help.js +42 -0
- package/dist/daemon/help.js.map +1 -0
- package/dist/daemon/lsp-config.d.ts +4 -0
- package/dist/daemon/lsp-config.d.ts.map +1 -0
- package/dist/daemon/lsp-config.js +155 -0
- package/dist/daemon/lsp-config.js.map +1 -0
- package/dist/daemon/outbound-budget.d.ts +10 -0
- package/dist/daemon/outbound-budget.d.ts.map +1 -0
- package/dist/daemon/outbound-budget.js +30 -0
- package/dist/daemon/outbound-budget.js.map +1 -0
- package/dist/daemon/security.d.ts +19 -0
- package/dist/daemon/security.d.ts.map +1 -0
- package/dist/daemon/security.js +177 -0
- package/dist/daemon/security.js.map +1 -0
- package/dist/daemon/server.d.ts +20 -0
- package/dist/daemon/server.d.ts.map +1 -0
- package/dist/daemon/server.js +480 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/shared-connection.d.ts +33 -0
- package/dist/daemon/shared-connection.d.ts.map +1 -0
- package/dist/daemon/shared-connection.js +271 -0
- package/dist/daemon/shared-connection.js.map +1 -0
- package/dist/daemon/shared-revision.d.ts +5 -0
- package/dist/daemon/shared-revision.d.ts.map +1 -0
- package/dist/daemon/shared-revision.js +61 -0
- package/dist/daemon/shared-revision.js.map +1 -0
- package/dist/daemon/shared-roots.d.ts +9 -0
- package/dist/daemon/shared-roots.d.ts.map +1 -0
- package/dist/daemon/shared-roots.js +33 -0
- package/dist/daemon/shared-roots.js.map +1 -0
- package/dist/daemon/workspace-runtime.d.ts +46 -0
- package/dist/daemon/workspace-runtime.d.ts.map +1 -0
- package/dist/daemon/workspace-runtime.js +705 -0
- package/dist/daemon/workspace-runtime.js.map +1 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +39 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +765 -0
- package/dist/main.js.map +1 -0
- package/dist/migrations.d.ts +35 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +412 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +30 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/assistant-message.js +159 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +36 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/bash-execution.js +173 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts +18 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +54 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +17 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +45 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +17 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +46 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts +74 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +514 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.js +33 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts +25 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-editor.js +85 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -0
- package/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-message.js +81 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/modes/interactive/components/diff.d.ts.map +1 -0
- package/dist/modes/interactive/components/diff.js +118 -0
- package/dist/modes/interactive/components/diff.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.js +21 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-editor.js +128 -0
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-input.js +61 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/extension-selector.d.ts +26 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-selector.js +83 -0
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +36 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -0
- package/dist/modes/interactive/components/footer.js +227 -0
- package/dist/modes/interactive/components/footer.js.map +1 -0
- package/dist/modes/interactive/components/hook-execution.d.ts +19 -0
- package/dist/modes/interactive/components/hook-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/hook-execution.js +185 -0
- package/dist/modes/interactive/components/hook-execution.js.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.js +36 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +55 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
- package/dist/modes/interactive/components/login-dialog.js +176 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts +4 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.js +19 -0
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
- package/dist/modes/interactive/components/mermaid.d.ts +14 -0
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
- package/dist/modes/interactive/components/mermaid.js +228 -0
- package/dist/modes/interactive/components/mermaid.js.map +1 -0
- package/dist/modes/interactive/components/message-card.d.ts +9 -0
- package/dist/modes/interactive/components/message-card.d.ts.map +1 -0
- package/dist/modes/interactive/components/message-card.js +24 -0
- package/dist/modes/interactive/components/message-card.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +52 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/model-selector.js +296 -0
- package/dist/modes/interactive/components/model-selector.js.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.js +165 -0
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +50 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.js +300 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +22 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.js +144 -0
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +108 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector.js +835 -0
- package/dist/modes/interactive/components/session-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +81 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-selector.js +455 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.js +39 -0
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +18 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +48 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/theme-selector.js +50 -0
- package/dist/modes/interactive/components/theme-selector.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.js +51 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +67 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution.js +312 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +127 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +1098 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.js +114 -0
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +16 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message.js +50 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.js +33 -0
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +554 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.js +5870 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +86 -0
- package/dist/modes/interactive/theme/light.json +85 -0
- package/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/dist/modes/interactive/theme/theme.d.ts +109 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme.js +1040 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -0
- package/dist/modes/json-event.d.ts +28 -0
- package/dist/modes/json-event.d.ts.map +1 -0
- package/dist/modes/json-event.js +12 -0
- package/dist/modes/json-event.js.map +1 -0
- package/dist/modes/print-mode.d.ts +29 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +142 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +321 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-client.js +755 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-mode.js +963 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -0
- package/dist/modes/rpc/rpc-types.d.ts +652 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -0
- package/dist/package-manager-cli.d.ts +4 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +488 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +25 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/changelog.d.ts +23 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +74 -0
- package/dist/utils/changelog.js.map +1 -0
- package/dist/utils/child-process.d.ts +15 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +88 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +13 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -0
- package/dist/utils/clipboard-image.js +248 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +20 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/clipboard.d.ts +4 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +171 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/deprecation.d.ts +4 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/deprecation.js +13 -0
- package/dist/utils/deprecation.js.map +1 -0
- package/dist/utils/directory-completions.d.ts +5 -0
- package/dist/utils/directory-completions.d.ts.map +1 -0
- package/dist/utils/directory-completions.js +64 -0
- package/dist/utils/directory-completions.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts +6 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +166 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +8 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +26 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +5 -0
- package/dist/utils/fs-watch.d.ts.map +1 -0
- package/dist/utils/fs-watch.js +25 -0
- package/dist/utils/fs-watch.js.map +1 -0
- package/dist/utils/git.d.ts +27 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +237 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/grip-user-agent.d.ts +2 -0
- package/dist/utils/grip-user-agent.d.ts.map +1 -0
- package/dist/utils/grip-user-agent.js +5 -0
- package/dist/utils/grip-user-agent.js.map +1 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +40 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/image-convert.d.ts +12 -0
- package/dist/utils/image-convert.d.ts.map +1 -0
- package/dist/utils/image-convert.js +43 -0
- package/dist/utils/image-convert.js.map +1 -0
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +133 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +19 -0
- package/dist/utils/image-resize.d.ts.map +1 -0
- package/dist/utils/image-resize.js +86 -0
- package/dist/utils/image-resize.js.map +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +7 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/mime.d.ts +3 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +110 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +22 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/dist/utils/paths.d.ts +31 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +87 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/photon.d.ts +22 -0
- package/dist/utils/photon.d.ts.map +1 -0
- package/dist/utils/photon.js +126 -0
- package/dist/utils/photon.js.map +1 -0
- package/dist/utils/shell.d.ts +28 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +188 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +125 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +5 -0
- package/dist/utils/tools-manager.d.ts.map +1 -0
- package/dist/utils/tools-manager.js +329 -0
- package/dist/utils/tools-manager.js.map +1 -0
- package/dist/utils/version-check.d.ts +14 -0
- package/dist/utils/version-check.d.ts.map +1 -0
- package/dist/utils/version-check.js +91 -0
- package/dist/utils/version-check.js.map +1 -0
- package/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/utils/windows-self-update.d.ts.map +1 -0
- package/dist/utils/windows-self-update.js +77 -0
- package/dist/utils/windows-self-update.js.map +1 -0
- package/docs/compaction.md +408 -0
- package/docs/custom-provider.md +714 -0
- package/docs/daemon-architecture.md +399 -0
- package/docs/daemon.md +112 -0
- package/docs/development.md +72 -0
- package/docs/docs.json +164 -0
- package/docs/extensions.md +2745 -0
- package/docs/hooks.md +85 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +77 -0
- package/docs/json.md +89 -0
- package/docs/keybindings.md +230 -0
- package/docs/lsp.md +499 -0
- package/docs/migrating-to-grip.md +18 -0
- package/docs/models.md +496 -0
- package/docs/packages.md +228 -0
- package/docs/prompt-templates.md +88 -0
- package/docs/providers.md +251 -0
- package/docs/quickstart.md +159 -0
- package/docs/rpc.md +1492 -0
- package/docs/rules.md +117 -0
- package/docs/sandbox.md +104 -0
- package/docs/sdk.md +360 -0
- package/docs/session-format.md +414 -0
- package/docs/sessions.md +137 -0
- package/docs/settings.md +372 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +246 -0
- package/docs/terminal-setup.md +112 -0
- package/docs/termux.md +112 -0
- package/docs/themes.md +295 -0
- package/docs/tmux.md +61 -0
- package/docs/tui.md +918 -0
- package/docs/usage.md +304 -0
- package/docs/windows.md +17 -0
- package/examples/README.md +29 -0
- package/examples/daemon-docker/Dockerfile +43 -0
- package/examples/daemon-docker/README.md +45 -0
- package/examples/extensions/README.md +208 -0
- package/examples/extensions/auto-commit-on-exit.ts +46 -0
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/border-status-editor.ts +145 -0
- package/examples/extensions/built-in-tool-renderer.ts +259 -0
- package/examples/extensions/claude-rules.ts +86 -0
- package/examples/extensions/commands.ts +82 -0
- package/examples/extensions/confirm-destructive.ts +59 -0
- package/examples/extensions/custom-compaction.ts +131 -0
- package/examples/extensions/custom-footer.ts +79 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +707 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +402 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +104 -0
- package/examples/extensions/dirty-repo-guard.ts +59 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +133 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/examples/extensions/dynamic-resources/RULES.md +8 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +16 -0
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/git-merge-and-resolve.ts +125 -0
- package/examples/extensions/github-issue-autocomplete.ts +191 -0
- package/examples/extensions/handoff.ts +191 -0
- package/examples/extensions/hello.ts +26 -0
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/inline-bash.ts +104 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +194 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/minimal-mode.ts +428 -0
- package/examples/extensions/modal-editor.ts +86 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +55 -0
- package/examples/extensions/overlay-qa-tests.ts +1374 -0
- package/examples/extensions/overlay-test.ts +171 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +65 -0
- package/examples/extensions/plan-mode/index.ts +346 -0
- package/examples/extensions/plan-mode/utils.ts +172 -0
- package/examples/extensions/preset.ts +426 -0
- package/examples/extensions/prompt-customizer.ts +97 -0
- package/examples/extensions/protected-paths.ts +29 -0
- package/examples/extensions/provider-payload.ts +18 -0
- package/examples/extensions/qna.ts +125 -0
- package/examples/extensions/question.ts +294 -0
- package/examples/extensions/questionnaire.ts +459 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/rpc-demo.ts +118 -0
- package/examples/extensions/sandbox/index.ts +320 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +381 -0
- package/examples/extensions/space-invaders.ts +597 -0
- package/examples/extensions/status-line.ts +32 -0
- package/examples/extensions/structured-output.ts +65 -0
- package/examples/extensions/subagent/README.md +175 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +1083 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +186 -0
- package/examples/extensions/system-prompt-header.ts +17 -0
- package/examples/extensions/tic-tac-toe.ts +1091 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +306 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +141 -0
- package/examples/extensions/trigger-compact.ts +50 -0
- package/examples/extensions/truncated-tool.ts +200 -0
- package/examples/extensions/widget-placement.ts +9 -0
- package/examples/extensions/with-deps/index.ts +32 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/working-indicator.ts +123 -0
- package/examples/extensions/working-message-test.ts +25 -0
- package/examples/remote-session-server/README.md +71 -0
- package/examples/remote-session-server/server.ts +353 -0
- package/examples/rpc-extension-ui.ts +642 -0
- package/examples/sdk/01-minimal.ts +27 -0
- package/examples/sdk/02-custom-model.ts +54 -0
- package/examples/sdk/03-custom-prompt.ts +76 -0
- package/examples/sdk/04-skills.ts +60 -0
- package/examples/sdk/05-tools.ts +55 -0
- package/examples/sdk/06-extensions.ts +94 -0
- package/examples/sdk/07-context-files.ts +47 -0
- package/examples/sdk/08-prompt-templates.ts +56 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +50 -0
- package/examples/sdk/10-settings.ts +58 -0
- package/examples/sdk/11-sessions.ts +54 -0
- package/examples/sdk/12-full-control.ts +79 -0
- package/examples/sdk/13-session-runtime.ts +49 -0
- package/examples/sdk/README.md +94 -0
- package/npm-shrinkwrap.json +1878 -0
- package/package.json +101 -5
|
@@ -0,0 +1,1842 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS, decodeCanonicalBase64, decodeRemoteWorkspaceMessage, getRemoteWorkspaceMethodKind, hashRemoteWorkspaceCatalog, isRemoteWorkspaceMethod, negotiateRemoteWorkspaceHandshake, parseRemoteWorkspaceClientMessage, parseRemoteWorkspaceRequestParams, parseRemoteWorkspaceResult, parseRemoteWorkspaceServerMessage, REMOTE_WORKSPACE_PROTOCOL_VERSIONS, RemoteWorkspaceNegotiationError, RemoteWorkspaceValidationError, validateRemoteWorkspaceCatalog, validateRemoteWorkspaceHandshakeAck, validateRemoteWorkspaceLocalToolSchemas, validateRemoteWorkspaceProtocolLimits, } from "./contract.js";
|
|
3
|
+
export class RemoteWorkspaceRequestError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
executionState;
|
|
6
|
+
retryable;
|
|
7
|
+
details;
|
|
8
|
+
constructor(error) {
|
|
9
|
+
super(error.message);
|
|
10
|
+
this.name = "RemoteWorkspaceRequestError";
|
|
11
|
+
this.code = error.code;
|
|
12
|
+
this.executionState = error.executionState;
|
|
13
|
+
this.retryable = error.retryable;
|
|
14
|
+
this.details = error.details;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export class RemoteWorkspaceDisconnectedError extends Error {
|
|
18
|
+
executionState;
|
|
19
|
+
constructor(message, executionState) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "RemoteWorkspaceDisconnectedError";
|
|
22
|
+
this.executionState = executionState;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class RemoteWorkspaceTerminationError extends Error {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "RemoteWorkspaceTerminationError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class RemoteWorkspaceTransportError extends Error {
|
|
32
|
+
constructor(error) {
|
|
33
|
+
super(error instanceof Error ? error.message : String(error));
|
|
34
|
+
this.name = "RemoteWorkspaceTransportError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function classifyValidationClose(error) {
|
|
38
|
+
return error instanceof RemoteWorkspaceValidationError && error.message.includes("exceeds")
|
|
39
|
+
? "message_too_large"
|
|
40
|
+
: "invalid_payload";
|
|
41
|
+
}
|
|
42
|
+
function serializeProtocolMessage(value) {
|
|
43
|
+
try {
|
|
44
|
+
const serialized = JSON.stringify(value);
|
|
45
|
+
if (serialized === undefined)
|
|
46
|
+
throw new Error("Value is not JSON-serializable");
|
|
47
|
+
return serialized;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw new RemoteWorkspaceValidationError(`Protocol value is not JSON-serializable: ${error instanceof Error ? error.message : String(error)}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function prepareClientOutbound(message, limits) {
|
|
54
|
+
const serialized = serializeProtocolMessage(message);
|
|
55
|
+
const decoded = decodeRemoteWorkspaceMessage(serialized, limits);
|
|
56
|
+
return { message: parseRemoteWorkspaceClientMessage(decoded, limits), bytes: Buffer.byteLength(serialized, "utf8") };
|
|
57
|
+
}
|
|
58
|
+
function prepareServerOutbound(message, limits) {
|
|
59
|
+
const serialized = serializeProtocolMessage(message);
|
|
60
|
+
const decoded = decodeRemoteWorkspaceMessage(serialized, limits);
|
|
61
|
+
return { message: parseRemoteWorkspaceServerMessage(decoded, limits), bytes: Buffer.byteLength(serialized, "utf8") };
|
|
62
|
+
}
|
|
63
|
+
function cloneCatalog(catalog) {
|
|
64
|
+
const cloned = JSON.parse(serializeProtocolMessage(catalog));
|
|
65
|
+
validateRemoteWorkspaceCatalog(cloned);
|
|
66
|
+
return cloned;
|
|
67
|
+
}
|
|
68
|
+
function cloneHandshakeAck(ack) {
|
|
69
|
+
return JSON.parse(serializeProtocolMessage(ack));
|
|
70
|
+
}
|
|
71
|
+
const WORKSPACE_IDENTITY_RESULT_METHODS = new Set([
|
|
72
|
+
"workspace.read",
|
|
73
|
+
"workspace.stat",
|
|
74
|
+
"workspace.readdir",
|
|
75
|
+
"workspace.glob",
|
|
76
|
+
"workspace.grep",
|
|
77
|
+
"workspace.detect_image_mime",
|
|
78
|
+
"resource.read",
|
|
79
|
+
]);
|
|
80
|
+
function cloneWorkspaceIdentity(workspace) {
|
|
81
|
+
return JSON.parse(serializeProtocolMessage(workspace));
|
|
82
|
+
}
|
|
83
|
+
function assertResultWorkspaceIdentity(method, result, expected) {
|
|
84
|
+
if (!WORKSPACE_IDENTITY_RESULT_METHODS.has(method))
|
|
85
|
+
return;
|
|
86
|
+
const actual = result.workspace;
|
|
87
|
+
if (actual.id !== expected.id || actual.root !== expected.root || actual.pathFlavor !== expected.pathFlavor) {
|
|
88
|
+
throw new RemoteWorkspaceValidationError(`Result workspace identity does not match negotiation for ${method}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function validateConfiguredInteger(value, label, minimum, maximum) {
|
|
92
|
+
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
93
|
+
throw new RemoteWorkspaceValidationError(`${label} must be an integer from ${minimum} to ${maximum}`);
|
|
94
|
+
}
|
|
95
|
+
return value;
|
|
96
|
+
}
|
|
97
|
+
function boundedCloseReason(reason) {
|
|
98
|
+
let message = reason.message;
|
|
99
|
+
while (Buffer.byteLength(message, "utf8") > 120)
|
|
100
|
+
message = message.slice(0, -1);
|
|
101
|
+
return { code: reason.code, message: message || reason.code };
|
|
102
|
+
}
|
|
103
|
+
function methodCapability(method) {
|
|
104
|
+
if (method === "catalog.get" || method === "tool.invoke")
|
|
105
|
+
return undefined;
|
|
106
|
+
if (method === "lsp.status")
|
|
107
|
+
return "lsp_status";
|
|
108
|
+
if (method === "transfer.upload" || method === "transfer.download")
|
|
109
|
+
return "file_transfer";
|
|
110
|
+
if (method === "artifact.read")
|
|
111
|
+
return "artifacts";
|
|
112
|
+
return "primitive_operations";
|
|
113
|
+
}
|
|
114
|
+
async function settlesWithinDeadline(promise, timeoutMs) {
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const timer = setTimeout(() => resolve(false), timeoutMs);
|
|
117
|
+
promise.then(() => {
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
resolve(true);
|
|
120
|
+
}, (error) => {
|
|
121
|
+
clearTimeout(timer);
|
|
122
|
+
reject(error);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function isTransferMethod(method) {
|
|
127
|
+
return method === "transfer.upload" || method === "transfer.download" || method === "artifact.read";
|
|
128
|
+
}
|
|
129
|
+
function payloadByteLength(payload) {
|
|
130
|
+
return typeof payload === "string" ? Buffer.byteLength(payload, "utf8") : payload.byteLength;
|
|
131
|
+
}
|
|
132
|
+
export class RemoteWorkspaceClientProtocol {
|
|
133
|
+
transport;
|
|
134
|
+
versions;
|
|
135
|
+
requiredCapabilities;
|
|
136
|
+
optionalCapabilities;
|
|
137
|
+
receiveLimits;
|
|
138
|
+
localToolSchemas;
|
|
139
|
+
handshakeTimeoutMs;
|
|
140
|
+
tombstoneMs;
|
|
141
|
+
maxTombstones;
|
|
142
|
+
onCatalogChanged;
|
|
143
|
+
onCatalogRefreshed;
|
|
144
|
+
onDisconnect;
|
|
145
|
+
connectionPrefix = randomBytes(16).toString("base64url");
|
|
146
|
+
pending = new Map();
|
|
147
|
+
tombstones = new Map();
|
|
148
|
+
nextId = 1;
|
|
149
|
+
handshakeId;
|
|
150
|
+
handshakeOffer;
|
|
151
|
+
handshakeResolve;
|
|
152
|
+
handshakeReject;
|
|
153
|
+
handshakeTimer;
|
|
154
|
+
negotiated;
|
|
155
|
+
latestCatalogEvent;
|
|
156
|
+
catalogRefreshPromise;
|
|
157
|
+
catalogRefreshing = false;
|
|
158
|
+
receiveQueue = Promise.resolve();
|
|
159
|
+
sendQueue = Promise.resolve();
|
|
160
|
+
pendingReceiveBytes = 0;
|
|
161
|
+
pendingReceiveMessages = 0;
|
|
162
|
+
pendingOutboundBytes = 0;
|
|
163
|
+
pendingOutboundMessages = 0;
|
|
164
|
+
pendingTransferBytes = 0;
|
|
165
|
+
closed = false;
|
|
166
|
+
constructor(transport, options = {}) {
|
|
167
|
+
this.transport = transport;
|
|
168
|
+
this.versions = options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS;
|
|
169
|
+
this.requiredCapabilities = options.requiredCapabilities ?? [];
|
|
170
|
+
this.optionalCapabilities = options.optionalCapabilities ?? [];
|
|
171
|
+
this.receiveLimits = {
|
|
172
|
+
...validateRemoteWorkspaceProtocolLimits(options.receiveLimits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),
|
|
173
|
+
};
|
|
174
|
+
this.localToolSchemas = new Map(options.localToolSchemas ?? []);
|
|
175
|
+
this.handshakeTimeoutMs = validateConfiguredInteger(options.handshakeTimeoutMs ?? 10_000, "handshakeTimeoutMs", 1, 5 * 60_000);
|
|
176
|
+
this.tombstoneMs = validateConfiguredInteger(options.tombstoneMs ?? 5 * 60 * 1000, "tombstoneMs", 1, 24 * 60 * 60 * 1000);
|
|
177
|
+
this.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, "maxTombstones", 1, 1_000_000);
|
|
178
|
+
this.onCatalogChanged = options.onCatalogChanged;
|
|
179
|
+
this.onCatalogRefreshed = options.onCatalogRefreshed;
|
|
180
|
+
this.onDisconnect = options.onDisconnect;
|
|
181
|
+
}
|
|
182
|
+
get handshake() {
|
|
183
|
+
return this.negotiated ? cloneHandshakeAck(this.negotiated) : undefined;
|
|
184
|
+
}
|
|
185
|
+
setLocalToolSchemas(schemas) {
|
|
186
|
+
const copy = new Map(schemas);
|
|
187
|
+
if (this.negotiated)
|
|
188
|
+
validateRemoteWorkspaceLocalToolSchemas(this.negotiated.catalog, copy);
|
|
189
|
+
this.localToolSchemas = copy;
|
|
190
|
+
}
|
|
191
|
+
async start() {
|
|
192
|
+
if (this.closed)
|
|
193
|
+
throw new Error("Remote workspace protocol client is closed");
|
|
194
|
+
if (this.negotiated)
|
|
195
|
+
return cloneHandshakeAck(this.negotiated);
|
|
196
|
+
if (this.handshakeResolve)
|
|
197
|
+
throw new Error("Remote workspace protocol handshake is already in progress");
|
|
198
|
+
const id = this.createId();
|
|
199
|
+
const offer = {
|
|
200
|
+
type: "handshake",
|
|
201
|
+
id,
|
|
202
|
+
versions: this.versions.map((version) => ({ ...version })),
|
|
203
|
+
requiredCapabilities: [...this.requiredCapabilities],
|
|
204
|
+
optionalCapabilities: [...this.optionalCapabilities],
|
|
205
|
+
receiveLimits: { ...this.receiveLimits },
|
|
206
|
+
};
|
|
207
|
+
this.handshakeId = id;
|
|
208
|
+
this.handshakeOffer = offer;
|
|
209
|
+
const result = new Promise((resolve, reject) => {
|
|
210
|
+
this.handshakeResolve = resolve;
|
|
211
|
+
this.handshakeReject = reject;
|
|
212
|
+
this.handshakeTimer = setTimeout(() => {
|
|
213
|
+
this.failHandshake(new Error("Remote workspace handshake timed out"));
|
|
214
|
+
void this.closeWithReason({ code: "policy_violation", message: "Handshake timed out" }).catch(() => undefined);
|
|
215
|
+
}, this.handshakeTimeoutMs);
|
|
216
|
+
});
|
|
217
|
+
try {
|
|
218
|
+
await this.send(offer);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
this.failHandshake(error instanceof Error ? error : new Error(String(error)));
|
|
222
|
+
await this.closeWithReason({ code: "protocol_error", message: "Handshake send failed" });
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
waitForCatalogRefresh() {
|
|
227
|
+
return this.catalogRefreshPromise ?? Promise.resolve();
|
|
228
|
+
}
|
|
229
|
+
request(method, params, options = {}) {
|
|
230
|
+
if (method === "tool.invoke" && this.catalogRefreshPromise) {
|
|
231
|
+
return this.catalogRefreshPromise.then(() => this.beginRequest(method, params, options).result);
|
|
232
|
+
}
|
|
233
|
+
return this.beginRequest(method, params, options).result;
|
|
234
|
+
}
|
|
235
|
+
beginRequest(method, params, options = {}) {
|
|
236
|
+
if (this.closed)
|
|
237
|
+
throw new Error("Remote workspace protocol client is closed");
|
|
238
|
+
const negotiated = this.negotiated;
|
|
239
|
+
if (!negotiated)
|
|
240
|
+
throw new Error("Remote workspace protocol handshake has not completed");
|
|
241
|
+
if (method === "tool.invoke" && this.catalogRefreshing) {
|
|
242
|
+
throw new Error("Remote workspace tool catalog refresh is in progress");
|
|
243
|
+
}
|
|
244
|
+
parseRemoteWorkspaceRequestParams(method, params, negotiated.limits);
|
|
245
|
+
this.assertMethodAvailable(method, params, negotiated);
|
|
246
|
+
if (this.pending.size >= negotiated.limits.maxActiveRequests) {
|
|
247
|
+
throw new Error("Remote workspace active request limit reached");
|
|
248
|
+
}
|
|
249
|
+
if (isTransferMethod(method) && this.activeTransferCount() >= negotiated.limits.maxActiveTransfers) {
|
|
250
|
+
throw new Error("Remote workspace active transfer limit reached");
|
|
251
|
+
}
|
|
252
|
+
const timeoutMs = options.timeoutMs ?? negotiated.limits.maxRequestMs;
|
|
253
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > negotiated.limits.maxRequestMs) {
|
|
254
|
+
throw new Error(`Invalid remote workspace timeout: ${timeoutMs}`);
|
|
255
|
+
}
|
|
256
|
+
if (options.signal?.aborted)
|
|
257
|
+
throw new Error("Remote workspace request aborted before send");
|
|
258
|
+
const id = this.createId();
|
|
259
|
+
let resolveResult = () => undefined;
|
|
260
|
+
let rejectResult = () => undefined;
|
|
261
|
+
const result = new Promise((resolve, reject) => {
|
|
262
|
+
resolveResult = resolve;
|
|
263
|
+
rejectResult = reject;
|
|
264
|
+
});
|
|
265
|
+
const kind = this.requestKind(method);
|
|
266
|
+
const pending = {
|
|
267
|
+
method,
|
|
268
|
+
kind,
|
|
269
|
+
resolve: resolveResult,
|
|
270
|
+
reject: rejectResult,
|
|
271
|
+
deadlineTimer: setTimeout(() => {
|
|
272
|
+
void this.requestCancellation(id, "deadline_exceeded", "Remote workspace request timed out");
|
|
273
|
+
}, timeoutMs + 25),
|
|
274
|
+
cancelSent: false,
|
|
275
|
+
signal: options.signal,
|
|
276
|
+
onUpdate: options.onUpdate,
|
|
277
|
+
onTransferStart: options.onTransferStart,
|
|
278
|
+
onTransferChunk: options.onTransferChunk,
|
|
279
|
+
nextUpdateSequence: 0,
|
|
280
|
+
nextIncomingTransferSequence: 0,
|
|
281
|
+
nextOutgoingTransferSequence: 0,
|
|
282
|
+
...(method === "transfer.upload" ? { upload: this.createClientUploadState(params) } : {}),
|
|
283
|
+
};
|
|
284
|
+
if (options.signal) {
|
|
285
|
+
pending.onAbort = () => {
|
|
286
|
+
void this.requestCancellation(id, "cancelled", "Remote workspace request cancelled");
|
|
287
|
+
};
|
|
288
|
+
options.signal.addEventListener("abort", pending.onAbort, { once: true });
|
|
289
|
+
}
|
|
290
|
+
this.pending.set(id, pending);
|
|
291
|
+
const request = { type: "request", id, method, timeoutMs, params };
|
|
292
|
+
void this.send(request).catch((error) => this.handleTransportFailure(error));
|
|
293
|
+
return {
|
|
294
|
+
id,
|
|
295
|
+
result,
|
|
296
|
+
sendTransferChunk: (chunk) => this.queueUploadChunk(id, chunk),
|
|
297
|
+
finishTransfer: (length, sha256) => this.queueUploadFinish(id, length, sha256),
|
|
298
|
+
cancel: (reason) => this.requestCancellation(id, "cancelled", reason ?? "Remote workspace request cancelled"),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
receive(payload) {
|
|
302
|
+
if (this.closed)
|
|
303
|
+
return Promise.resolve();
|
|
304
|
+
const limits = this.negotiated?.limits ?? this.receiveLimits;
|
|
305
|
+
const bytes = payloadByteLength(payload);
|
|
306
|
+
if (bytes > limits.maxMessageBytes ||
|
|
307
|
+
this.pendingReceiveMessages >= limits.maxPendingInboundMessages ||
|
|
308
|
+
this.pendingReceiveBytes + bytes > limits.maxPendingInboundBytes) {
|
|
309
|
+
return this.protocolFailure(new Error("Remote workspace inbound queue exceeds negotiated limits"), "message_too_large");
|
|
310
|
+
}
|
|
311
|
+
const queuedPayload = typeof payload === "string" ? payload : Uint8Array.from(payload);
|
|
312
|
+
this.pendingReceiveMessages++;
|
|
313
|
+
this.pendingReceiveBytes += bytes;
|
|
314
|
+
const operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));
|
|
315
|
+
const tracked = operation.finally(() => {
|
|
316
|
+
this.pendingReceiveMessages--;
|
|
317
|
+
this.pendingReceiveBytes -= bytes;
|
|
318
|
+
});
|
|
319
|
+
this.receiveQueue = tracked.catch(() => undefined);
|
|
320
|
+
return tracked;
|
|
321
|
+
}
|
|
322
|
+
async disconnect(message = "Remote workspace connection closed") {
|
|
323
|
+
if (this.closed)
|
|
324
|
+
return;
|
|
325
|
+
this.closed = true;
|
|
326
|
+
this.onDisconnect?.(message);
|
|
327
|
+
this.failHandshake(new RemoteWorkspaceDisconnectedError(message, "not_started"));
|
|
328
|
+
for (const [id, pending] of this.pending) {
|
|
329
|
+
const state = pending.kind === "read" ? "not_started" : "indeterminate";
|
|
330
|
+
this.rejectPending(id, new RemoteWorkspaceDisconnectedError(message, state), true);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async close() {
|
|
334
|
+
if (this.closed)
|
|
335
|
+
return;
|
|
336
|
+
await this.closeWithReason({ code: "normal", message: "Client closed" });
|
|
337
|
+
}
|
|
338
|
+
async decodeServerMessage(payload) {
|
|
339
|
+
try {
|
|
340
|
+
const limits = this.negotiated?.limits ?? this.receiveLimits;
|
|
341
|
+
const decoded = decodeRemoteWorkspaceMessage(payload, limits);
|
|
342
|
+
return parseRemoteWorkspaceServerMessage(decoded, limits);
|
|
343
|
+
}
|
|
344
|
+
catch (error) {
|
|
345
|
+
await this.protocolFailure(error, classifyValidationClose(error));
|
|
346
|
+
return undefined;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
async handleHandshakeAck(message) {
|
|
350
|
+
if (!this.handshakeId || message.id !== this.handshakeId || !this.handshakeOffer || !this.handshakeResolve) {
|
|
351
|
+
await this.protocolFailure(new Error("Unexpected handshake response"));
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
const ack = validateRemoteWorkspaceHandshakeAck(message, this.handshakeOffer, this.localToolSchemas);
|
|
356
|
+
this.negotiated = cloneHandshakeAck(ack);
|
|
357
|
+
const resolve = this.handshakeResolve;
|
|
358
|
+
this.clearHandshake();
|
|
359
|
+
this.addTombstone(message.id);
|
|
360
|
+
resolve(cloneHandshakeAck(ack));
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
this.failHandshake(error instanceof Error ? error : new Error(String(error)));
|
|
364
|
+
await this.closeWithReason({ code: "protocol_error", message: "Invalid handshake response" });
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async handleCatalogChanged(message) {
|
|
368
|
+
if (!this.negotiated?.capabilities.includes("catalog_refresh")) {
|
|
369
|
+
await this.protocolFailure(new Error("Unnegotiated catalog event"));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
this.catalogRefreshing = true;
|
|
373
|
+
try {
|
|
374
|
+
this.onCatalogChanged?.(message);
|
|
375
|
+
}
|
|
376
|
+
catch (error) {
|
|
377
|
+
await this.protocolFailure(error);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
this.latestCatalogEvent = message;
|
|
381
|
+
this.scheduleCatalogRefresh();
|
|
382
|
+
}
|
|
383
|
+
handleHandshakeError(message) {
|
|
384
|
+
if (!this.handshakeId || message.id !== this.handshakeId)
|
|
385
|
+
return false;
|
|
386
|
+
this.addTombstone(message.id);
|
|
387
|
+
this.failHandshake(new RemoteWorkspaceRequestError(message.error));
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
async findPendingRequest(message) {
|
|
391
|
+
const pending = this.pending.get(message.id);
|
|
392
|
+
if (pending)
|
|
393
|
+
return pending;
|
|
394
|
+
this.pruneTombstones();
|
|
395
|
+
if (!this.tombstones.has(message.id)) {
|
|
396
|
+
await this.protocolFailure(new Error(`Response for unknown request ID: ${message.id}`));
|
|
397
|
+
}
|
|
398
|
+
return undefined;
|
|
399
|
+
}
|
|
400
|
+
async handleRequestUpdate(message, pending) {
|
|
401
|
+
if (!this.negotiated?.capabilities.includes("tool_updates") || message.sequence !== pending.nextUpdateSequence) {
|
|
402
|
+
await this.protocolFailure(new Error(`Out-of-order or unnegotiated update for request ${message.id}`));
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
pending.nextUpdateSequence++;
|
|
406
|
+
try {
|
|
407
|
+
await pending.onUpdate?.(message.update);
|
|
408
|
+
}
|
|
409
|
+
catch (error) {
|
|
410
|
+
await this.failClientCallback(message.id, error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async handleTransferStart(message, pending) {
|
|
414
|
+
if ((pending.method !== "transfer.download" && pending.method !== "artifact.read") ||
|
|
415
|
+
pending.download ||
|
|
416
|
+
message.length > (this.negotiated?.limits.maxTransferBytes ?? 0)) {
|
|
417
|
+
await this.protocolFailure(new Error(`Invalid transfer start for ${pending.method}`));
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
pending.download = {
|
|
421
|
+
expectedLength: message.length,
|
|
422
|
+
expectedSha256: message.sha256,
|
|
423
|
+
bytes: 0,
|
|
424
|
+
chunks: 0,
|
|
425
|
+
hash: createHash("sha256"),
|
|
426
|
+
};
|
|
427
|
+
try {
|
|
428
|
+
await pending.onTransferStart?.({ length: message.length, sha256: message.sha256 });
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
await this.failClientCallback(message.id, error);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async handleTransferChunk(message, pending) {
|
|
435
|
+
const download = pending.download;
|
|
436
|
+
if (!download || message.sequence !== pending.nextIncomingTransferSequence) {
|
|
437
|
+
await this.protocolFailure(new Error(`Out-of-order transfer chunk for request ${message.id}`));
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const limits = this.negotiated?.limits ?? this.receiveLimits;
|
|
441
|
+
let chunk;
|
|
442
|
+
try {
|
|
443
|
+
chunk = decodeCanonicalBase64(message.dataBase64, limits.maxTransferChunkBytes);
|
|
444
|
+
}
|
|
445
|
+
catch (error) {
|
|
446
|
+
await this.protocolFailure(error, "invalid_payload");
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
if (download.chunks + 1 > limits.maxTransferChunks ||
|
|
450
|
+
download.bytes + chunk.byteLength > limits.maxTransferBytes ||
|
|
451
|
+
download.bytes + chunk.byteLength > download.expectedLength) {
|
|
452
|
+
await this.protocolFailure(new Error(`Transfer exceeds declared or negotiated limits for request ${message.id}`), "message_too_large");
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
download.chunks++;
|
|
456
|
+
download.bytes += chunk.byteLength;
|
|
457
|
+
download.hash.update(chunk);
|
|
458
|
+
pending.nextIncomingTransferSequence++;
|
|
459
|
+
try {
|
|
460
|
+
await pending.onTransferChunk?.(chunk);
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
await this.failClientCallback(message.id, error);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async handleRequestResult(message, pending) {
|
|
467
|
+
try {
|
|
468
|
+
const limits = this.negotiated?.limits ?? this.receiveLimits;
|
|
469
|
+
const result = parseRemoteWorkspaceResult(pending.method, message.result, limits);
|
|
470
|
+
assertResultWorkspaceIdentity(pending.method, result, this.negotiated.workspace);
|
|
471
|
+
this.verifyClientTransferResult(pending, result);
|
|
472
|
+
this.resolvePending(message.id, result);
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
await this.protocolFailure(error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
async handlePendingMessage(message, pending) {
|
|
479
|
+
switch (message.type) {
|
|
480
|
+
case "update":
|
|
481
|
+
await this.handleRequestUpdate(message, pending);
|
|
482
|
+
return;
|
|
483
|
+
case "transfer_start":
|
|
484
|
+
await this.handleTransferStart(message, pending);
|
|
485
|
+
return;
|
|
486
|
+
case "transfer_chunk":
|
|
487
|
+
await this.handleTransferChunk(message, pending);
|
|
488
|
+
return;
|
|
489
|
+
case "error":
|
|
490
|
+
this.rejectPending(message.id, new RemoteWorkspaceRequestError(message.error), true);
|
|
491
|
+
return;
|
|
492
|
+
case "result":
|
|
493
|
+
await this.handleRequestResult(message, pending);
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
async receivePayload(payload) {
|
|
498
|
+
if (this.closed)
|
|
499
|
+
return;
|
|
500
|
+
const message = await this.decodeServerMessage(payload);
|
|
501
|
+
if (!message)
|
|
502
|
+
return;
|
|
503
|
+
if (message.type === "handshake_ack") {
|
|
504
|
+
await this.handleHandshakeAck(message);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
if (message.type === "catalog_changed") {
|
|
508
|
+
await this.handleCatalogChanged(message);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (message.type === "error" && this.handleHandshakeError(message))
|
|
512
|
+
return;
|
|
513
|
+
const pending = await this.findPendingRequest(message);
|
|
514
|
+
if (!pending)
|
|
515
|
+
return;
|
|
516
|
+
await this.handlePendingMessage(message, pending);
|
|
517
|
+
}
|
|
518
|
+
assertMethodAvailable(method, params, ack) {
|
|
519
|
+
const capabilities = new Set(ack.capabilities);
|
|
520
|
+
const capability = methodCapability(method);
|
|
521
|
+
if (capability && !capabilities.has(capability)) {
|
|
522
|
+
throw new Error(`Remote workspace capability was not negotiated: ${capability}`);
|
|
523
|
+
}
|
|
524
|
+
if (method === "artifact.read" && !capabilities.has("file_transfer")) {
|
|
525
|
+
throw new Error("Remote workspace artifact reads require file transfer capability");
|
|
526
|
+
}
|
|
527
|
+
if (method !== "catalog.get" && method !== "tool.invoke" && !ack.catalog.operations.includes(method)) {
|
|
528
|
+
throw new Error(`Remote workspace operation is not available: ${method}`);
|
|
529
|
+
}
|
|
530
|
+
if (method !== "tool.invoke")
|
|
531
|
+
return;
|
|
532
|
+
const invocation = params;
|
|
533
|
+
const remoteTool = ack.catalog.tools.find((tool) => tool.name === invocation.toolName);
|
|
534
|
+
const localHash = this.localToolSchemas.get(invocation.toolName);
|
|
535
|
+
if (!remoteTool || !localHash)
|
|
536
|
+
throw new Error(`Remote tool has no matching local canonical definition: ${invocation.toolName}`);
|
|
537
|
+
for (const featureFlag of remoteTool.featureFlags) {
|
|
538
|
+
if (!capabilities.has(featureFlag)) {
|
|
539
|
+
throw new Error(`Remote tool requires an unnegotiated capability: ${featureFlag}`);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
if (remoteTool.schemaHash !== localHash || invocation.schemaHash !== localHash) {
|
|
543
|
+
throw new Error(`Remote tool schema does not match the local canonical definition: ${invocation.toolName}`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
requestKind(method) {
|
|
547
|
+
return method === "tool.invoke" ? "mutation" : getRemoteWorkspaceMethodKind(method);
|
|
548
|
+
}
|
|
549
|
+
createClientUploadState(params) {
|
|
550
|
+
const upload = params;
|
|
551
|
+
if (upload.length > (this.negotiated?.limits.maxTransferBytes ?? 0)) {
|
|
552
|
+
throw new Error("Remote workspace upload exceeds negotiated total-byte limit");
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
expectedLength: upload.length,
|
|
556
|
+
expectedSha256: upload.sha256,
|
|
557
|
+
bytes: 0,
|
|
558
|
+
pendingBytes: 0,
|
|
559
|
+
chunks: 0,
|
|
560
|
+
pendingChunks: 0,
|
|
561
|
+
hash: createHash("sha256"),
|
|
562
|
+
finished: false,
|
|
563
|
+
queue: Promise.resolve(),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
queueUploadChunk(id, chunk) {
|
|
567
|
+
const pending = this.requireUploadRequest(id);
|
|
568
|
+
const upload = pending.upload;
|
|
569
|
+
const limits = this.negotiated.limits;
|
|
570
|
+
if (chunk.byteLength > limits.maxTransferChunkBytes) {
|
|
571
|
+
throw new Error("Remote workspace transfer chunk exceeds limit");
|
|
572
|
+
}
|
|
573
|
+
if (this.pendingTransferBytes + chunk.byteLength > limits.maxPendingTransferBytes ||
|
|
574
|
+
upload.pendingBytes + chunk.byteLength > limits.maxPendingTransferBytes ||
|
|
575
|
+
upload.bytes + upload.pendingBytes + chunk.byteLength > limits.maxTransferBytes ||
|
|
576
|
+
upload.bytes + upload.pendingBytes + chunk.byteLength > upload.expectedLength ||
|
|
577
|
+
upload.chunks + upload.pendingChunks + 1 > limits.maxTransferChunks) {
|
|
578
|
+
throw new Error("Remote workspace pending upload exceeds negotiated limits");
|
|
579
|
+
}
|
|
580
|
+
const copiedChunk = Buffer.from(chunk);
|
|
581
|
+
upload.pendingBytes += copiedChunk.byteLength;
|
|
582
|
+
this.pendingTransferBytes += copiedChunk.byteLength;
|
|
583
|
+
upload.pendingChunks++;
|
|
584
|
+
const operation = upload.queue.then(async () => {
|
|
585
|
+
try {
|
|
586
|
+
if (upload.finished)
|
|
587
|
+
throw new Error("Remote workspace upload is already finished");
|
|
588
|
+
await this.send({
|
|
589
|
+
type: "transfer_chunk",
|
|
590
|
+
id,
|
|
591
|
+
sequence: pending.nextOutgoingTransferSequence,
|
|
592
|
+
dataBase64: copiedChunk.toString("base64"),
|
|
593
|
+
});
|
|
594
|
+
pending.nextOutgoingTransferSequence++;
|
|
595
|
+
upload.chunks++;
|
|
596
|
+
upload.bytes += copiedChunk.byteLength;
|
|
597
|
+
upload.hash.update(copiedChunk);
|
|
598
|
+
}
|
|
599
|
+
finally {
|
|
600
|
+
upload.pendingBytes -= copiedChunk.byteLength;
|
|
601
|
+
this.pendingTransferBytes -= copiedChunk.byteLength;
|
|
602
|
+
upload.pendingChunks--;
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
upload.queue = operation.catch(() => undefined);
|
|
606
|
+
return operation.catch(async (error) => {
|
|
607
|
+
if (error instanceof RemoteWorkspaceTransportError)
|
|
608
|
+
await this.handleTransportFailure(error);
|
|
609
|
+
else
|
|
610
|
+
await this.requestCancellation(id, "cancelled", error instanceof Error ? error.message : String(error));
|
|
611
|
+
throw error;
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
queueUploadFinish(id, length, sha256) {
|
|
615
|
+
const pending = this.requireUploadRequest(id);
|
|
616
|
+
const upload = pending.upload;
|
|
617
|
+
const operation = upload.queue.then(async () => {
|
|
618
|
+
if (upload.finished)
|
|
619
|
+
throw new Error("Remote workspace upload is already finished");
|
|
620
|
+
const digest = upload.hash.digest("hex");
|
|
621
|
+
if (length !== upload.expectedLength ||
|
|
622
|
+
sha256 !== upload.expectedSha256 ||
|
|
623
|
+
upload.bytes !== upload.expectedLength ||
|
|
624
|
+
digest !== upload.expectedSha256) {
|
|
625
|
+
throw new Error("Remote workspace upload length or SHA-256 does not match its declaration");
|
|
626
|
+
}
|
|
627
|
+
upload.finished = true;
|
|
628
|
+
await this.send({ type: "transfer_finish", id, length, sha256 });
|
|
629
|
+
});
|
|
630
|
+
upload.queue = operation.catch(() => undefined);
|
|
631
|
+
return operation.catch(async (error) => {
|
|
632
|
+
if (error instanceof RemoteWorkspaceTransportError)
|
|
633
|
+
await this.handleTransportFailure(error);
|
|
634
|
+
else
|
|
635
|
+
await this.requestCancellation(id, "cancelled", error instanceof Error ? error.message : String(error));
|
|
636
|
+
throw error;
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
verifyClientTransferResult(pending, result) {
|
|
640
|
+
if (pending.method === "transfer.upload") {
|
|
641
|
+
const upload = pending.upload;
|
|
642
|
+
const metadata = result;
|
|
643
|
+
if (!upload?.finished ||
|
|
644
|
+
metadata.length !== upload.expectedLength ||
|
|
645
|
+
metadata.sha256 !== upload.expectedSha256) {
|
|
646
|
+
throw new RemoteWorkspaceValidationError("Upload terminal result does not match the verified upload");
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
if (pending.method !== "transfer.download" && pending.method !== "artifact.read")
|
|
651
|
+
return;
|
|
652
|
+
const download = pending.download;
|
|
653
|
+
const metadata = result;
|
|
654
|
+
if (!download)
|
|
655
|
+
throw new RemoteWorkspaceValidationError("Download completed without transfer metadata");
|
|
656
|
+
const digest = download.hash.digest("hex");
|
|
657
|
+
if (download.bytes !== download.expectedLength ||
|
|
658
|
+
digest !== download.expectedSha256 ||
|
|
659
|
+
metadata.length !== download.expectedLength ||
|
|
660
|
+
metadata.sha256 !== download.expectedSha256) {
|
|
661
|
+
throw new RemoteWorkspaceValidationError("Download length or SHA-256 verification failed");
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
scheduleCatalogRefresh() {
|
|
665
|
+
if (this.catalogRefreshPromise || this.closed)
|
|
666
|
+
return;
|
|
667
|
+
this.catalogRefreshPromise = Promise.resolve()
|
|
668
|
+
.then(async () => {
|
|
669
|
+
while (this.latestCatalogEvent && !this.closed) {
|
|
670
|
+
const expected = this.latestCatalogEvent;
|
|
671
|
+
this.latestCatalogEvent = undefined;
|
|
672
|
+
const catalog = (await this.beginRequest("catalog.get", {}).result);
|
|
673
|
+
validateRemoteWorkspaceCatalog(catalog);
|
|
674
|
+
validateRemoteWorkspaceLocalToolSchemas(catalog, this.localToolSchemas);
|
|
675
|
+
const catalogHash = hashRemoteWorkspaceCatalog(catalog);
|
|
676
|
+
if (catalog.generation !== expected.generation || catalogHash !== expected.catalogHash) {
|
|
677
|
+
if (this.latestCatalogEvent)
|
|
678
|
+
continue;
|
|
679
|
+
throw new RemoteWorkspaceValidationError("Catalog refresh does not match the advertised generation");
|
|
680
|
+
}
|
|
681
|
+
if (!this.negotiated)
|
|
682
|
+
throw new Error("Remote workspace handshake disappeared during refresh");
|
|
683
|
+
this.negotiated = { ...this.negotiated, catalog, catalogHash };
|
|
684
|
+
await this.onCatalogRefreshed?.(cloneCatalog(catalog));
|
|
685
|
+
}
|
|
686
|
+
})
|
|
687
|
+
.catch((error) => this.protocolFailure(error))
|
|
688
|
+
.finally(() => {
|
|
689
|
+
this.catalogRefreshPromise = undefined;
|
|
690
|
+
if (this.latestCatalogEvent && !this.closed)
|
|
691
|
+
this.scheduleCatalogRefresh();
|
|
692
|
+
else
|
|
693
|
+
this.catalogRefreshing = false;
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
activeTransferCount() {
|
|
697
|
+
let count = 0;
|
|
698
|
+
for (const pending of this.pending.values())
|
|
699
|
+
if (isTransferMethod(pending.method))
|
|
700
|
+
count++;
|
|
701
|
+
return count;
|
|
702
|
+
}
|
|
703
|
+
createId() {
|
|
704
|
+
return `${this.connectionPrefix}:${this.nextId++}`;
|
|
705
|
+
}
|
|
706
|
+
requireUploadRequest(id) {
|
|
707
|
+
const pending = this.pending.get(id);
|
|
708
|
+
if (!pending?.upload || pending.method !== "transfer.upload")
|
|
709
|
+
throw new Error(`No active upload request: ${id}`);
|
|
710
|
+
return pending;
|
|
711
|
+
}
|
|
712
|
+
async requestCancellation(id, code, message) {
|
|
713
|
+
const pending = this.pending.get(id);
|
|
714
|
+
if (!pending || pending.cancelSent)
|
|
715
|
+
return;
|
|
716
|
+
pending.cancelSent = true;
|
|
717
|
+
const cancellationMs = this.negotiated?.limits.maxCancellationMs ?? 1000;
|
|
718
|
+
pending.cancelTimer = setTimeout(() => {
|
|
719
|
+
const executionState = pending.kind === "read" ? "not_started" : "indeterminate";
|
|
720
|
+
this.rejectPending(id, new RemoteWorkspaceRequestError({ code, message, executionState, retryable: false }), true);
|
|
721
|
+
void this.closeWithReason({
|
|
722
|
+
code: "policy_violation",
|
|
723
|
+
message: `Remote workspace cancellation was not acknowledged: ${id}`,
|
|
724
|
+
}).catch(() => this.disconnect("Remote workspace cancellation was not acknowledged"));
|
|
725
|
+
}, cancellationMs + 500);
|
|
726
|
+
try {
|
|
727
|
+
const sent = await settlesWithinDeadline(this.send({ type: "cancel", id, reason: message }), cancellationMs + 250);
|
|
728
|
+
if (!sent)
|
|
729
|
+
throw new RemoteWorkspaceTransportError("Cancellation send timed out");
|
|
730
|
+
}
|
|
731
|
+
catch (error) {
|
|
732
|
+
await this.handleTransportFailure(error);
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
if (!this.pending.has(id) && pending.cancelTimer)
|
|
736
|
+
clearTimeout(pending.cancelTimer);
|
|
737
|
+
}
|
|
738
|
+
async failClientCallback(id, error) {
|
|
739
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
740
|
+
const executionState = this.pending.get(id)?.kind === "read" ? "not_started" : "indeterminate";
|
|
741
|
+
await this.requestCancellation(id, "cancelled", "Client callback failed");
|
|
742
|
+
this.rejectPending(id, new RemoteWorkspaceRequestError({
|
|
743
|
+
code: "internal_error",
|
|
744
|
+
message: `Remote workspace client callback failed: ${message}`,
|
|
745
|
+
executionState,
|
|
746
|
+
retryable: false,
|
|
747
|
+
}), true);
|
|
748
|
+
await this.closeWithReason({
|
|
749
|
+
code: "policy_violation",
|
|
750
|
+
message: `Remote workspace client callback failed for ${id}`,
|
|
751
|
+
}).catch(() => this.disconnect("Remote workspace client callback failed"));
|
|
752
|
+
}
|
|
753
|
+
resolvePending(id, value) {
|
|
754
|
+
const pending = this.takePending(id);
|
|
755
|
+
if (pending)
|
|
756
|
+
pending.resolve(value);
|
|
757
|
+
}
|
|
758
|
+
rejectPending(id, error, tombstone) {
|
|
759
|
+
const pending = this.takePending(id, tombstone);
|
|
760
|
+
if (pending)
|
|
761
|
+
pending.reject(error);
|
|
762
|
+
}
|
|
763
|
+
takePending(id, tombstone = true) {
|
|
764
|
+
const pending = this.pending.get(id);
|
|
765
|
+
if (!pending)
|
|
766
|
+
return undefined;
|
|
767
|
+
this.pending.delete(id);
|
|
768
|
+
clearTimeout(pending.deadlineTimer);
|
|
769
|
+
if (pending.cancelTimer)
|
|
770
|
+
clearTimeout(pending.cancelTimer);
|
|
771
|
+
if (pending.signal && pending.onAbort)
|
|
772
|
+
pending.signal.removeEventListener("abort", pending.onAbort);
|
|
773
|
+
if (tombstone)
|
|
774
|
+
this.addTombstone(id);
|
|
775
|
+
return pending;
|
|
776
|
+
}
|
|
777
|
+
addTombstone(id) {
|
|
778
|
+
this.pruneTombstones();
|
|
779
|
+
if (this.tombstones.size >= this.maxTombstones) {
|
|
780
|
+
void this.protocolFailure(new Error("Remote workspace tombstone limit exceeded"), "policy_violation").catch(() => undefined);
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
this.tombstones.set(id, performance.now() + this.tombstoneMs);
|
|
784
|
+
}
|
|
785
|
+
pruneTombstones() {
|
|
786
|
+
const now = performance.now();
|
|
787
|
+
for (const [id, expiresAt] of this.tombstones)
|
|
788
|
+
if (expiresAt <= now)
|
|
789
|
+
this.tombstones.delete(id);
|
|
790
|
+
}
|
|
791
|
+
clearHandshake() {
|
|
792
|
+
if (this.handshakeTimer)
|
|
793
|
+
clearTimeout(this.handshakeTimer);
|
|
794
|
+
this.handshakeTimer = undefined;
|
|
795
|
+
this.handshakeId = undefined;
|
|
796
|
+
this.handshakeOffer = undefined;
|
|
797
|
+
this.handshakeResolve = undefined;
|
|
798
|
+
this.handshakeReject = undefined;
|
|
799
|
+
}
|
|
800
|
+
failHandshake(error) {
|
|
801
|
+
const reject = this.handshakeReject;
|
|
802
|
+
this.clearHandshake();
|
|
803
|
+
reject?.(error);
|
|
804
|
+
}
|
|
805
|
+
send(message) {
|
|
806
|
+
const limits = this.negotiated?.limits ?? this.receiveLimits;
|
|
807
|
+
const prepared = prepareClientOutbound(message, limits);
|
|
808
|
+
const estimatedBytes = prepared.bytes;
|
|
809
|
+
if (this.pendingOutboundMessages >= limits.maxPendingOutboundMessages ||
|
|
810
|
+
this.pendingOutboundBytes + estimatedBytes > limits.maxPendingOutboundBytes) {
|
|
811
|
+
return Promise.reject(new RemoteWorkspaceValidationError("Remote workspace client outbound queue exceeds limits"));
|
|
812
|
+
}
|
|
813
|
+
this.pendingOutboundMessages++;
|
|
814
|
+
this.pendingOutboundBytes += estimatedBytes;
|
|
815
|
+
const operation = this.sendQueue.then(async () => {
|
|
816
|
+
if (this.closed)
|
|
817
|
+
throw new RemoteWorkspaceTransportError("Remote workspace protocol client is closed");
|
|
818
|
+
try {
|
|
819
|
+
const sent = await settlesWithinDeadline(this.transport.send(prepared.message), limits.maxTransportSendMs);
|
|
820
|
+
if (!sent)
|
|
821
|
+
throw new RemoteWorkspaceTransportError("Remote workspace client transport send timed out");
|
|
822
|
+
}
|
|
823
|
+
catch (error) {
|
|
824
|
+
throw error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
const tracked = operation.finally(() => {
|
|
828
|
+
this.pendingOutboundMessages--;
|
|
829
|
+
this.pendingOutboundBytes -= estimatedBytes;
|
|
830
|
+
});
|
|
831
|
+
this.sendQueue = tracked.catch(() => undefined);
|
|
832
|
+
return tracked;
|
|
833
|
+
}
|
|
834
|
+
async handleTransportFailure(error) {
|
|
835
|
+
const message = `Remote workspace transport failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
836
|
+
try {
|
|
837
|
+
await this.closeWithReason({ code: "protocol_error", message });
|
|
838
|
+
}
|
|
839
|
+
catch {
|
|
840
|
+
await this.disconnect(message);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
async protocolFailure(error, code = "protocol_error") {
|
|
844
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
845
|
+
try {
|
|
846
|
+
await this.closeWithReason({ code, message });
|
|
847
|
+
}
|
|
848
|
+
catch {
|
|
849
|
+
await this.disconnect(message);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
async closeWithReason(reason) {
|
|
853
|
+
if (this.closed)
|
|
854
|
+
return;
|
|
855
|
+
await this.disconnect(reason.message);
|
|
856
|
+
const sendDeadline = this.negotiated?.limits.maxTransportSendMs ?? this.receiveLimits.maxTransportSendMs;
|
|
857
|
+
const sendsStopped = await settlesWithinDeadline(this.sendQueue, sendDeadline);
|
|
858
|
+
const closed = await settlesWithinDeadline(Promise.resolve(this.transport.close(boundedCloseReason(reason))), sendDeadline);
|
|
859
|
+
if (!sendsStopped || !closed) {
|
|
860
|
+
throw new RemoteWorkspaceTerminationError("Remote workspace client transport shutdown timed out");
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
export class RemoteWorkspaceServerProtocol {
|
|
865
|
+
transport;
|
|
866
|
+
handler;
|
|
867
|
+
workspace;
|
|
868
|
+
negotiation;
|
|
869
|
+
backendLabel;
|
|
870
|
+
invalidRequestStrikes;
|
|
871
|
+
tombstoneMs;
|
|
872
|
+
maxTombstones;
|
|
873
|
+
active = new Map();
|
|
874
|
+
tombstones = new Map();
|
|
875
|
+
catalog;
|
|
876
|
+
catalogHash;
|
|
877
|
+
limits;
|
|
878
|
+
negotiatedCapabilities = new Set();
|
|
879
|
+
receiveQueue = Promise.resolve();
|
|
880
|
+
sendQueue = Promise.resolve();
|
|
881
|
+
disconnectPromise;
|
|
882
|
+
transportFailurePromise;
|
|
883
|
+
pendingReceiveBytes = 0;
|
|
884
|
+
pendingReceiveMessages = 0;
|
|
885
|
+
pendingOutboundBytes = 0;
|
|
886
|
+
pendingOutboundMessages = 0;
|
|
887
|
+
pendingTransferBytes = 0;
|
|
888
|
+
handshaken = false;
|
|
889
|
+
closed = false;
|
|
890
|
+
draining = false;
|
|
891
|
+
strikes = 0;
|
|
892
|
+
constructor(transport, handler, options) {
|
|
893
|
+
const catalog = cloneCatalog(options.catalog);
|
|
894
|
+
this.validateCatalogCapabilities(catalog, new Set(options.capabilities));
|
|
895
|
+
this.transport = transport;
|
|
896
|
+
this.handler = handler;
|
|
897
|
+
this.workspace = cloneWorkspaceIdentity(options.workspace);
|
|
898
|
+
this.catalog = catalog;
|
|
899
|
+
this.catalogHash = hashRemoteWorkspaceCatalog(catalog);
|
|
900
|
+
this.limits = {
|
|
901
|
+
...validateRemoteWorkspaceProtocolLimits(options.limits ?? DEFAULT_REMOTE_WORKSPACE_PROTOCOL_LIMITS),
|
|
902
|
+
};
|
|
903
|
+
this.negotiation = {
|
|
904
|
+
serverVersions: options.versions ?? REMOTE_WORKSPACE_PROTOCOL_VERSIONS,
|
|
905
|
+
serverCapabilities: options.capabilities,
|
|
906
|
+
requiredClientCapabilities: options.requiredClientCapabilities,
|
|
907
|
+
serverLimits: this.limits,
|
|
908
|
+
};
|
|
909
|
+
this.backendLabel = options.backendLabel;
|
|
910
|
+
this.invalidRequestStrikes = validateConfiguredInteger(options.invalidRequestStrikes ?? 3, "invalidRequestStrikes", 1, 100);
|
|
911
|
+
this.tombstoneMs = validateConfiguredInteger(options.tombstoneMs ?? 5 * 60 * 1000, "tombstoneMs", 1, 24 * 60 * 60 * 1000);
|
|
912
|
+
this.maxTombstones = validateConfiguredInteger(options.maxTombstones ?? 65_536, "maxTombstones", 1, 1_000_000);
|
|
913
|
+
}
|
|
914
|
+
get handshakeComplete() {
|
|
915
|
+
return this.handshaken;
|
|
916
|
+
}
|
|
917
|
+
receive(payload) {
|
|
918
|
+
if (this.closed)
|
|
919
|
+
return Promise.resolve();
|
|
920
|
+
const bytes = payloadByteLength(payload);
|
|
921
|
+
if (bytes > this.limits.maxMessageBytes ||
|
|
922
|
+
this.pendingReceiveMessages >= this.limits.maxPendingInboundMessages ||
|
|
923
|
+
this.pendingReceiveBytes + bytes > this.limits.maxPendingInboundBytes) {
|
|
924
|
+
return this.closeWithReason({ code: "message_too_large", message: "Inbound protocol queue exceeds limits" });
|
|
925
|
+
}
|
|
926
|
+
const queuedPayload = typeof payload === "string" ? payload : Uint8Array.from(payload);
|
|
927
|
+
this.pendingReceiveMessages++;
|
|
928
|
+
this.pendingReceiveBytes += bytes;
|
|
929
|
+
const operation = this.receiveQueue.then(() => this.receivePayload(queuedPayload));
|
|
930
|
+
const tracked = operation.finally(() => {
|
|
931
|
+
this.pendingReceiveMessages--;
|
|
932
|
+
this.pendingReceiveBytes -= bytes;
|
|
933
|
+
});
|
|
934
|
+
this.receiveQueue = tracked.catch(() => undefined);
|
|
935
|
+
return tracked;
|
|
936
|
+
}
|
|
937
|
+
publishCatalog(catalog) {
|
|
938
|
+
if (this.closed)
|
|
939
|
+
return Promise.reject(new Error("Remote workspace protocol server is closed"));
|
|
940
|
+
const clonedCatalog = cloneCatalog(catalog);
|
|
941
|
+
const operation = this.receiveQueue.then(() => this.publishCatalogNow(clonedCatalog));
|
|
942
|
+
this.receiveQueue = operation.catch(() => undefined);
|
|
943
|
+
return operation;
|
|
944
|
+
}
|
|
945
|
+
async publishCatalogNow(catalog) {
|
|
946
|
+
if (this.closed)
|
|
947
|
+
throw new Error("Remote workspace protocol server is closed");
|
|
948
|
+
this.validateCatalogCapabilities(catalog, new Set(this.negotiation.serverCapabilities));
|
|
949
|
+
if (catalog.generation <= this.catalog.generation)
|
|
950
|
+
throw new Error("Remote workspace catalog generation must increase");
|
|
951
|
+
for (const [id, active] of Array.from(this.active)) {
|
|
952
|
+
if (active.kind !== "process" && active.kind !== "service")
|
|
953
|
+
continue;
|
|
954
|
+
await this.cancelActive(id, this.createError("generation_retired", "Remote workspace generation retired", "not_started", false), true);
|
|
955
|
+
}
|
|
956
|
+
this.catalog = catalog;
|
|
957
|
+
this.catalogHash = hashRemoteWorkspaceCatalog(catalog);
|
|
958
|
+
if (this.handshaken && this.negotiatedCapabilities.has("catalog_refresh")) {
|
|
959
|
+
await this.send({ type: "catalog_changed", generation: catalog.generation, catalogHash: this.catalogHash });
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
async beginDrain(message = "Remote workspace connection is draining") {
|
|
963
|
+
if (this.closed)
|
|
964
|
+
return;
|
|
965
|
+
this.draining = true;
|
|
966
|
+
await Promise.all(Array.from(this.active.keys()).map((id) => this.cancelActive(id, this.createError("connection_draining", message, "not_started", false), false)));
|
|
967
|
+
}
|
|
968
|
+
disconnect() {
|
|
969
|
+
return this.disconnectInternal(true);
|
|
970
|
+
}
|
|
971
|
+
disconnectInternal(observeReceiveQueue) {
|
|
972
|
+
if (this.disconnectPromise)
|
|
973
|
+
return this.disconnectPromise;
|
|
974
|
+
if (this.closed)
|
|
975
|
+
return Promise.resolve();
|
|
976
|
+
this.closed = true;
|
|
977
|
+
const activeRequests = Array.from(this.active.values());
|
|
978
|
+
for (const active of activeRequests) {
|
|
979
|
+
clearTimeout(active.timer);
|
|
980
|
+
active.controller.abort();
|
|
981
|
+
}
|
|
982
|
+
const queuedSends = this.sendQueue;
|
|
983
|
+
const queuedReceives = this.receiveQueue;
|
|
984
|
+
this.disconnectPromise = Promise.all([
|
|
985
|
+
...activeRequests.map(async (active) => {
|
|
986
|
+
const termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(() => undefined);
|
|
987
|
+
const stopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);
|
|
988
|
+
active.settled = true;
|
|
989
|
+
this.active.delete(active.request.id);
|
|
990
|
+
return stopped;
|
|
991
|
+
}),
|
|
992
|
+
settlesWithinDeadline(queuedSends, this.limits.maxTransportSendMs),
|
|
993
|
+
...(observeReceiveQueue ? [settlesWithinDeadline(queuedReceives, this.limits.maxCancellationMs)] : []),
|
|
994
|
+
]).then((results) => {
|
|
995
|
+
if (results.some((stopped) => !stopped)) {
|
|
996
|
+
throw new RemoteWorkspaceTerminationError("Remote workspace handlers did not terminate before the disconnect deadline");
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
return this.disconnectPromise;
|
|
1000
|
+
}
|
|
1001
|
+
async receivePayload(payload) {
|
|
1002
|
+
if (this.closed)
|
|
1003
|
+
return;
|
|
1004
|
+
let decoded;
|
|
1005
|
+
try {
|
|
1006
|
+
decoded = decodeRemoteWorkspaceMessage(payload, this.limits);
|
|
1007
|
+
}
|
|
1008
|
+
catch (error) {
|
|
1009
|
+
await this.closeWithReason({ code: classifyValidationClose(error), message: this.errorMessage(error) });
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
let message;
|
|
1013
|
+
try {
|
|
1014
|
+
message = parseRemoteWorkspaceClientMessage(decoded, this.limits);
|
|
1015
|
+
}
|
|
1016
|
+
catch (error) {
|
|
1017
|
+
await this.handleInvalidMessage(decoded, error);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
if (!this.handshaken) {
|
|
1021
|
+
if (message.type !== "handshake") {
|
|
1022
|
+
await this.closeWithReason({ code: "protocol_error", message: "Request received before handshake" });
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
1025
|
+
await this.handleHandshake(message);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (message.type === "handshake") {
|
|
1029
|
+
await this.closeWithReason({ code: "protocol_error", message: "Duplicate handshake" });
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (message.type === "cancel") {
|
|
1033
|
+
await this.handleCancel(message.id);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
if (message.type === "transfer_chunk") {
|
|
1037
|
+
await this.handleUploadChunk(message.id, message.sequence, message.dataBase64);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (message.type === "transfer_finish") {
|
|
1041
|
+
await this.handleUploadFinish(message.id, message.length, message.sha256);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
await this.handleRequest(message);
|
|
1045
|
+
}
|
|
1046
|
+
async handleRequestValidationFailure(requestId, error) {
|
|
1047
|
+
this.addTombstone(requestId);
|
|
1048
|
+
const semantic = error instanceof RemoteWorkspaceRequestError;
|
|
1049
|
+
if (!semantic)
|
|
1050
|
+
this.strikes++;
|
|
1051
|
+
const protocolError = semantic
|
|
1052
|
+
? {
|
|
1053
|
+
code: error.code,
|
|
1054
|
+
message: error.message,
|
|
1055
|
+
executionState: error.executionState,
|
|
1056
|
+
retryable: error.retryable,
|
|
1057
|
+
...(error.details === undefined ? {} : { details: error.details }),
|
|
1058
|
+
}
|
|
1059
|
+
: this.createError("invalid_request", this.errorMessage(error), "not_started", false);
|
|
1060
|
+
await this.send({ type: "error", id: requestId, error: protocolError });
|
|
1061
|
+
if (this.strikes >= this.invalidRequestStrikes) {
|
|
1062
|
+
await this.closeWithReason({ code: "policy_violation", message: "Too many invalid requests" });
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
async handleHandshake(handshake) {
|
|
1066
|
+
if (this.isDuplicateId(handshake.id)) {
|
|
1067
|
+
await this.closeWithReason({ code: "protocol_error", message: `Duplicate request ID: ${handshake.id}` });
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
try {
|
|
1071
|
+
const negotiated = negotiateRemoteWorkspaceHandshake(handshake, this.negotiation);
|
|
1072
|
+
this.limits = negotiated.limits;
|
|
1073
|
+
this.negotiatedCapabilities = new Set(negotiated.capabilities);
|
|
1074
|
+
this.handshaken = true;
|
|
1075
|
+
this.addTombstone(handshake.id);
|
|
1076
|
+
await this.send({
|
|
1077
|
+
type: "handshake_ack",
|
|
1078
|
+
id: handshake.id,
|
|
1079
|
+
version: negotiated.version,
|
|
1080
|
+
capabilities: negotiated.capabilities,
|
|
1081
|
+
workspace: this.workspace,
|
|
1082
|
+
limits: negotiated.limits,
|
|
1083
|
+
catalog: this.catalog,
|
|
1084
|
+
catalogHash: this.catalogHash,
|
|
1085
|
+
backendMetadata: {
|
|
1086
|
+
kind: "remote-workspace",
|
|
1087
|
+
...(this.backendLabel ? { label: this.backendLabel } : {}),
|
|
1088
|
+
},
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
catch (error) {
|
|
1092
|
+
const protocolError = error instanceof RemoteWorkspaceNegotiationError
|
|
1093
|
+
? this.createError(error.code, error.message, "not_started", false)
|
|
1094
|
+
: this.createError("invalid_request", this.errorMessage(error), "not_started", false);
|
|
1095
|
+
await this.send({ type: "error", id: handshake.id, error: protocolError }).catch(() => undefined);
|
|
1096
|
+
await this.closeWithReason({ code: "policy_violation", message: protocolError.message });
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
async handleRequest(request) {
|
|
1100
|
+
if (this.isDuplicateId(request.id)) {
|
|
1101
|
+
await this.closeWithReason({ code: "protocol_error", message: `Duplicate request ID: ${request.id}` });
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
if (this.draining) {
|
|
1105
|
+
this.addTombstone(request.id);
|
|
1106
|
+
await this.sendError(request.id, "connection_draining", "Connection is draining", "not_started", false);
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
if (!isRemoteWorkspaceMethod(request.method)) {
|
|
1110
|
+
this.addTombstone(request.id);
|
|
1111
|
+
await this.sendError(request.id, "method_not_supported", `Unsupported method: ${request.method}`, "not_started", false);
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
const typedRequest = request;
|
|
1115
|
+
try {
|
|
1116
|
+
parseRemoteWorkspaceRequestParams(typedRequest.method, typedRequest.params, this.limits);
|
|
1117
|
+
this.assertMethodAvailable(typedRequest);
|
|
1118
|
+
this.validateCatalogRequest(typedRequest);
|
|
1119
|
+
}
|
|
1120
|
+
catch (error) {
|
|
1121
|
+
await this.handleRequestValidationFailure(request.id, error);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
if (this.active.size >= this.limits.maxActiveRequests) {
|
|
1125
|
+
this.addTombstone(request.id);
|
|
1126
|
+
await this.sendError(request.id, "limit_exceeded", "Active request limit reached", "not_started", true);
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
if (isTransferMethod(typedRequest.method) && this.activeTransferCount() >= this.limits.maxActiveTransfers) {
|
|
1130
|
+
this.addTombstone(request.id);
|
|
1131
|
+
await this.sendError(request.id, "limit_exceeded", "Active transfer limit reached", "not_started", true);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
const controller = new AbortController();
|
|
1135
|
+
const active = this.createActiveRequest(typedRequest, controller, Math.min(request.timeoutMs, this.limits.maxRequestMs));
|
|
1136
|
+
this.active.set(request.id, active);
|
|
1137
|
+
if (typedRequest.method === "catalog.get") {
|
|
1138
|
+
await this.completeActive(request.id, this.catalog);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
this.startHandler(active);
|
|
1142
|
+
}
|
|
1143
|
+
createActiveRequest(request, controller, timeoutMs) {
|
|
1144
|
+
let active;
|
|
1145
|
+
const context = {
|
|
1146
|
+
signal: controller.signal,
|
|
1147
|
+
request,
|
|
1148
|
+
markSideEffectStarted: () => {
|
|
1149
|
+
if (!active.settled)
|
|
1150
|
+
active.sideEffectStarted = true;
|
|
1151
|
+
},
|
|
1152
|
+
markCommitted: () => {
|
|
1153
|
+
if (!active.settled) {
|
|
1154
|
+
active.sideEffectStarted = true;
|
|
1155
|
+
active.committed = true;
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
sendUpdate: (update) => {
|
|
1159
|
+
if (!this.negotiatedCapabilities.has("tool_updates")) {
|
|
1160
|
+
return Promise.reject(new RemoteWorkspaceRequestError(this.createError("not_available", "Tool updates were not negotiated", "not_started", false)));
|
|
1161
|
+
}
|
|
1162
|
+
let prepared;
|
|
1163
|
+
try {
|
|
1164
|
+
prepared = prepareServerOutbound({ type: "update", id: request.id, sequence: active.nextUpdateSequence, update }, this.limits);
|
|
1165
|
+
}
|
|
1166
|
+
catch (error) {
|
|
1167
|
+
return Promise.reject(new RemoteWorkspaceRequestError(this.createError("result_too_large", this.errorMessage(error), this.executionStateFromActive(active), false)));
|
|
1168
|
+
}
|
|
1169
|
+
try {
|
|
1170
|
+
const queued = this.queueActiveOutbound(active, prepared.bytes, async () => {
|
|
1171
|
+
if (!active.settled)
|
|
1172
|
+
await this.sendPrepared(prepared, true);
|
|
1173
|
+
});
|
|
1174
|
+
active.nextUpdateSequence++;
|
|
1175
|
+
return queued;
|
|
1176
|
+
}
|
|
1177
|
+
catch (error) {
|
|
1178
|
+
return Promise.reject(error);
|
|
1179
|
+
}
|
|
1180
|
+
},
|
|
1181
|
+
startTransfer: (length, sha256) => {
|
|
1182
|
+
const prepared = prepareServerOutbound({ type: "transfer_start", id: request.id, length, sha256 }, this.limits);
|
|
1183
|
+
return this.queueActiveOutbound(active, prepared.bytes, async () => {
|
|
1184
|
+
if (active.settled || active.outgoingTransfer)
|
|
1185
|
+
throw new Error("Transfer has already started or settled");
|
|
1186
|
+
if (!this.negotiatedCapabilities.has("file_transfer"))
|
|
1187
|
+
throw new Error("File transfer was not negotiated");
|
|
1188
|
+
if (length > this.limits.maxTransferBytes)
|
|
1189
|
+
throw new Error("Transfer exceeds total-byte limit");
|
|
1190
|
+
active.outgoingTransfer = {
|
|
1191
|
+
expectedLength: length,
|
|
1192
|
+
expectedSha256: sha256,
|
|
1193
|
+
bytes: 0,
|
|
1194
|
+
pendingBytes: 0,
|
|
1195
|
+
chunks: 0,
|
|
1196
|
+
hash: createHash("sha256"),
|
|
1197
|
+
finishing: false,
|
|
1198
|
+
finished: false,
|
|
1199
|
+
};
|
|
1200
|
+
await this.sendPrepared(prepared, true);
|
|
1201
|
+
});
|
|
1202
|
+
},
|
|
1203
|
+
sendTransferChunk: (chunk) => {
|
|
1204
|
+
if (chunk.byteLength > this.limits.maxTransferChunkBytes) {
|
|
1205
|
+
return Promise.reject(new Error("Transfer chunk exceeds limit"));
|
|
1206
|
+
}
|
|
1207
|
+
if (active.pendingOutgoingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||
|
|
1208
|
+
this.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes) {
|
|
1209
|
+
return Promise.reject(new Error("Pending transfer chunks exceed limit"));
|
|
1210
|
+
}
|
|
1211
|
+
const copiedChunk = Buffer.from(chunk);
|
|
1212
|
+
const prepared = prepareServerOutbound({
|
|
1213
|
+
type: "transfer_chunk",
|
|
1214
|
+
id: request.id,
|
|
1215
|
+
sequence: active.nextOutgoingTransferSequence,
|
|
1216
|
+
dataBase64: copiedChunk.toString("base64"),
|
|
1217
|
+
}, this.limits);
|
|
1218
|
+
active.pendingOutgoingTransferBytes += copiedChunk.byteLength;
|
|
1219
|
+
this.pendingTransferBytes += copiedChunk.byteLength;
|
|
1220
|
+
try {
|
|
1221
|
+
const queued = this.queueActiveOutbound(active, prepared.bytes, async () => {
|
|
1222
|
+
const transfer = active.outgoingTransfer;
|
|
1223
|
+
if (active.settled || !transfer || transfer.finished)
|
|
1224
|
+
throw new Error("Transfer has not started or has settled");
|
|
1225
|
+
if (transfer.chunks + 1 > this.limits.maxTransferChunks ||
|
|
1226
|
+
transfer.bytes + copiedChunk.byteLength > this.limits.maxTransferBytes ||
|
|
1227
|
+
transfer.bytes + copiedChunk.byteLength > transfer.expectedLength) {
|
|
1228
|
+
throw new Error("Transfer exceeds declared or negotiated limits");
|
|
1229
|
+
}
|
|
1230
|
+
await this.sendPrepared(prepared, true);
|
|
1231
|
+
transfer.chunks++;
|
|
1232
|
+
transfer.bytes += copiedChunk.byteLength;
|
|
1233
|
+
transfer.hash.update(copiedChunk);
|
|
1234
|
+
});
|
|
1235
|
+
active.nextOutgoingTransferSequence++;
|
|
1236
|
+
return queued.finally(() => {
|
|
1237
|
+
active.pendingOutgoingTransferBytes -= copiedChunk.byteLength;
|
|
1238
|
+
this.pendingTransferBytes -= copiedChunk.byteLength;
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
catch (error) {
|
|
1242
|
+
active.pendingOutgoingTransferBytes -= copiedChunk.byteLength;
|
|
1243
|
+
this.pendingTransferBytes -= copiedChunk.byteLength;
|
|
1244
|
+
return Promise.reject(error);
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1247
|
+
};
|
|
1248
|
+
let resolveHandlerSettled = () => undefined;
|
|
1249
|
+
const handlerSettled = new Promise((resolve) => {
|
|
1250
|
+
resolveHandlerSettled = resolve;
|
|
1251
|
+
});
|
|
1252
|
+
const timer = setTimeout(() => {
|
|
1253
|
+
void this.cancelActive(request.id, this.createError("deadline_exceeded", "Remote workspace request deadline exceeded", "not_started", false), false).catch(() => undefined);
|
|
1254
|
+
}, timeoutMs);
|
|
1255
|
+
active = {
|
|
1256
|
+
request,
|
|
1257
|
+
controller,
|
|
1258
|
+
context,
|
|
1259
|
+
timer,
|
|
1260
|
+
kind: this.requestKind(request),
|
|
1261
|
+
sideEffectStarted: false,
|
|
1262
|
+
committed: false,
|
|
1263
|
+
nextUpdateSequence: 0,
|
|
1264
|
+
nextIncomingTransferSequence: 0,
|
|
1265
|
+
nextOutgoingTransferSequence: 0,
|
|
1266
|
+
pendingOutgoingTransferBytes: 0,
|
|
1267
|
+
outboundQueue: Promise.resolve(),
|
|
1268
|
+
inboundQueue: Promise.resolve(),
|
|
1269
|
+
handlerSettled,
|
|
1270
|
+
resolveHandlerSettled,
|
|
1271
|
+
handlerDone: false,
|
|
1272
|
+
...(request.method === "transfer.upload"
|
|
1273
|
+
? { incomingTransfer: this.createServerUploadState(request.params) }
|
|
1274
|
+
: {}),
|
|
1275
|
+
settled: false,
|
|
1276
|
+
};
|
|
1277
|
+
return active;
|
|
1278
|
+
}
|
|
1279
|
+
startHandler(active) {
|
|
1280
|
+
void (async () => {
|
|
1281
|
+
try {
|
|
1282
|
+
const result = await this.handler.handleRequest(active.request, active.context);
|
|
1283
|
+
active.handlerDone = true;
|
|
1284
|
+
active.resolveHandlerSettled();
|
|
1285
|
+
if (active.settled)
|
|
1286
|
+
return;
|
|
1287
|
+
if (active.cancellationError)
|
|
1288
|
+
return;
|
|
1289
|
+
await this.completeActive(active.request.id, result);
|
|
1290
|
+
}
|
|
1291
|
+
catch (error) {
|
|
1292
|
+
active.handlerDone = true;
|
|
1293
|
+
active.resolveHandlerSettled();
|
|
1294
|
+
if (active.settled)
|
|
1295
|
+
return;
|
|
1296
|
+
if (active.cancellationError)
|
|
1297
|
+
return;
|
|
1298
|
+
await this.handleHandlerFailure(active.request.id, error);
|
|
1299
|
+
}
|
|
1300
|
+
})();
|
|
1301
|
+
}
|
|
1302
|
+
queueActiveOutbound(active, estimatedBytes, operation) {
|
|
1303
|
+
if (this.closed || active.settled || this.active.get(active.request.id) !== active) {
|
|
1304
|
+
throw new RemoteWorkspaceRequestError(this.createError("connection_draining", "Remote workspace request is no longer active", this.executionStateFromActive(active), false));
|
|
1305
|
+
}
|
|
1306
|
+
if (this.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||
|
|
1307
|
+
this.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes) {
|
|
1308
|
+
throw new RemoteWorkspaceRequestError(this.createError("limit_exceeded", "Remote workspace outbound queue exceeds limits", "not_started", false));
|
|
1309
|
+
}
|
|
1310
|
+
this.pendingOutboundMessages++;
|
|
1311
|
+
this.pendingOutboundBytes += estimatedBytes;
|
|
1312
|
+
const queued = active.outboundQueue.then(operation);
|
|
1313
|
+
const result = queued.finally(() => {
|
|
1314
|
+
this.pendingOutboundMessages--;
|
|
1315
|
+
this.pendingOutboundBytes -= estimatedBytes;
|
|
1316
|
+
});
|
|
1317
|
+
active.outboundQueue = result.then(() => undefined, () => undefined);
|
|
1318
|
+
return result;
|
|
1319
|
+
}
|
|
1320
|
+
async cancelActive(id, error, requireTermination) {
|
|
1321
|
+
const active = this.active.get(id);
|
|
1322
|
+
if (!active || active.settled)
|
|
1323
|
+
return;
|
|
1324
|
+
active.cancellationError ??= error;
|
|
1325
|
+
active.controller.abort();
|
|
1326
|
+
const termination = Promise.all([active.handlerSettled, active.inboundQueue, active.outboundQueue]).then(() => undefined);
|
|
1327
|
+
const handlerStopped = await this.settlesWithin(termination, this.limits.maxCancellationMs);
|
|
1328
|
+
if (handlerStopped) {
|
|
1329
|
+
await this.finalizeCancellation(active);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
if (requireTermination) {
|
|
1333
|
+
await this.closeUnconfirmedCancellation(id);
|
|
1334
|
+
throw new Error(`Remote workspace request did not stop before generation retirement: ${id}`);
|
|
1335
|
+
}
|
|
1336
|
+
await this.closeUnconfirmedCancellation(id);
|
|
1337
|
+
}
|
|
1338
|
+
async finalizeCancellation(active) {
|
|
1339
|
+
if (active.settled || !active.cancellationError)
|
|
1340
|
+
return;
|
|
1341
|
+
await active.outboundQueue;
|
|
1342
|
+
if (active.settled)
|
|
1343
|
+
return;
|
|
1344
|
+
await this.failActive(active.request.id, {
|
|
1345
|
+
...active.cancellationError,
|
|
1346
|
+
executionState: this.executionStateFromActive(active),
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1349
|
+
async closeUnconfirmedCancellation(id) {
|
|
1350
|
+
if (this.closed)
|
|
1351
|
+
return;
|
|
1352
|
+
this.closed = true;
|
|
1353
|
+
this.draining = true;
|
|
1354
|
+
const activeRequests = Array.from(this.active.values());
|
|
1355
|
+
for (const active of activeRequests) {
|
|
1356
|
+
clearTimeout(active.timer);
|
|
1357
|
+
active.controller.abort();
|
|
1358
|
+
active.settled = true;
|
|
1359
|
+
}
|
|
1360
|
+
await Promise.all([
|
|
1361
|
+
settlesWithinDeadline(Promise.all(activeRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue])).then(() => undefined), this.limits.maxCancellationMs),
|
|
1362
|
+
settlesWithinDeadline(this.sendQueue, this.limits.maxTransportSendMs),
|
|
1363
|
+
]);
|
|
1364
|
+
this.active.clear();
|
|
1365
|
+
const closed = await settlesWithinDeadline(Promise.resolve(this.transport.close(boundedCloseReason({
|
|
1366
|
+
code: "policy_violation",
|
|
1367
|
+
message: `Remote workspace request did not stop after cancellation: ${id}`,
|
|
1368
|
+
}))), this.limits.maxTransportSendMs).catch(() => false);
|
|
1369
|
+
if (!closed)
|
|
1370
|
+
throw new RemoteWorkspaceTerminationError("Remote workspace transport close timed out");
|
|
1371
|
+
throw new RemoteWorkspaceTerminationError(`Remote workspace request did not terminate: ${id}`);
|
|
1372
|
+
}
|
|
1373
|
+
settlesWithin(promise, timeoutMs) {
|
|
1374
|
+
return new Promise((resolve) => {
|
|
1375
|
+
let settled = false;
|
|
1376
|
+
const timer = setTimeout(() => {
|
|
1377
|
+
if (settled)
|
|
1378
|
+
return;
|
|
1379
|
+
settled = true;
|
|
1380
|
+
resolve(false);
|
|
1381
|
+
}, timeoutMs);
|
|
1382
|
+
promise.then(() => {
|
|
1383
|
+
if (settled)
|
|
1384
|
+
return;
|
|
1385
|
+
settled = true;
|
|
1386
|
+
clearTimeout(timer);
|
|
1387
|
+
resolve(true);
|
|
1388
|
+
});
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
assertMethodAvailable(request) {
|
|
1392
|
+
const capability = methodCapability(request.method);
|
|
1393
|
+
if (capability && !this.negotiatedCapabilities.has(capability)) {
|
|
1394
|
+
throw new RemoteWorkspaceRequestError(this.createError("not_available", `Capability was not negotiated: ${capability}`, "not_started", false));
|
|
1395
|
+
}
|
|
1396
|
+
if (request.method === "artifact.read" && !this.negotiatedCapabilities.has("file_transfer")) {
|
|
1397
|
+
throw new RemoteWorkspaceRequestError(this.createError("not_available", "Artifact reads require file transfer", "not_started", false));
|
|
1398
|
+
}
|
|
1399
|
+
if (request.method !== "catalog.get" &&
|
|
1400
|
+
request.method !== "tool.invoke" &&
|
|
1401
|
+
!this.catalog.operations.includes(request.method)) {
|
|
1402
|
+
throw new RemoteWorkspaceRequestError(this.createError("not_available", `Operation is not advertised: ${request.method}`, "not_started", false));
|
|
1403
|
+
}
|
|
1404
|
+
if (request.method === "transfer.upload") {
|
|
1405
|
+
const upload = request.params;
|
|
1406
|
+
if (upload.length > this.limits.maxTransferBytes) {
|
|
1407
|
+
throw new RemoteWorkspaceRequestError(this.createError("limit_exceeded", "Upload exceeds total-byte limit", "not_started", false));
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
validateCatalogRequest(request) {
|
|
1412
|
+
if (request.method !== "tool.invoke")
|
|
1413
|
+
return;
|
|
1414
|
+
const params = request.params;
|
|
1415
|
+
if (params.generation !== this.catalog.generation || params.catalogHash !== this.catalogHash) {
|
|
1416
|
+
throw new RemoteWorkspaceRequestError(this.createError("stale_generation", "Tool catalog generation is stale", "not_started", true));
|
|
1417
|
+
}
|
|
1418
|
+
const tool = this.catalog.tools.find((entry) => entry.name === params.toolName);
|
|
1419
|
+
if (!tool) {
|
|
1420
|
+
throw new RemoteWorkspaceRequestError(this.createError("not_available", `Tool is not available: ${params.toolName}`, "not_started", false));
|
|
1421
|
+
}
|
|
1422
|
+
for (const featureFlag of tool.featureFlags) {
|
|
1423
|
+
if (!this.negotiatedCapabilities.has(featureFlag)) {
|
|
1424
|
+
throw new RemoteWorkspaceRequestError(this.createError("not_available", `Tool ${params.toolName} requires unnegotiated capability ${featureFlag}`, "not_started", false));
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
if (tool.schemaHash !== params.schemaHash) {
|
|
1428
|
+
throw new RemoteWorkspaceRequestError(this.createError("schema_mismatch", `Schema mismatch for tool ${params.toolName}`, "not_started", false));
|
|
1429
|
+
}
|
|
1430
|
+
if (!this.handler.validateToolArguments(params.toolName, params.arguments)) {
|
|
1431
|
+
throw new RemoteWorkspaceValidationError(`Arguments do not match the canonical schema for ${params.toolName}`);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
requestKind(request) {
|
|
1435
|
+
if (request.method !== "tool.invoke")
|
|
1436
|
+
return getRemoteWorkspaceMethodKind(request.method);
|
|
1437
|
+
const params = request.params;
|
|
1438
|
+
return this.catalog.tools.find((tool) => tool.name === params.toolName)?.executionMode ?? "service";
|
|
1439
|
+
}
|
|
1440
|
+
createServerUploadState(params) {
|
|
1441
|
+
const upload = params;
|
|
1442
|
+
if (upload.length > this.limits.maxTransferBytes) {
|
|
1443
|
+
throw new RemoteWorkspaceRequestError(this.createError("limit_exceeded", "Upload exceeds total-byte limit", "not_started", false));
|
|
1444
|
+
}
|
|
1445
|
+
return {
|
|
1446
|
+
expectedLength: upload.length,
|
|
1447
|
+
expectedSha256: upload.sha256,
|
|
1448
|
+
bytes: 0,
|
|
1449
|
+
pendingBytes: 0,
|
|
1450
|
+
chunks: 0,
|
|
1451
|
+
hash: createHash("sha256"),
|
|
1452
|
+
finishing: false,
|
|
1453
|
+
finished: false,
|
|
1454
|
+
};
|
|
1455
|
+
}
|
|
1456
|
+
async handleUploadChunk(id, sequence, dataBase64) {
|
|
1457
|
+
const active = this.active.get(id);
|
|
1458
|
+
if (!active) {
|
|
1459
|
+
await this.handleUnknownCorrelatedId(id, "upload chunk");
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
const transfer = active.incomingTransfer;
|
|
1463
|
+
if (!transfer || transfer.finished || sequence !== active.nextIncomingTransferSequence) {
|
|
1464
|
+
await this.closeWithReason({ code: "protocol_error", message: `Out-of-order upload chunk for ${id}` });
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
let chunk;
|
|
1468
|
+
try {
|
|
1469
|
+
chunk = decodeCanonicalBase64(dataBase64, this.limits.maxTransferChunkBytes);
|
|
1470
|
+
if (transfer.chunks + 1 > this.limits.maxTransferChunks ||
|
|
1471
|
+
transfer.bytes + chunk.byteLength > this.limits.maxTransferBytes ||
|
|
1472
|
+
transfer.bytes + chunk.byteLength > transfer.expectedLength ||
|
|
1473
|
+
transfer.pendingBytes + chunk.byteLength > this.limits.maxPendingTransferBytes ||
|
|
1474
|
+
this.pendingTransferBytes + chunk.byteLength > this.limits.maxPendingTransferBytes) {
|
|
1475
|
+
throw new RemoteWorkspaceValidationError("Upload exceeds declared or negotiated limits");
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
catch (error) {
|
|
1479
|
+
await this.cancelActive(id, this.createError("limit_exceeded", this.errorMessage(error), "not_started", false), false);
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
active.nextIncomingTransferSequence++;
|
|
1483
|
+
transfer.chunks++;
|
|
1484
|
+
transfer.bytes += chunk.byteLength;
|
|
1485
|
+
transfer.pendingBytes += chunk.byteLength;
|
|
1486
|
+
this.pendingTransferBytes += chunk.byteLength;
|
|
1487
|
+
transfer.hash.update(chunk);
|
|
1488
|
+
const operation = active.inboundQueue
|
|
1489
|
+
.then(async () => {
|
|
1490
|
+
if (active.settled)
|
|
1491
|
+
return;
|
|
1492
|
+
await this.handler.handleUploadChunk?.(active.request, chunk, sequence, active.context);
|
|
1493
|
+
})
|
|
1494
|
+
.finally(() => {
|
|
1495
|
+
transfer.pendingBytes -= chunk.byteLength;
|
|
1496
|
+
this.pendingTransferBytes -= chunk.byteLength;
|
|
1497
|
+
});
|
|
1498
|
+
active.inboundQueue = operation.catch(() => undefined);
|
|
1499
|
+
void operation.catch(async (error) => {
|
|
1500
|
+
if (this.active.get(id) === active)
|
|
1501
|
+
await this.handleHandlerFailure(id, error);
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
async handleUploadFinish(id, length, sha256) {
|
|
1505
|
+
const active = this.active.get(id);
|
|
1506
|
+
if (!active) {
|
|
1507
|
+
await this.handleUnknownCorrelatedId(id, "upload finish");
|
|
1508
|
+
return;
|
|
1509
|
+
}
|
|
1510
|
+
const transfer = active.incomingTransfer;
|
|
1511
|
+
if (!transfer || transfer.finishing || transfer.finished) {
|
|
1512
|
+
await this.closeWithReason({ code: "protocol_error", message: `Unexpected upload finish for ${id}` });
|
|
1513
|
+
return;
|
|
1514
|
+
}
|
|
1515
|
+
transfer.finishing = true;
|
|
1516
|
+
const digest = transfer.hash.digest("hex");
|
|
1517
|
+
if (length !== transfer.expectedLength ||
|
|
1518
|
+
sha256 !== transfer.expectedSha256 ||
|
|
1519
|
+
transfer.bytes !== transfer.expectedLength ||
|
|
1520
|
+
digest !== transfer.expectedSha256) {
|
|
1521
|
+
await this.cancelActive(id, this.createError("invalid_request", "Upload length or SHA-256 verification failed", "not_started", false), false);
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
const operation = active.inboundQueue.then(async () => {
|
|
1525
|
+
if (active.settled)
|
|
1526
|
+
return;
|
|
1527
|
+
await this.handler.handleUploadFinish?.(active.request, { length, sha256 }, active.context);
|
|
1528
|
+
transfer.finished = true;
|
|
1529
|
+
});
|
|
1530
|
+
active.inboundQueue = operation.catch(() => undefined);
|
|
1531
|
+
void operation.catch(async (error) => {
|
|
1532
|
+
if (this.active.get(id) === active)
|
|
1533
|
+
await this.handleHandlerFailure(id, error);
|
|
1534
|
+
});
|
|
1535
|
+
}
|
|
1536
|
+
async handleCancel(id) {
|
|
1537
|
+
if (this.active.has(id)) {
|
|
1538
|
+
await this.cancelActive(id, this.createError("cancelled", "Remote workspace request cancelled", "not_started", false), false);
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
this.pruneTombstones();
|
|
1542
|
+
if (this.tombstones.has(id))
|
|
1543
|
+
return;
|
|
1544
|
+
await this.closeWithReason({ code: "protocol_error", message: `Cancellation for unknown request ID: ${id}` });
|
|
1545
|
+
}
|
|
1546
|
+
async completeActive(id, result) {
|
|
1547
|
+
const active = this.active.get(id);
|
|
1548
|
+
if (!active || active.settled)
|
|
1549
|
+
return;
|
|
1550
|
+
await Promise.all([active.outboundQueue, active.inboundQueue]);
|
|
1551
|
+
if (active.settled)
|
|
1552
|
+
return;
|
|
1553
|
+
if (active.cancellationError)
|
|
1554
|
+
return;
|
|
1555
|
+
try {
|
|
1556
|
+
const validated = parseRemoteWorkspaceResult(active.request.method, result, this.limits);
|
|
1557
|
+
assertResultWorkspaceIdentity(active.request.method, validated, this.workspace);
|
|
1558
|
+
this.verifyServerTransferResult(active, validated);
|
|
1559
|
+
this.takeActive(id);
|
|
1560
|
+
try {
|
|
1561
|
+
await this.send({ type: "result", id, result: validated });
|
|
1562
|
+
}
|
|
1563
|
+
catch (error) {
|
|
1564
|
+
if (!(error instanceof RemoteWorkspaceValidationError))
|
|
1565
|
+
throw error;
|
|
1566
|
+
await this.send({
|
|
1567
|
+
type: "error",
|
|
1568
|
+
id,
|
|
1569
|
+
error: this.createError("result_too_large", error.message, active.kind === "read" ? "not_started" : active.committed ? "completed" : "indeterminate", false),
|
|
1570
|
+
});
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
catch (error) {
|
|
1574
|
+
await this.handleHandlerFailure(id, error);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
verifyServerTransferResult(active, result) {
|
|
1578
|
+
if (active.request.method === "transfer.upload") {
|
|
1579
|
+
const transfer = active.incomingTransfer;
|
|
1580
|
+
const metadata = result;
|
|
1581
|
+
if (!transfer?.finished ||
|
|
1582
|
+
metadata.length !== transfer.expectedLength ||
|
|
1583
|
+
metadata.sha256 !== transfer.expectedSha256) {
|
|
1584
|
+
throw new RemoteWorkspaceValidationError("Upload completed without verified terminal metadata");
|
|
1585
|
+
}
|
|
1586
|
+
return;
|
|
1587
|
+
}
|
|
1588
|
+
if (active.request.method !== "transfer.download" && active.request.method !== "artifact.read")
|
|
1589
|
+
return;
|
|
1590
|
+
const transfer = active.outgoingTransfer;
|
|
1591
|
+
const metadata = result;
|
|
1592
|
+
if (!transfer)
|
|
1593
|
+
throw new RemoteWorkspaceValidationError("Download completed without transfer metadata");
|
|
1594
|
+
const digest = transfer.hash.digest("hex");
|
|
1595
|
+
transfer.finished = true;
|
|
1596
|
+
if (transfer.bytes !== transfer.expectedLength ||
|
|
1597
|
+
digest !== transfer.expectedSha256 ||
|
|
1598
|
+
metadata.length !== transfer.expectedLength ||
|
|
1599
|
+
metadata.sha256 !== transfer.expectedSha256) {
|
|
1600
|
+
throw new RemoteWorkspaceValidationError("Download length or SHA-256 verification failed");
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
async handleHandlerFailure(id, error) {
|
|
1604
|
+
if (error instanceof RemoteWorkspaceRequestError) {
|
|
1605
|
+
await this.failActive(id, {
|
|
1606
|
+
code: error.code,
|
|
1607
|
+
message: error.message,
|
|
1608
|
+
executionState: this.reconcileExecutionState(id, error.executionState),
|
|
1609
|
+
retryable: error.retryable,
|
|
1610
|
+
...(error.details === undefined ? {} : { details: error.details }),
|
|
1611
|
+
});
|
|
1612
|
+
return;
|
|
1613
|
+
}
|
|
1614
|
+
await this.failActive(id, this.createError("internal_error", "Remote workspace request handler failed", this.executionStateFor(id), false));
|
|
1615
|
+
}
|
|
1616
|
+
async failActive(id, error) {
|
|
1617
|
+
const active = this.takeActive(id);
|
|
1618
|
+
if (!active)
|
|
1619
|
+
return;
|
|
1620
|
+
active.controller.abort();
|
|
1621
|
+
const [handlerStopped, outboundStopped, inboundStopped] = await Promise.all([
|
|
1622
|
+
settlesWithinDeadline(active.handlerSettled, this.limits.maxCancellationMs),
|
|
1623
|
+
settlesWithinDeadline(active.outboundQueue, this.limits.maxTransportSendMs),
|
|
1624
|
+
settlesWithinDeadline(active.inboundQueue, this.limits.maxCancellationMs),
|
|
1625
|
+
]);
|
|
1626
|
+
if (!handlerStopped || !outboundStopped || !inboundStopped) {
|
|
1627
|
+
await this.failServerTransport(new RemoteWorkspaceTransportError("Request callback queue did not drain"));
|
|
1628
|
+
return;
|
|
1629
|
+
}
|
|
1630
|
+
const boundedError = {
|
|
1631
|
+
...error,
|
|
1632
|
+
message: error.message.slice(0, 4096) || error.code,
|
|
1633
|
+
};
|
|
1634
|
+
try {
|
|
1635
|
+
await this.send({ type: "error", id, error: boundedError });
|
|
1636
|
+
}
|
|
1637
|
+
catch (sendError) {
|
|
1638
|
+
if (sendError instanceof RemoteWorkspaceTransportError)
|
|
1639
|
+
return;
|
|
1640
|
+
await this.send({
|
|
1641
|
+
type: "error",
|
|
1642
|
+
id,
|
|
1643
|
+
error: this.createError("internal_error", "Remote workspace error details exceeded protocol limits", boundedError.executionState, false),
|
|
1644
|
+
}).catch(() => undefined);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
takeActive(id) {
|
|
1648
|
+
const active = this.active.get(id);
|
|
1649
|
+
if (!active || active.settled)
|
|
1650
|
+
return undefined;
|
|
1651
|
+
active.settled = true;
|
|
1652
|
+
this.active.delete(id);
|
|
1653
|
+
clearTimeout(active.timer);
|
|
1654
|
+
this.addTombstone(id);
|
|
1655
|
+
return active;
|
|
1656
|
+
}
|
|
1657
|
+
executionStateFor(id) {
|
|
1658
|
+
const active = this.active.get(id);
|
|
1659
|
+
return active ? this.executionStateFromActive(active) : "not_started";
|
|
1660
|
+
}
|
|
1661
|
+
executionStateFromActive(active) {
|
|
1662
|
+
if (active.committed)
|
|
1663
|
+
return "completed";
|
|
1664
|
+
if (active.sideEffectStarted)
|
|
1665
|
+
return "indeterminate";
|
|
1666
|
+
return "not_started";
|
|
1667
|
+
}
|
|
1668
|
+
reconcileExecutionState(id, reported) {
|
|
1669
|
+
const active = this.active.get(id);
|
|
1670
|
+
if (!active)
|
|
1671
|
+
return reported;
|
|
1672
|
+
if (active.committed)
|
|
1673
|
+
return "completed";
|
|
1674
|
+
if (active.sideEffectStarted && reported === "not_started")
|
|
1675
|
+
return "indeterminate";
|
|
1676
|
+
return reported;
|
|
1677
|
+
}
|
|
1678
|
+
async handleInvalidMessage(value, error) {
|
|
1679
|
+
const id = this.usableId(value);
|
|
1680
|
+
if (!id) {
|
|
1681
|
+
await this.closeWithReason({ code: "invalid_payload", message: this.errorMessage(error) });
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
if (this.isDuplicateId(id)) {
|
|
1685
|
+
await this.closeWithReason({ code: "protocol_error", message: `Duplicate request ID: ${id}` });
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
this.addTombstone(id);
|
|
1689
|
+
this.strikes++;
|
|
1690
|
+
await this.sendError(id, "invalid_request", this.errorMessage(error), "not_started", false).catch(() => undefined);
|
|
1691
|
+
if (this.strikes >= this.invalidRequestStrikes) {
|
|
1692
|
+
await this.closeWithReason({ code: "policy_violation", message: "Too many invalid requests" });
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
usableId(value) {
|
|
1696
|
+
if (!value || typeof value !== "object" || !("id" in value))
|
|
1697
|
+
return undefined;
|
|
1698
|
+
const id = value.id;
|
|
1699
|
+
return typeof id === "string" && /^[A-Za-z0-9._:-]{1,128}$/.test(id) ? id : undefined;
|
|
1700
|
+
}
|
|
1701
|
+
async handleUnknownCorrelatedId(id, label) {
|
|
1702
|
+
this.pruneTombstones();
|
|
1703
|
+
if (this.tombstones.has(id))
|
|
1704
|
+
return;
|
|
1705
|
+
await this.closeWithReason({ code: "protocol_error", message: `${label} for unknown request ID: ${id}` });
|
|
1706
|
+
}
|
|
1707
|
+
isDuplicateId(id) {
|
|
1708
|
+
this.pruneTombstones();
|
|
1709
|
+
return this.active.has(id) || this.tombstones.has(id);
|
|
1710
|
+
}
|
|
1711
|
+
addTombstone(id) {
|
|
1712
|
+
this.pruneTombstones();
|
|
1713
|
+
if (this.tombstones.size >= this.maxTombstones) {
|
|
1714
|
+
void this.closeWithReason({
|
|
1715
|
+
code: "policy_violation",
|
|
1716
|
+
message: "Request tombstone limit exceeded",
|
|
1717
|
+
}).catch(() => undefined);
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
this.tombstones.set(id, performance.now() + this.tombstoneMs);
|
|
1721
|
+
}
|
|
1722
|
+
pruneTombstones() {
|
|
1723
|
+
const now = performance.now();
|
|
1724
|
+
for (const [id, expiresAt] of this.tombstones)
|
|
1725
|
+
if (expiresAt <= now)
|
|
1726
|
+
this.tombstones.delete(id);
|
|
1727
|
+
}
|
|
1728
|
+
activeTransferCount() {
|
|
1729
|
+
let count = 0;
|
|
1730
|
+
for (const active of this.active.values())
|
|
1731
|
+
if (isTransferMethod(active.request.method))
|
|
1732
|
+
count++;
|
|
1733
|
+
return count;
|
|
1734
|
+
}
|
|
1735
|
+
validateCatalogCapabilities(catalog, capabilities) {
|
|
1736
|
+
for (const method of catalog.operations) {
|
|
1737
|
+
const capability = methodCapability(method);
|
|
1738
|
+
if (capability && !capabilities.has(capability)) {
|
|
1739
|
+
throw new RemoteWorkspaceValidationError(`Catalog operation ${method} requires capability ${capability}`);
|
|
1740
|
+
}
|
|
1741
|
+
if (method === "artifact.read" && !capabilities.has("file_transfer")) {
|
|
1742
|
+
throw new RemoteWorkspaceValidationError("Artifact reads require file transfer capability");
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
for (const tool of catalog.tools) {
|
|
1746
|
+
for (const featureFlag of tool.featureFlags) {
|
|
1747
|
+
if (!capabilities.has(featureFlag)) {
|
|
1748
|
+
throw new RemoteWorkspaceValidationError(`Tool ${tool.name} requires unsupported capability ${featureFlag}`);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
createError(code, message, executionState, retryable) {
|
|
1754
|
+
return { code, message: message.slice(0, 4096) || code, executionState, retryable };
|
|
1755
|
+
}
|
|
1756
|
+
sendError(id, code, message, executionState, retryable) {
|
|
1757
|
+
return this.send({ type: "error", id, error: this.createError(code, message, executionState, retryable) });
|
|
1758
|
+
}
|
|
1759
|
+
send(message, preReserved = false) {
|
|
1760
|
+
if (this.closed)
|
|
1761
|
+
return Promise.reject(new Error("Remote workspace protocol server is closed"));
|
|
1762
|
+
return this.sendPrepared(prepareServerOutbound(message, this.limits), preReserved);
|
|
1763
|
+
}
|
|
1764
|
+
sendPrepared(prepared, preReserved = false) {
|
|
1765
|
+
if (this.closed)
|
|
1766
|
+
return Promise.reject(new Error("Remote workspace protocol server is closed"));
|
|
1767
|
+
const estimatedBytes = prepared.bytes;
|
|
1768
|
+
if (!preReserved &&
|
|
1769
|
+
(this.pendingOutboundMessages >= this.limits.maxPendingOutboundMessages ||
|
|
1770
|
+
this.pendingOutboundBytes + estimatedBytes > this.limits.maxPendingOutboundBytes)) {
|
|
1771
|
+
const error = new RemoteWorkspaceTransportError("Remote workspace outbound queue exceeds negotiated limits");
|
|
1772
|
+
void this.failServerTransport(error);
|
|
1773
|
+
return Promise.reject(error);
|
|
1774
|
+
}
|
|
1775
|
+
if (!preReserved) {
|
|
1776
|
+
this.pendingOutboundMessages++;
|
|
1777
|
+
this.pendingOutboundBytes += estimatedBytes;
|
|
1778
|
+
}
|
|
1779
|
+
const operation = this.sendQueue.then(async () => {
|
|
1780
|
+
if (this.closed)
|
|
1781
|
+
throw new RemoteWorkspaceTransportError("Remote workspace protocol server is closed");
|
|
1782
|
+
try {
|
|
1783
|
+
const sent = await settlesWithinDeadline(this.transport.send(prepared.message), this.limits.maxTransportSendMs);
|
|
1784
|
+
if (!sent)
|
|
1785
|
+
throw new RemoteWorkspaceTransportError("Remote workspace transport send timed out");
|
|
1786
|
+
}
|
|
1787
|
+
catch (error) {
|
|
1788
|
+
throw error instanceof RemoteWorkspaceTransportError ? error : new RemoteWorkspaceTransportError(error);
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
const tracked = operation.finally(() => {
|
|
1792
|
+
if (!preReserved) {
|
|
1793
|
+
this.pendingOutboundMessages--;
|
|
1794
|
+
this.pendingOutboundBytes -= estimatedBytes;
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1797
|
+
this.sendQueue = tracked.catch(() => undefined);
|
|
1798
|
+
return tracked.catch(async (error) => {
|
|
1799
|
+
if (error instanceof RemoteWorkspaceTransportError)
|
|
1800
|
+
await this.failServerTransport(error);
|
|
1801
|
+
throw error;
|
|
1802
|
+
});
|
|
1803
|
+
}
|
|
1804
|
+
failServerTransport(error) {
|
|
1805
|
+
this.transportFailurePromise ??= this.failServerTransportNow(error);
|
|
1806
|
+
return this.transportFailurePromise;
|
|
1807
|
+
}
|
|
1808
|
+
async failServerTransportNow(error) {
|
|
1809
|
+
const activeRequests = Array.from(this.active.values());
|
|
1810
|
+
this.closed = true;
|
|
1811
|
+
this.draining = true;
|
|
1812
|
+
for (const active of activeRequests) {
|
|
1813
|
+
clearTimeout(active.timer);
|
|
1814
|
+
active.controller.abort();
|
|
1815
|
+
active.settled = true;
|
|
1816
|
+
}
|
|
1817
|
+
await settlesWithinDeadline(Promise.all(activeRequests.flatMap((active) => [active.handlerSettled, active.inboundQueue, active.outboundQueue])).then(() => undefined), this.limits.maxCancellationMs).catch(() => false);
|
|
1818
|
+
this.active.clear();
|
|
1819
|
+
await settlesWithinDeadline(Promise.resolve(this.transport.close(boundedCloseReason({ code: "protocol_error", message: error.message }))), this.limits.maxTransportSendMs).catch(() => false);
|
|
1820
|
+
}
|
|
1821
|
+
errorMessage(error) {
|
|
1822
|
+
return error instanceof Error ? error.message : String(error);
|
|
1823
|
+
}
|
|
1824
|
+
async closeWithReason(reason) {
|
|
1825
|
+
if (this.closed)
|
|
1826
|
+
return;
|
|
1827
|
+
let terminationError;
|
|
1828
|
+
try {
|
|
1829
|
+
await this.disconnectInternal(false);
|
|
1830
|
+
}
|
|
1831
|
+
catch (error) {
|
|
1832
|
+
terminationError = error;
|
|
1833
|
+
}
|
|
1834
|
+
const closed = await settlesWithinDeadline(Promise.resolve(this.transport.close(boundedCloseReason(reason))), this.limits.maxTransportSendMs).catch(() => false);
|
|
1835
|
+
if (!closed && !terminationError) {
|
|
1836
|
+
terminationError = new RemoteWorkspaceTerminationError("Remote workspace transport close timed out");
|
|
1837
|
+
}
|
|
1838
|
+
if (terminationError)
|
|
1839
|
+
throw terminationError;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
//# sourceMappingURL=session.js.map
|