@code-yeongyu/senpi 2026.8.14 → 2026.8.17
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 +128 -0
- package/README.md +1 -0
- package/dist/bun/cli.js +1 -0
- package/dist/bun/cli.js.map +1 -1
- package/dist/bun/register-cursor-agent.d.ts +2 -0
- package/dist/bun/register-cursor-agent.d.ts.map +1 -0
- package/dist/bun/register-cursor-agent.js +4 -0
- package/dist/bun/register-cursor-agent.js.map +1 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +14 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/auth-command.d.ts.map +1 -1
- package/dist/cli/auth-command.js +5 -4
- package/dist/cli/auth-command.js.map +1 -1
- package/dist/core/agent-session-services.js +2 -2
- package/dist/core/agent-session-services.js.map +1 -1
- package/dist/core/agent-session.d.ts +127 -13
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +420 -82
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +19 -8
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/cursor-exec-bridge-session.d.ts +11 -0
- package/dist/core/cursor-exec-bridge-session.d.ts.map +1 -0
- package/dist/core/cursor-exec-bridge-session.js +17 -0
- package/dist/core/cursor-exec-bridge-session.js.map +1 -0
- package/dist/core/cursor-exec-bridge.d.ts +52 -0
- package/dist/core/cursor-exec-bridge.d.ts.map +1 -0
- package/dist/core/cursor-exec-bridge.js +196 -0
- package/dist/core/cursor-exec-bridge.js.map +1 -0
- package/dist/core/experimental.d.ts +4 -0
- package/dist/core/experimental.d.ts.map +1 -1
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -1
- package/dist/core/export-html/template.js +31 -9
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.d.ts +4 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.d.ts.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.js +30 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-environment.js.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.d.ts +2 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.js +13 -47
- package/dist/core/extensions/builtin/claude-sdk-oauth/auth-lane.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/availability.d.ts +18 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/availability.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/availability.js +64 -2
- package/dist/core/extensions/builtin/claude-sdk-oauth/availability.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.d.ts +3 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.d.ts.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.js +27 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/config-dir-credentials.js.map +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts +15 -2
- package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js +45 -11
- package/dist/core/extensions/builtin/claude-sdk-oauth/oauth-login.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +17 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/stream.d.ts.map +1 -1
- package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js +1 -0
- package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/index.js +48 -6
- package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
- package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/log.js +2 -0
- package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/overflow-retry.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/overflow-retry.js +38 -4
- package/dist/core/extensions/builtin/compaction/overflow-retry.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
- package/dist/core/extensions/builtin/compaction/speculative.js +2 -1
- package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
- package/dist/core/extensions/builtin/compaction/summarization-turn-order.d.ts +33 -0
- package/dist/core/extensions/builtin/compaction/summarization-turn-order.d.ts.map +1 -0
- package/dist/core/extensions/builtin/compaction/summarization-turn-order.js +67 -0
- package/dist/core/extensions/builtin/compaction/summarization-turn-order.js.map +1 -0
- package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/index.js +31 -8
- package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/config-reload/routine-settings.js +11 -12
- package/dist/core/extensions/builtin/config-reload/routine-settings.js.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.d.ts +28 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js +278 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts +38 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.js +75 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.d.ts +24 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.js +64 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.d.ts +139 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.js +362 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/errors.d.ts +19 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/errors.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/errors.js +122 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/errors.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts +30 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js +117 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/failover.d.ts +39 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/failover.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/failover.js +155 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/failover.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts +89 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js +147 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.d.ts +20 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.js +99 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts +25 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.js +67 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/index.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts +27 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +242 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts +73 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js +330 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.d.ts +94 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.js +248 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts +35 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js +186 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.d.ts +11 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.js +17 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.d.ts +104 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.js +301 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts +26 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +587 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts +43 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js +196 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js.map +1 -0
- package/dist/core/extensions/builtin/goal/channel-state-subscriptions.d.ts +9 -0
- package/dist/core/extensions/builtin/goal/channel-state-subscriptions.d.ts.map +1 -0
- package/dist/core/extensions/builtin/goal/channel-state-subscriptions.js +18 -0
- package/dist/core/extensions/builtin/goal/channel-state-subscriptions.js.map +1 -0
- package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/monitor-continuation.js +10 -9
- package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
- package/dist/core/extensions/builtin/goal/terminal-provider-error.d.ts.map +1 -1
- package/dist/core/extensions/builtin/goal/terminal-provider-error.js +16 -0
- package/dist/core/extensions/builtin/goal/terminal-provider-error.js.map +1 -1
- package/dist/core/extensions/builtin/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/index.js +9 -2
- package/dist/core/extensions/builtin/index.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/detectors.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/detectors.js +21 -3
- package/dist/core/extensions/builtin/loop-guard/detectors.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/escalation.d.ts +24 -0
- package/dist/core/extensions/builtin/loop-guard/escalation.d.ts.map +1 -0
- package/dist/core/extensions/builtin/loop-guard/escalation.js +77 -0
- package/dist/core/extensions/builtin/loop-guard/escalation.js.map +1 -0
- package/dist/core/extensions/builtin/loop-guard/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/index.js +98 -3
- package/dist/core/extensions/builtin/loop-guard/index.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/notice.d.ts +9 -0
- package/dist/core/extensions/builtin/loop-guard/notice.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/notice.js +17 -0
- package/dist/core/extensions/builtin/loop-guard/notice.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/policy.d.ts +4 -0
- package/dist/core/extensions/builtin/loop-guard/policy.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/policy.js +4 -0
- package/dist/core/extensions/builtin/loop-guard/policy.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/renderer.d.ts +2 -0
- package/dist/core/extensions/builtin/loop-guard/renderer.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/renderer.js +11 -0
- package/dist/core/extensions/builtin/loop-guard/renderer.js.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/tracker.d.ts +1 -1
- package/dist/core/extensions/builtin/loop-guard/tracker.d.ts.map +1 -1
- package/dist/core/extensions/builtin/loop-guard/tracker.js +3 -1
- package/dist/core/extensions/builtin/loop-guard/tracker.js.map +1 -1
- package/dist/core/extensions/builtin/mcp/process-tree.js +6 -1
- package/dist/core/extensions/builtin/mcp/process-tree.js.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.d.ts +6 -0
- package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
- package/dist/core/extensions/builtin/monitor-state-event.js +10 -0
- package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/glm-5-3.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/glm-5-3.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/glm-5-3.js +14 -0
- package/dist/core/extensions/builtin/prompt-preset/glm-5-3.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.d.ts +3 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.d.ts.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js +91 -0
- package/dist/core/extensions/builtin/prompt-preset/grok-4.6.js.map +1 -0
- package/dist/core/extensions/builtin/prompt-preset/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/index.js +22 -2
- package/dist/core/extensions/builtin/prompt-preset/index.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/presets.js +25 -0
- package/dist/core/extensions/builtin/prompt-preset/presets.js.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.d.ts.map +1 -1
- package/dist/core/extensions/builtin/prompt-preset/settings.js +2 -0
- package/dist/core/extensions/builtin/prompt-preset/settings.js.map +1 -1
- package/dist/core/extensions/builtin/reasoning/index.d.ts +3 -0
- package/dist/core/extensions/builtin/reasoning/index.d.ts.map +1 -0
- package/dist/core/extensions/builtin/reasoning/index.js +211 -0
- package/dist/core/extensions/builtin/reasoning/index.js.map +1 -0
- package/dist/core/extensions/builtin/service-tier.d.ts +44 -1
- package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
- package/dist/core/extensions/builtin/service-tier.js +176 -24
- package/dist/core/extensions/builtin/service-tier.js.map +1 -1
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +4 -4
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +12 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts +24 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/lockfile-policy.d.ts +15 -0
- package/dist/core/lockfile-policy.d.ts.map +1 -0
- package/dist/core/lockfile-policy.js +15 -0
- package/dist/core/lockfile-policy.js.map +1 -0
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +1 -0
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +34 -7
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +116 -28
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/model-runtime.d.ts +7 -2
- package/dist/core/model-runtime.d.ts.map +1 -1
- package/dist/core/model-runtime.js +19 -5
- package/dist/core/model-runtime.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +5 -0
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +8 -9
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/provider-api-key-auth.d.ts.map +1 -1
- package/dist/core/provider-api-key-auth.js +45 -2
- package/dist/core/provider-api-key-auth.js.map +1 -1
- package/dist/core/provider-composer.d.ts +12 -1
- package/dist/core/provider-composer.d.ts.map +1 -1
- package/dist/core/provider-composer.js.map +1 -1
- package/dist/core/provider-display-names.d.ts.map +1 -1
- package/dist/core/provider-display-names.js +2 -0
- package/dist/core/provider-display-names.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts +13 -0
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +15 -0
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/retry-fallback/controller.d.ts +1 -1
- package/dist/core/retry-fallback/controller.d.ts.map +1 -1
- package/dist/core/retry-fallback/controller.js +5 -3
- package/dist/core/retry-fallback/controller.js.map +1 -1
- package/dist/core/sdk.d.ts +5 -3
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +20 -8
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +20 -4
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +50 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +258 -15
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.js +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/thinking-levels.d.ts +22 -0
- package/dist/core/thinking-levels.d.ts.map +1 -1
- package/dist/core/thinking-levels.js +29 -0
- package/dist/core/thinking-levels.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +2 -0
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +2 -0
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/find.js +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.js +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -0
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js +2 -0
- package/dist/core/tools/write.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +18 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/model-favorites.d.ts +2 -1
- package/dist/modes/interactive/components/model-favorites.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-favorites.js +72 -6
- package/dist/modes/interactive/components/model-favorites.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +5 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +10 -6
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +10 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +22 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/editor-paste-transfer.d.ts +10 -0
- package/dist/modes/interactive/editor-paste-transfer.d.ts.map +1 -1
- package/dist/modes/interactive/editor-paste-transfer.js +16 -0
- package/dist/modes/interactive/editor-paste-transfer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +14 -4
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +111 -52
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +36 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +2 -0
- package/dist/modes/interactive/theme/grok-day.json +2 -0
- package/dist/modes/interactive/theme/grok-night.json +2 -0
- package/dist/modes/interactive/theme/light.json +2 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +23 -10
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +9 -1
- package/dist/modes/interactive/theme/theme.d.ts +5 -3
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +12 -2
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +4 -1
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +5 -2
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/connection-handler.d.ts +10 -0
- package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
- package/dist/modes/rpc/connection-handler.js +119 -46
- package/dist/modes/rpc/connection-handler.js.map +1 -1
- package/dist/modes/rpc/jsonl.d.ts +6 -1
- package/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/dist/modes/rpc/jsonl.js +47 -14
- package/dist/modes/rpc/jsonl.js.map +1 -1
- package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
- package/dist/modes/rpc/multi-session-host.js +29 -7
- package/dist/modes/rpc/multi-session-host.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +26 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +25 -2
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-command-invocation.d.ts +5 -0
- package/dist/modes/rpc/rpc-command-invocation.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-command-invocation.js +2 -0
- package/dist/modes/rpc/rpc-command-invocation.js.map +1 -0
- package/dist/modes/rpc/rpc-command-surface.d.ts +49 -0
- package/dist/modes/rpc/rpc-command-surface.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-command-surface.js +40 -0
- package/dist/modes/rpc/rpc-command-surface.js.map +1 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts +4 -0
- package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-input-validation.js +20 -0
- package/dist/modes/rpc/rpc-input-validation.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +12 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +72 -12
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-command-router.d.ts.map +1 -1
- package/dist/modes/rpc/session-command-router.js +2 -14
- package/dist/modes/rpc/session-command-router.js.map +1 -1
- package/dist/modes/rpc/session-event-writer.d.ts +29 -10
- package/dist/modes/rpc/session-event-writer.d.ts.map +1 -1
- package/dist/modes/rpc/session-event-writer.js +229 -50
- package/dist/modes/rpc/session-event-writer.js.map +1 -1
- package/dist/server/create-harness.d.ts.map +1 -1
- package/dist/server/create-harness.js +2 -0
- package/dist/server/create-harness.js.map +1 -1
- package/dist/utils/shell.d.ts +27 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +79 -13
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +10 -1
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +14 -19
- package/dist/utils/tools-manager.js.map +1 -1
- package/docs/compaction.md +3 -3
- package/docs/custom-provider.md +1 -1
- package/docs/environment-variables.md +8 -2
- package/docs/extensions.md +4 -0
- package/docs/json.md +6 -4
- package/docs/keybindings.md +8 -2
- package/docs/providers.md +56 -0
- package/docs/rpc.md +206 -12
- package/docs/sdk.md +9 -0
- package/docs/settings.md +93 -1
- package/docs/skills.md +14 -3
- package/docs/terminal-setup.md +23 -2
- package/docs/themes.md +24 -3
- package/docs/tui.md +2 -2
- package/docs/usage.md +24 -4
- package/examples/extensions/subagent/agents.ts +40 -9
- package/examples/extensions/subagent/index.ts +26 -3
- package/node_modules/@bufbuild/protobuf/README.md +44 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/clone.d.ts +6 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/clone.js +66 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/boot.d.ts +1 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/boot.js +18 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/enum.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/enum.js +31 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/extension.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/extension.js +28 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/file.d.ts +1 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/file.js +18 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/index.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/index.js +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/message.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/message.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/service.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/service.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/symbols.d.ts +140 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/symbols.js +43 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/types.d.ts +79 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/types.js +15 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/boot.d.ts +63 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/boot.js +105 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/embed.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/embed.js +244 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/enum.d.ts +29 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/enum.js +58 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/extension.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/extension.js +28 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/file.d.ts +7 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/file.js +35 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/index.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/index.js +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/message.d.ts +15 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/message.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/restore-json-names.d.ts +5 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/restore-json-names.js +29 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/scalar.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/scalar.js +67 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/service.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/service.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/symbols.d.ts +145 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/symbols.js +74 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/types.d.ts +85 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/types.js +15 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/create.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/create.js +259 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/descriptors.d.ts +634 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/descriptors.js +53 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/equals.d.ts +46 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/equals.js +209 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/extensions.d.ts +60 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/extensions.js +168 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/fields.d.ts +23 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/fields.js +45 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/from-binary.d.ts +48 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/from-binary.js +247 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/from-json.d.ts +73 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/from-json.js +739 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/index.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/index.js +55 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/is-message.d.ts +7 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/is-message.js +33 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/json-value.d.ts +16 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/json-value.js +15 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/merge.d.ts +13 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/merge.js +70 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/package.json +4 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/proto-int64.d.ts +98 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/proto-int64.js +131 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/error.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/error.js +36 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/guard.d.ts +20 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/guard.js +78 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/index.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/index.js +41 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/names.d.ts +30 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/names.js +115 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/nested-types.d.ts +35 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/nested-types.js +110 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/path.d.ts +107 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/path.js +376 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-check.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-check.js +268 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-types.d.ts +217 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-types.js +16 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect.js +546 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/scalar.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/scalar.js +129 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/unsafe.d.ts +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/unsafe.js +149 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/registry.d.ts +98 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/registry.js +945 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/to-binary.d.ts +24 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/to-binary.js +199 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/to-json.d.ts +67 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/to-json.js +391 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/from-text.d.ts +35 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/from-text.js +684 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/index.d.ts +4 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/index.js +21 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/is-group-like.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/is-group-like.js +47 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/reader.d.ts +91 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/reader.js +481 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/to-text.d.ts +34 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/to-text.js +358 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/writer.d.ts +76 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/writer.js +220 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/types.d.ts +135 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/types.js +15 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/unknown-enum.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/unknown-enum.js +26 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/base64-encoding.d.ts +23 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/base64-encoding.js +156 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/binary-encoding.d.ts +268 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/binary-encoding.js +530 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/index.d.ts +5 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/index.js +34 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/size-delimited.d.ts +69 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/size-delimited.js +191 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-encoding.d.ts +28 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-encoding.js +61 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-format.d.ts +13 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-format.js +199 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/varint.d.ts +70 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/varint.js +322 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/any.d.ts +38 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/any.js +75 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/duration.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/duration.js +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/any_pb.d.ts +238 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/any_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/api_pb.d.ts +537 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/api_pb.js +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +490 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/compiler/plugin_pb.js +68 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/cpp_features_pb.d.ts +91 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/cpp_features_pb.js +60 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/descriptor_pb.d.ts +4143 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/descriptor_pb.js +892 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/duration_pb.d.ts +161 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/duration_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/empty_pb.d.ts +39 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/empty_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/field_mask_pb.d.ts +424 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/field_mask_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/go_features_pb.d.ts +189 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/go_features_pb.js +126 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/java_features_pb.d.ts +194 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/java_features_pb.js +106 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/source_context_pb.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/source_context_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/struct_pb.d.ts +195 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/struct_pb.js +59 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/timestamp_pb.d.ts +222 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/timestamp_pb.js +27 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/type_pb.d.ts +722 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/type_pb.js +242 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/wrappers_pb.d.ts +330 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/wrappers_pb.js +67 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/index.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/index.js +48 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/timestamp.d.ts +21 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/timestamp.js +57 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/wrappers.d.ts +25 -0
- package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/wrappers.js +66 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/clone.d.ts +6 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/clone.js +63 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/boot.d.ts +1 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/boot.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/enum.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/enum.js +26 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/extension.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/extension.js +25 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/file.d.ts +1 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/file.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/index.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/index.js +23 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/message.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/message.js +21 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/service.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/service.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/symbols.d.ts +140 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/symbols.js +40 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/types.d.ts +79 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/types.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/boot.d.ts +63 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/boot.js +101 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/embed.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/embed.js +239 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/enum.d.ts +29 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/enum.js +53 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/extension.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/extension.js +25 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/file.d.ts +7 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/file.js +32 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/index.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/index.js +23 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.d.ts +15 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.js +21 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/restore-json-names.d.ts +5 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/restore-json-names.js +26 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/scalar.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/scalar.js +63 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/service.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/service.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/symbols.d.ts +145 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/symbols.js +71 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/types.d.ts +85 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/types.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/create.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/create.js +256 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/descriptors.d.ts +634 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/descriptors.js +50 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/equals.d.ts +46 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/equals.js +206 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/extensions.d.ts +60 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/extensions.js +159 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/fields.d.ts +23 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/fields.js +41 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/from-binary.d.ts +48 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/from-binary.js +241 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/from-json.d.ts +73 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/from-json.js +731 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/index.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/index.js +29 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/is-message.d.ts +7 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/is-message.js +30 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/json-value.d.ts +16 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/json-value.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/merge.d.ts +13 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/merge.js +67 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/package.json +4 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/proto-int64.d.ts +98 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/proto-int64.js +128 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/error.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/error.js +31 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/guard.d.ts +20 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/guard.js +71 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/index.d.ts +8 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/index.js +21 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/names.d.ts +30 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/names.js +109 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/nested-types.d.ts +35 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/nested-types.js +105 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/path.d.ts +107 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/path.js +369 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-check.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-check.js +262 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-types.d.ts +217 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-types.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect.js +541 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/scalar.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/scalar.js +124 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/unsafe.d.ts +39 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/reflect/unsafe.js +140 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/registry.d.ts +98 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/registry.js +939 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/to-binary.d.ts +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/to-binary.js +195 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/to-json.d.ts +67 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/to-json.js +386 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/from-text.d.ts +35 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/from-text.js +680 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/index.d.ts +4 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/index.js +15 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/is-group-like.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/is-group-like.js +44 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/reader.d.ts +91 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/reader.js +477 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/to-text.d.ts +34 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/to-text.js +355 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/writer.d.ts +76 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/writer.js +214 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/types.d.ts +135 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/types.js +14 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/unknown-enum.d.ts +10 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/unknown-enum.js +23 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/base64-encoding.d.ts +23 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/base64-encoding.js +152 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/binary-encoding.d.ts +268 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/binary-encoding.js +525 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/index.d.ts +5 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/index.js +18 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/size-delimited.d.ts +69 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/size-delimited.js +186 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-encoding.d.ts +28 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-encoding.js +57 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-format.d.ts +13 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-format.js +195 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/varint.d.ts +70 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wire/varint.js +313 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/any.d.ts +38 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/any.js +69 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/duration.d.ts +9 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/duration.js +35 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/any_pb.d.ts +238 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/any_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/api_pb.d.ts +537 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/api_pb.js +36 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +490 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/compiler/plugin_pb.js +65 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/cpp_features_pb.d.ts +91 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/cpp_features_pb.js +57 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.d.ts +4143 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.js +888 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/duration_pb.d.ts +161 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/duration_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/empty_pb.d.ts +39 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/empty_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/field_mask_pb.d.ts +424 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/field_mask_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/go_features_pb.d.ts +189 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/go_features_pb.js +123 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/java_features_pb.d.ts +194 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/java_features_pb.js +103 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/source_context_pb.d.ts +43 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/source_context_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/struct_pb.d.ts +195 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/struct_pb.js +56 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/timestamp_pb.d.ts +222 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/timestamp_pb.js +24 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/type_pb.d.ts +722 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/type_pb.js +239 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/wrappers_pb.d.ts +330 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/wrappers_pb.js +64 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/index.d.ts +19 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/index.js +32 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/timestamp.d.ts +21 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/timestamp.js +50 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/wrappers.d.ts +25 -0
- package/node_modules/@bufbuild/protobuf/dist/esm/wkt/wrappers.js +61 -0
- package/node_modules/@bufbuild/protobuf/package.json +147 -0
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +28 -0
- package/node_modules/@code-yeongyu/senpi-codemode/README.md +5 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/index.ts +14 -1
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/call-capture.ts +64 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-handler.ts +45 -61
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +8 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-execution-event.ts +201 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool-options.ts +2 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/eval-tool.ts +22 -0
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +43 -6
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +4 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +59 -17
- package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +14 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +14 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts +25 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +77 -12
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.d.ts +43 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.js +67 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/events.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts +4 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js +127 -88
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/codec.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.d.ts +5 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.js +9 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/errors.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.d.ts +12 -3
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.js +88 -45
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/repo.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.js +28 -19
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/storage.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/jsonl/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/memory.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts +6 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js +7 -2
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/session.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/state.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.js +19 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/testing/conformance.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts +18 -5
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/proxy.d.ts +4 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/proxy.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/proxy.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/search/index.d.ts +21 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/index.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/index.js +2 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/index.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.d.ts +29 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.js +103 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/search/scanning.js.map +1 -0
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +12 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/README.md +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +40 -14
- package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +16 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.d.ts +69 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.js +159 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cloudflare-gateway-binding.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js +125 -5
- package/node_modules/@earendil-works/pi-ai/dist/api/constrained-sampling.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.d.ts +17 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.js +14 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.d.ts +13 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.js +33 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.d.ts +79 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.js +360 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts +16797 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js +3828 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.d.ts +55 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.js +76 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts +116 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.js +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +191 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +3316 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.d.ts +29 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.js +33 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js +13 -9
- package/node_modules/@earendil-works/pi-ai/dist/api/google-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +7 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js +208 -42
- package/node_modules/@earendil-works/pi-ai/dist/api/mistral-conversations.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +13 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +10 -7
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js +31 -7
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses-shared.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +8 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.d.ts +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.js +234 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/cursor.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js +6 -3
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/github-copilot.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/load.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js +44 -9
- package/node_modules/@earendil-works/pi-ai/dist/auth/resolve.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/bun-oauth.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/compat.js +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.d.ts +11 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.js +11 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +3 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +6 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/all.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +65 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/alibaba-token-plan.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/deepseek.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/kimi-coding.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/nvidia.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/qwen-token-plan.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai-coding-cn.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/zai.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +17 -4
- package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.d.ts +57 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.js +57 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts +10 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js +22 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/pi-user-agent.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js +3 -2
- package/node_modules/@earendil-works/pi-ai/dist/utils/prompt-cache-ttl.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/validation.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/validation.js +34 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/validation.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +3 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/README.md +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.d.ts +26 -0
- package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js +121 -0
- package/node_modules/@earendil-works/pi-tui/dist/alt-screen-search.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +21 -0
- package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.d.ts +6 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js +23 -7
- package/node_modules/@earendil-works/pi-tui/dist/components/scroll-view.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts +9 -0
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js +82 -0
- package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js.map +1 -0
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts +4 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js +7 -1
- package/node_modules/@earendil-works/pi-tui/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts +30 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js +24 -0
- package/node_modules/@earendil-works/pi-tui/dist/keybindings.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/layout.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/layout.js +9 -2
- package/node_modules/@earendil-works/pi-tui/dist/layout.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts +8 -2
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js +6 -2
- package/node_modules/@earendil-works/pi-tui/dist/stdin-buffer.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +6 -0
- package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js +18 -1
- package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts +21 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js +243 -5
- package/node_modules/@earendil-works/pi-tui/dist/tui-alt-screen.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts +2 -0
- package/node_modules/@earendil-works/pi-tui/dist/tui.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-tui/dist/tui.js +33 -8
- package/node_modules/@earendil-works/pi-tui/dist/tui.js.map +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +11 -8
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.d.ts +0 -19
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.d.ts.map +0 -1
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.js +0 -41
- package/node_modules/@earendil-works/pi-agent-core/dist/harness/session/search.js.map +0 -1
|
@@ -0,0 +1,3316 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cursor agent protocol (`agent.v1.AgentService/Run`) over HTTP/2 Connect.
|
|
3
|
+
*
|
|
4
|
+
* Ported from upstream oh-my-pi's Cursor provider and adapted to senpi's
|
|
5
|
+
* provider-neutral API architecture. The protocol is server-driven: the
|
|
6
|
+
* client opens one `Run` stream per assistant turn, the server streams
|
|
7
|
+
* interaction updates (text/thinking/tool-call deltas) and, mid-turn, sends
|
|
8
|
+
* `ExecServerMessage` frames asking the CLIENT to execute a tool and blocks
|
|
9
|
+
* until the answer arrives on the same stream. Tool execution is therefore
|
|
10
|
+
* bridged through injected {@link CursorExecHandlers}; each bridged call is
|
|
11
|
+
* synthesized into the assistant message as an already-resolved `toolCall`
|
|
12
|
+
* block (marked {@link kCursorExecResolved} so the agent loop never re-runs
|
|
13
|
+
* it) and paired with a `ToolResultMessage` delivered via `onToolResult`.
|
|
14
|
+
*
|
|
15
|
+
* NOTE: This module is Node-only (node:http2) and must only be reached
|
|
16
|
+
* through the lazy wrapper (`cursor-agent.lazy.ts`).
|
|
17
|
+
*/
|
|
18
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
19
|
+
import * as http2 from "node:http2";
|
|
20
|
+
import { create, fromBinary, fromJson, toBinary, toJson } from "@bufbuild/protobuf";
|
|
21
|
+
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
22
|
+
import { calculateCost } from "../models.js";
|
|
23
|
+
import { clearStreamingPartialJson, kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingEnvelopeId, kStreamingLastParseLen, kStreamingPartialJson, } from "../utils/block-symbols.js";
|
|
24
|
+
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
25
|
+
import { providerHeadersToRecord } from "../utils/headers.js";
|
|
26
|
+
import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse.js";
|
|
27
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
|
|
28
|
+
import { deterministicUuid } from "./cursor-agent/deterministic-id.js";
|
|
29
|
+
import { armExecHeartbeat } from "./cursor-agent/exec-lifecycle.js";
|
|
30
|
+
import { buildMcpStateResult, buildNeutralHookResult, buildPiBashError, buildPiBashResult, buildPiEditError, buildPiEditRejected, buildPiEditResult, buildPiFindError, buildPiFindResult, buildPiGrepError, buildPiGrepResult, buildPiLsError, buildPiLsResult, buildPiReadError, buildPiReadResult, buildPiWriteError, buildPiWriteRejected, buildPiWriteResult, } from "./cursor-agent/exec-modern.js";
|
|
31
|
+
import { AgentClientMessageSchema, AgentConversationTurnStructureSchema, AgentRunRequestSchema, AgentServerMessageSchema, AgentStoreConflictErrorSchema, AgentStoreConflictResultSchema, AssistantMessageSchema, BackgroundShellSpawnResultSchema, CanvasDiagnosticsErrorSchema, CanvasDiagnosticsResultSchema, ClientHeartbeatSchema, ComputerUseErrorSchema, ComputerUseResultSchema, ConversationActionSchema, ConversationSearchErrorSchema, ConversationSearchResultSchema, ConversationStateStructureSchema, ConversationStepSchema, ConversationTurnStructureSchema, DeleteErrorSchema, DeleteRejectedSchema, DeleteResultSchema, DeleteSuccessSchema, DiagnosticsErrorSchema, DiagnosticsRejectedSchema, DiagnosticsResultSchema, DiagnosticsSuccessSchema, ExecClientControlMessageSchema, ExecClientHeartbeatSchema, ExecClientMessageSchema, ExecClientStreamCloseSchema, ExecClientThrowSchema, FetchErrorSchema, FetchResultSchema, ForceBackgroundShellResultSchema, ForceBackgroundShellStatus, ForceBackgroundSubagentResultSchema, ForceBackgroundSubagentStatus, GetBlobResultSchema, GetUsableModelsRequestSchema, GetUsableModelsResponseSchema, GrepContentMatchSchema, GrepContentResultSchema, GrepCountResultSchema, GrepErrorSchema, GrepFileCountSchema, GrepFileMatchSchema, GrepFilesResultSchema, GrepResultSchema, GrepSuccessSchema, GrepUnionResultSchema, KvClientMessageSchema, ListMcpResourcesExecResultSchema, ListMcpResourcesSuccessSchema, LsDirectoryTreeNode_FileSchema, LsDirectoryTreeNodeSchema, LsErrorSchema, LsRejectedSchema, LsResultSchema, LsSuccessSchema, McpAllowlistPrecheckResultSchema, McpApprovedSchema, McpArgsSchema, McpErrorSchema, McpImageContentSchema, McpRejectedSchema, McpResultSchema, McpSuccessSchema, McpTextContentSchema, McpToolCallSchema, McpToolDefinitionSchema, McpToolErrorSchema, McpToolNotFoundSchema, McpToolResultContentItemSchema, McpToolResultSchema, ModelDetailsSchema, ReadErrorSchema, ReadMcpResourceExecResultSchema, ReadMcpResourceNotFoundSchema, ReadRejectedSchema, ReadResultSchema, ReadSuccessSchema, RecordScreenFailureSchema, RecordScreenResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, RequestedModelSchema, ResumeActionSchema, SelectedContextSchema, SelectedImageSchema, SetBlobResultSchema, ShellAllowlistPrecheckResultSchema, ShellFailureSchema, ShellRejectedSchema, ShellResultSchema, ShellStreamExitSchema, ShellStreamSchema, ShellStreamStartSchema, ShellStreamStderrSchema, ShellStreamStdoutSchema, ShellSuccessSchema, SmartModeClassifierErrorSchema, SmartModeClassifierResultSchema, SubagentAwaitNotFoundSchema, SubagentAwaitResultSchema, SubagentErrorSchema, SubagentResultSchema, ToolCallSchema, UserMessageActionSchema, UserMessageSchema, WebFetchAllowlistPrecheckResultSchema, WriteErrorSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, WriteSuccessSchema, } from "./cursor-agent/gen/agent_pb.js";
|
|
32
|
+
import { composeShellCommand, omitUndefinedArgs, piLimit, piLsPath, piReadArgs, piTimeout, } from "./cursor-agent/pi-args.js";
|
|
33
|
+
export const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
34
|
+
export const CURSOR_CLIENT_VERSION = "cli-2026.07.23-e383d2b";
|
|
35
|
+
const EXEC_HEARTBEAT_INTERVAL_MS = 3000;
|
|
36
|
+
/**
|
|
37
|
+
* HTTP/1 connection-specific headers that HTTP/2 forbids. Node's
|
|
38
|
+
* `http2.request()` throws on these rather than dropping them.
|
|
39
|
+
*/
|
|
40
|
+
const HTTP2_FORBIDDEN_HEADERS = new Set([
|
|
41
|
+
"connection",
|
|
42
|
+
"keep-alive",
|
|
43
|
+
"proxy-connection",
|
|
44
|
+
"transfer-encoding",
|
|
45
|
+
"upgrade",
|
|
46
|
+
"http2-settings",
|
|
47
|
+
]);
|
|
48
|
+
/**
|
|
49
|
+
* Header names the Cursor request sets for itself. A caller copy in ANY
|
|
50
|
+
* casing has to go: the spread below adds the fixed lower-case name
|
|
51
|
+
* regardless, and two spellings of one field are a duplicate (node throws
|
|
52
|
+
* `ERR_HTTP2_HEADER_SINGLE_VALUE`) rather than an override.
|
|
53
|
+
*/
|
|
54
|
+
const CURSOR_RESERVED_HEADERS = new Set([
|
|
55
|
+
"content-type",
|
|
56
|
+
"connect-protocol-version",
|
|
57
|
+
"te",
|
|
58
|
+
"authorization",
|
|
59
|
+
"x-ghost-mode",
|
|
60
|
+
"x-cursor-client-version",
|
|
61
|
+
"x-cursor-client-type",
|
|
62
|
+
"x-request-id",
|
|
63
|
+
// Transport-owned: a plain `host` header suppresses the `:authority` node
|
|
64
|
+
// derives from the URL, silently retargeting the request.
|
|
65
|
+
"host",
|
|
66
|
+
// The Connect body is streamed after the headers (initial frame,
|
|
67
|
+
// heartbeats, tool responses), so no caller-supplied length can describe it.
|
|
68
|
+
"content-length",
|
|
69
|
+
]);
|
|
70
|
+
/** Reduce caller-supplied headers to what this HTTP/2 request can legally carry. */
|
|
71
|
+
export function sanitizeCursorCallerHeaders(headers) {
|
|
72
|
+
const sanitized = {};
|
|
73
|
+
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
74
|
+
const field = name.toLowerCase();
|
|
75
|
+
if (field.startsWith(":"))
|
|
76
|
+
continue;
|
|
77
|
+
if (HTTP2_FORBIDDEN_HEADERS.has(field))
|
|
78
|
+
continue;
|
|
79
|
+
if (CURSOR_RESERVED_HEADERS.has(field))
|
|
80
|
+
continue;
|
|
81
|
+
sanitized[field] = value;
|
|
82
|
+
}
|
|
83
|
+
return sanitized;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Text for a recognised frame this client answers with its own typed error
|
|
87
|
+
* variant. Phrased as a client capability statement, not a tool failure: the
|
|
88
|
+
* model reads it and should route around the capability, not retry the call.
|
|
89
|
+
*/
|
|
90
|
+
const NOT_IMPLEMENTED_SUFFIX = "not implemented by this client";
|
|
91
|
+
const NOT_IMPLEMENTED = "Not implemented by this client";
|
|
92
|
+
/** Bare gRPC `resource_exhausted` end-streams (also inside a Connect error message). */
|
|
93
|
+
const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/i;
|
|
94
|
+
const conversationStateCache = new Map();
|
|
95
|
+
const conversationBlobStores = new Map();
|
|
96
|
+
/**
|
|
97
|
+
* Base conversation id → rotated wire id. Cursor's backend can pin a
|
|
98
|
+
* per-conversation rejection (bare `resource_exhausted`, zero tokens) to one
|
|
99
|
+
* conversationId forever. On the first such failure the id is rotated once
|
|
100
|
+
* and the cached state migrates, so the retry loop's next attempt starts a
|
|
101
|
+
* fresh conversation. Keyed by the base id so a failed rotation never repeats.
|
|
102
|
+
*/
|
|
103
|
+
const rotatedConversationIds = new Map();
|
|
104
|
+
const CONNECT_END_STREAM_FLAG = 0b00000010;
|
|
105
|
+
function log(type, subtype, data) {
|
|
106
|
+
if (!process.env.DEBUG_CURSOR)
|
|
107
|
+
return;
|
|
108
|
+
const verbose = process.env.DEBUG_CURSOR === "2" || process.env.DEBUG_CURSOR === "verbose";
|
|
109
|
+
let dataStr = "";
|
|
110
|
+
if (verbose && data !== undefined) {
|
|
111
|
+
try {
|
|
112
|
+
dataStr = ` ${JSON.stringify(data, (_key, value) => {
|
|
113
|
+
if (value instanceof Uint8Array)
|
|
114
|
+
return `bytes(${value.length})`;
|
|
115
|
+
if (typeof value === "bigint")
|
|
116
|
+
return value.toString();
|
|
117
|
+
return value;
|
|
118
|
+
})?.slice(0, 500)}`;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
dataStr = " [unserializable]";
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.error(`[CURSOR] ${type}${subtype ? `: ${subtype}` : ""}${dataStr}`);
|
|
125
|
+
}
|
|
126
|
+
export function frameConnectMessage(data, flags = 0) {
|
|
127
|
+
const frame = Buffer.alloc(5 + data.length);
|
|
128
|
+
frame[0] = flags;
|
|
129
|
+
frame.writeUInt32BE(data.length, 1);
|
|
130
|
+
frame.set(data, 5);
|
|
131
|
+
return frame;
|
|
132
|
+
}
|
|
133
|
+
function parseConnectEndStream(data) {
|
|
134
|
+
try {
|
|
135
|
+
const payload = JSON.parse(new TextDecoder().decode(data));
|
|
136
|
+
const error = payload?.error;
|
|
137
|
+
if (error) {
|
|
138
|
+
const code = typeof error.code === "string" ? error.code : "unknown";
|
|
139
|
+
const message = typeof error.message === "string" ? error.message : "Unknown error";
|
|
140
|
+
return new Error(`Connect error ${code}: ${message}`);
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return new Error("Failed to parse Connect end stream");
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Maps an opaque HTTP/2 negotiation failure into an actionable error. The
|
|
150
|
+
* Cursor run RPC is HTTP/2-only (the ALB rejects HTTP/1.1 with 464), so a
|
|
151
|
+
* TLS-intercepting proxy that strips ALPN kills the run with no h1 fallback.
|
|
152
|
+
*/
|
|
153
|
+
export function mapH2TransportError(error, baseUrl) {
|
|
154
|
+
const code = error?.code;
|
|
155
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
156
|
+
if (code === "ERR_HTTP2_ERROR" && /h2 is not supported/i.test(message)) {
|
|
157
|
+
return new Error(`Cursor run transport could not negotiate HTTP/2 with ${baseUrl}: "h2 is not supported". ` +
|
|
158
|
+
"This host serves the run RPC over HTTP/2 only, and the TLS handshake did not negotiate " +
|
|
159
|
+
"h2 via ALPN — typically an ALPN-stripping TLS-intercepting proxy. " +
|
|
160
|
+
"Front the provider with a local HTTP/2 bridge and point the model's baseUrl at it.");
|
|
161
|
+
}
|
|
162
|
+
return error;
|
|
163
|
+
}
|
|
164
|
+
export const stream = (model, context, options) => {
|
|
165
|
+
const stream = new AssistantMessageEventStream();
|
|
166
|
+
(async () => {
|
|
167
|
+
const output = {
|
|
168
|
+
role: "assistant",
|
|
169
|
+
content: [],
|
|
170
|
+
api: "cursor-agent",
|
|
171
|
+
provider: model.provider,
|
|
172
|
+
model: model.id,
|
|
173
|
+
usage: {
|
|
174
|
+
input: 0,
|
|
175
|
+
output: 0,
|
|
176
|
+
cacheRead: 0,
|
|
177
|
+
cacheWrite: 0,
|
|
178
|
+
totalTokens: 0,
|
|
179
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
180
|
+
},
|
|
181
|
+
stopReason: "stop",
|
|
182
|
+
timestamp: Date.now(),
|
|
183
|
+
};
|
|
184
|
+
// Declared outside the `try` because BOTH exits must drain it: an exec
|
|
185
|
+
// handler decoded from the last chunk can still be running when the
|
|
186
|
+
// transport fails, and the error path finalizes the synthesized call
|
|
187
|
+
// just like the success path does.
|
|
188
|
+
const inFlightDispatches = new Set();
|
|
189
|
+
// A dispatch can spawn another, so re-check rather than awaiting one
|
|
190
|
+
// snapshot. The wait is bounded by the abort signal: exec handlers have
|
|
191
|
+
// no cancellation contract, so a hung tool must not hold the terminal
|
|
192
|
+
// event hostage after the user already gave up on the turn.
|
|
193
|
+
let abortSettled;
|
|
194
|
+
const drainInFlightDispatches = async () => {
|
|
195
|
+
const signal = options?.signal;
|
|
196
|
+
while (inFlightDispatches.size > 0) {
|
|
197
|
+
if (signal?.aborted)
|
|
198
|
+
return;
|
|
199
|
+
const settled = Promise.all([...inFlightDispatches]);
|
|
200
|
+
if (!signal) {
|
|
201
|
+
await settled;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
abortSettled ??= new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
|
|
205
|
+
await Promise.race([settled, abortSettled]);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
let h2Client = null;
|
|
209
|
+
let h2Request = null;
|
|
210
|
+
let heartbeatTimer = null;
|
|
211
|
+
let h2Settled = false;
|
|
212
|
+
let sawTurnEnded = false;
|
|
213
|
+
let endStreamError = null;
|
|
214
|
+
// Reachable from the catch: a stream that dies mid-turn must still close
|
|
215
|
+
// and pair the blocks it left open.
|
|
216
|
+
let openBlockState;
|
|
217
|
+
let resolveH2 = () => { };
|
|
218
|
+
let rejectH2 = () => { };
|
|
219
|
+
const h2Completion = new Promise((resolve, reject) => {
|
|
220
|
+
resolveH2 = resolve;
|
|
221
|
+
rejectH2 = reject;
|
|
222
|
+
});
|
|
223
|
+
const settleH2 = (error) => {
|
|
224
|
+
if (h2Settled)
|
|
225
|
+
return;
|
|
226
|
+
h2Settled = true;
|
|
227
|
+
if (error !== undefined) {
|
|
228
|
+
rejectH2(error);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
if (endStreamError) {
|
|
232
|
+
rejectH2(endStreamError);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!sawTurnEnded) {
|
|
236
|
+
rejectH2(new Error("Cursor stream ended before turnEnded"));
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
resolveH2();
|
|
240
|
+
};
|
|
241
|
+
// Hoisted out of the try block: the rotation in the catch path needs
|
|
242
|
+
// both ids, and the catch block cannot see try-scoped consts.
|
|
243
|
+
let baseConversationId;
|
|
244
|
+
let conversationId;
|
|
245
|
+
let usageState;
|
|
246
|
+
try {
|
|
247
|
+
const apiKey = options?.apiKey;
|
|
248
|
+
if (!apiKey) {
|
|
249
|
+
throw new Error("Cursor access token is required; run /login cursor");
|
|
250
|
+
}
|
|
251
|
+
baseConversationId = options?.conversationId ?? options?.sessionId ?? randomUUID();
|
|
252
|
+
conversationId = rotatedConversationIds.get(baseConversationId) ?? baseConversationId;
|
|
253
|
+
const blobStore = conversationBlobStores.get(conversationId) ?? new Map();
|
|
254
|
+
conversationBlobStores.set(conversationId, blobStore);
|
|
255
|
+
const cachedState = conversationStateCache.get(conversationId);
|
|
256
|
+
const { requestBytes, conversationState } = await buildGrpcRequest(model, context, options, {
|
|
257
|
+
conversationId,
|
|
258
|
+
blobStore,
|
|
259
|
+
conversationState: cachedState,
|
|
260
|
+
});
|
|
261
|
+
conversationStateCache.set(conversationId, conversationState);
|
|
262
|
+
const requestContextTools = buildMcpToolDefinitions(context.tools);
|
|
263
|
+
const baseUrl = model.baseUrl || CURSOR_API_URL;
|
|
264
|
+
const requestPath = "/agent.v1.AgentService/Run";
|
|
265
|
+
// Caller headers are additive, and are spread FIRST so the protocol
|
|
266
|
+
// framing, auth, and request id below always win.
|
|
267
|
+
const callerHeaders = sanitizeCursorCallerHeaders(providerHeadersToRecord(options?.headers));
|
|
268
|
+
const requestHeaders = {
|
|
269
|
+
...callerHeaders,
|
|
270
|
+
":method": "POST",
|
|
271
|
+
":path": requestPath,
|
|
272
|
+
"content-type": "application/connect+proto",
|
|
273
|
+
"connect-protocol-version": "1",
|
|
274
|
+
te: "trailers",
|
|
275
|
+
authorization: `Bearer ${apiKey}`,
|
|
276
|
+
"x-ghost-mode": "true",
|
|
277
|
+
"x-cursor-client-version": CURSOR_CLIENT_VERSION,
|
|
278
|
+
"x-cursor-client-type": "cli",
|
|
279
|
+
"x-request-id": randomUUID(),
|
|
280
|
+
};
|
|
281
|
+
h2Client = http2.connect(baseUrl);
|
|
282
|
+
h2Client.on("error", (error) => settleH2(mapH2TransportError(error, baseUrl)));
|
|
283
|
+
h2Request = h2Client.request(requestHeaders);
|
|
284
|
+
stream.push({ type: "start", partial: output });
|
|
285
|
+
let pendingBuffer = Buffer.alloc(0);
|
|
286
|
+
let currentTextBlock = null;
|
|
287
|
+
let currentThinkingBlock = null;
|
|
288
|
+
let currentToolCall = null;
|
|
289
|
+
const resolvedMcpToolCallIds = new Set();
|
|
290
|
+
usageState = { sawTokenDelta: false };
|
|
291
|
+
const state = {
|
|
292
|
+
get currentTextBlock() {
|
|
293
|
+
return currentTextBlock;
|
|
294
|
+
},
|
|
295
|
+
get currentThinkingBlock() {
|
|
296
|
+
return currentThinkingBlock;
|
|
297
|
+
},
|
|
298
|
+
get currentToolCall() {
|
|
299
|
+
return currentToolCall;
|
|
300
|
+
},
|
|
301
|
+
openToolCalls: new Map(),
|
|
302
|
+
resolvedMcpToolCallIds,
|
|
303
|
+
setTextBlock: (b) => {
|
|
304
|
+
currentTextBlock = b;
|
|
305
|
+
},
|
|
306
|
+
setThinkingBlock: (b) => {
|
|
307
|
+
currentThinkingBlock = b;
|
|
308
|
+
},
|
|
309
|
+
setToolCall: (t) => {
|
|
310
|
+
currentToolCall = t;
|
|
311
|
+
},
|
|
312
|
+
onToolResult: options?.onToolResult ?? options?.execHandlers?.onToolResult,
|
|
313
|
+
};
|
|
314
|
+
openBlockState = state;
|
|
315
|
+
const onConversationCheckpoint = (checkpoint) => {
|
|
316
|
+
conversationStateCache.set(conversationId, checkpoint);
|
|
317
|
+
};
|
|
318
|
+
h2Request.on("data", (chunk) => {
|
|
319
|
+
// Steady state drains fully per chunk; alias the fresh h2 chunk
|
|
320
|
+
// instead of copying it through Buffer.concat.
|
|
321
|
+
pendingBuffer = pendingBuffer.length === 0 ? chunk : Buffer.concat([pendingBuffer, chunk]);
|
|
322
|
+
while (pendingBuffer.length >= 5) {
|
|
323
|
+
const flags = pendingBuffer[0];
|
|
324
|
+
const msgLen = pendingBuffer.readUInt32BE(1);
|
|
325
|
+
if (pendingBuffer.length < 5 + msgLen)
|
|
326
|
+
break;
|
|
327
|
+
const messageBytes = pendingBuffer.subarray(5, 5 + msgLen);
|
|
328
|
+
pendingBuffer = pendingBuffer.subarray(5 + msgLen);
|
|
329
|
+
if (flags & CONNECT_END_STREAM_FLAG) {
|
|
330
|
+
const endError = parseConnectEndStream(messageBytes);
|
|
331
|
+
if (endError) {
|
|
332
|
+
endStreamError = endError;
|
|
333
|
+
h2Request?.close();
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
const serverMessage = fromBinary(AgentServerMessageSchema, messageBytes);
|
|
339
|
+
const isTurnEnded = serverMessage.message.case === "interactionUpdate" &&
|
|
340
|
+
serverMessage.message.value.message?.case === "turnEnded";
|
|
341
|
+
// Dispatch is fire-and-forget so the socket keeps draining
|
|
342
|
+
// while a handler runs, but the promise is tracked: `done`
|
|
343
|
+
// must not be pushed while an exec handler is still resolving,
|
|
344
|
+
// or the buffered tool result is delivered after the turn
|
|
345
|
+
// already finalized and the call is left unpaired.
|
|
346
|
+
const dispatch = handleServerMessage(serverMessage, output, stream, state, blobStore, h2Request, options?.execHandlers, state.onToolResult, usageState, requestContextTools, onConversationCheckpoint).catch((error) => {
|
|
347
|
+
log("error", "handleServerMessage", { error: String(error) });
|
|
348
|
+
});
|
|
349
|
+
inFlightDispatches.add(dispatch);
|
|
350
|
+
void dispatch.finally(() => inFlightDispatches.delete(dispatch));
|
|
351
|
+
// Application completion is not protocol success; wait for a
|
|
352
|
+
// clean HTTP/2 end.
|
|
353
|
+
if (isTurnEnded) {
|
|
354
|
+
sawTurnEnded = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
catch (e) {
|
|
358
|
+
log("error", "parseServerMessage", { error: String(e) });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
const sendHeartbeat = () => {
|
|
363
|
+
if (!h2Request || h2Request.closed) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const heartbeatMessage = create(AgentClientMessageSchema, {
|
|
367
|
+
message: { case: "clientHeartbeat", value: create(ClientHeartbeatSchema, {}) },
|
|
368
|
+
});
|
|
369
|
+
const heartbeatBytes = toBinary(AgentClientMessageSchema, heartbeatMessage);
|
|
370
|
+
h2Request.write(frameConnectMessage(heartbeatBytes));
|
|
371
|
+
};
|
|
372
|
+
h2Request.on("trailers", (trailers) => {
|
|
373
|
+
const status = trailers["grpc-status"];
|
|
374
|
+
const msg = trailers["grpc-message"];
|
|
375
|
+
if (status && status !== "0" && !endStreamError) {
|
|
376
|
+
endStreamError = new Error(`gRPC error ${status}: ${decodeURIComponent(String(msg || ""))}`);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
h2Request.on("end", () => {
|
|
380
|
+
settleH2();
|
|
381
|
+
});
|
|
382
|
+
h2Request.on("error", (error) => {
|
|
383
|
+
settleH2(mapH2TransportError(error, baseUrl));
|
|
384
|
+
});
|
|
385
|
+
if (options?.signal) {
|
|
386
|
+
options.signal.addEventListener("abort", () => {
|
|
387
|
+
h2Request?.close();
|
|
388
|
+
settleH2(new Error("Request was aborted"));
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
h2Request.write(frameConnectMessage(requestBytes));
|
|
392
|
+
heartbeatTimer = setInterval(sendHeartbeat, 5000);
|
|
393
|
+
await h2Completion;
|
|
394
|
+
// The transport is done, but a handler decoded from the last chunk
|
|
395
|
+
// may still be running. Pushing `done` now would let the host drain
|
|
396
|
+
// its buffered tool results before such a handler reserved its entry,
|
|
397
|
+
// leaving the call unpaired and stripped from rebuilt transcripts.
|
|
398
|
+
await drainInFlightDispatches();
|
|
399
|
+
endCurrentTextBlock(output, stream, state);
|
|
400
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
401
|
+
flushOpenToolCalls(output, stream, state);
|
|
402
|
+
calculateCost(model, output.usage);
|
|
403
|
+
stream.push({
|
|
404
|
+
type: "done",
|
|
405
|
+
reason: output.stopReason,
|
|
406
|
+
message: output,
|
|
407
|
+
});
|
|
408
|
+
stream.end();
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
// Same reason as the success path: a handler still running would land
|
|
412
|
+
// its real result after the turn finalized and be discarded — even
|
|
413
|
+
// though the tool may already have run side effects. On abort the
|
|
414
|
+
// drain returns immediately.
|
|
415
|
+
await drainInFlightDispatches();
|
|
416
|
+
// A stream that dies mid-turn leaves blocks open. Closing them here
|
|
417
|
+
// settles their live cards and pairs the server-owned calls that
|
|
418
|
+
// nothing else answers — an unpaired call is stripped from every
|
|
419
|
+
// rebuilt transcript.
|
|
420
|
+
if (openBlockState) {
|
|
421
|
+
endCurrentTextBlock(output, stream, openBlockState);
|
|
422
|
+
endCurrentThinkingBlock(output, stream, openBlockState);
|
|
423
|
+
flushOpenToolCalls(output, stream, openBlockState);
|
|
424
|
+
}
|
|
425
|
+
const message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
426
|
+
// A server-side per-conversation rejection surfaces as a bare
|
|
427
|
+
// resource_exhausted with zero tokens — the conversation is poisoned,
|
|
428
|
+
// not the account. Rotate the wire id once and migrate cached state
|
|
429
|
+
// so the caller's retry loop starts a fresh conversation.
|
|
430
|
+
if (conversationId !== undefined &&
|
|
431
|
+
baseConversationId !== undefined &&
|
|
432
|
+
usageState !== undefined &&
|
|
433
|
+
!usageState.sawTokenDelta &&
|
|
434
|
+
RESOURCE_EXHAUSTED_PATTERN.test(message) &&
|
|
435
|
+
!rotatedConversationIds.has(baseConversationId)) {
|
|
436
|
+
const rotated = randomUUID();
|
|
437
|
+
rotatedConversationIds.set(baseConversationId, rotated);
|
|
438
|
+
const cached = conversationStateCache.get(conversationId);
|
|
439
|
+
if (cached)
|
|
440
|
+
conversationStateCache.set(rotated, cached);
|
|
441
|
+
const blobs = conversationBlobStores.get(conversationId);
|
|
442
|
+
if (blobs)
|
|
443
|
+
conversationBlobStores.set(rotated, blobs);
|
|
444
|
+
}
|
|
445
|
+
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
446
|
+
output.errorMessage = message;
|
|
447
|
+
stream.push({ type: "error", reason: output.stopReason, error: output });
|
|
448
|
+
stream.end();
|
|
449
|
+
}
|
|
450
|
+
finally {
|
|
451
|
+
if (heartbeatTimer) {
|
|
452
|
+
clearInterval(heartbeatTimer);
|
|
453
|
+
heartbeatTimer = null;
|
|
454
|
+
}
|
|
455
|
+
h2Request?.close();
|
|
456
|
+
h2Client?.close();
|
|
457
|
+
}
|
|
458
|
+
})();
|
|
459
|
+
return stream;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* `streamSimple` for Cursor: reasoning/thinking is managed server-side per
|
|
463
|
+
* model (there is no client thinking knob on the Run request), so the simple
|
|
464
|
+
* options map through unchanged.
|
|
465
|
+
*/
|
|
466
|
+
export const streamSimple = (model, context, options) => {
|
|
467
|
+
return stream(model, context, options);
|
|
468
|
+
};
|
|
469
|
+
function markCursorExecResolved(block) {
|
|
470
|
+
block[kCursorExecResolved] = true;
|
|
471
|
+
}
|
|
472
|
+
/** Exported for tests: drives one Cursor server message through the stream (exec waits mark the stream busy). */
|
|
473
|
+
export async function handleServerMessage(msg, output, stream, state, blobStore, h2Request, execHandlers, onToolResult, usageState, requestContextTools, onConversationCheckpoint) {
|
|
474
|
+
const msgCase = msg.message.case;
|
|
475
|
+
log("serverMessage", msgCase);
|
|
476
|
+
if (msgCase === "interactionUpdate") {
|
|
477
|
+
processInteractionUpdate(msg.message.value, output, stream, state, usageState);
|
|
478
|
+
}
|
|
479
|
+
else if (msgCase === "kvServerMessage") {
|
|
480
|
+
handleKvServerMessage(msg.message.value, blobStore, h2Request);
|
|
481
|
+
}
|
|
482
|
+
else if (msgCase === "execServerMessage") {
|
|
483
|
+
// The server is waiting on OUR local tool result during this window — no
|
|
484
|
+
// AssistantMessageEvent flows until the handler finishes. Mark the wait
|
|
485
|
+
// as local work so idle watchdogs attribute the silence to the tool run
|
|
486
|
+
// instead of aborting a healthy stream.
|
|
487
|
+
await stream.trackLocalWork(handleExecServerMessage(msg.message.value, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state));
|
|
488
|
+
}
|
|
489
|
+
else if (msgCase === "conversationCheckpointUpdate") {
|
|
490
|
+
onConversationCheckpoint?.(msg.message.value);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function handleKvServerMessage(kvMsg, blobStore, h2Request) {
|
|
494
|
+
const kvCase = kvMsg.message.case;
|
|
495
|
+
if (kvCase === "getBlobArgs") {
|
|
496
|
+
const blobId = kvMsg.message.value.blobId;
|
|
497
|
+
const blobIdKey = Buffer.from(blobId).toString("hex");
|
|
498
|
+
const blobData = blobStore.get(blobIdKey);
|
|
499
|
+
const response = create(KvClientMessageSchema, {
|
|
500
|
+
id: kvMsg.id,
|
|
501
|
+
message: {
|
|
502
|
+
case: "getBlobResult",
|
|
503
|
+
value: create(GetBlobResultSchema, blobData ? { blobData } : {}),
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
const kvClientMessage = create(AgentClientMessageSchema, {
|
|
507
|
+
message: { case: "kvClientMessage", value: response },
|
|
508
|
+
});
|
|
509
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, kvClientMessage)));
|
|
510
|
+
log("kvClient", "getBlobResult", { blobId: blobIdKey.slice(0, 40) });
|
|
511
|
+
}
|
|
512
|
+
else if (kvCase === "setBlobArgs") {
|
|
513
|
+
const { blobId, blobData } = kvMsg.message.value;
|
|
514
|
+
const blobIdKey = Buffer.from(blobId).toString("hex");
|
|
515
|
+
blobStore.set(blobIdKey, blobData);
|
|
516
|
+
const response = create(KvClientMessageSchema, {
|
|
517
|
+
id: kvMsg.id,
|
|
518
|
+
message: {
|
|
519
|
+
case: "setBlobResult",
|
|
520
|
+
value: create(SetBlobResultSchema, {}),
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
const kvClientMessage = create(AgentClientMessageSchema, {
|
|
524
|
+
message: { case: "kvClientMessage", value: response },
|
|
525
|
+
});
|
|
526
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, kvClientMessage)));
|
|
527
|
+
log("kvClient", "setBlobResult", { blobId: blobIdKey.slice(0, 40) });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
function sendShellStreamEvent(h2Request, execMsg, event) {
|
|
531
|
+
sendExecClientMessage(h2Request, execMsg, "shellStream", create(ShellStreamSchema, { event }));
|
|
532
|
+
}
|
|
533
|
+
function sanitizeShellExecResult(execResult) {
|
|
534
|
+
const result = execResult.result;
|
|
535
|
+
if (!result)
|
|
536
|
+
return execResult;
|
|
537
|
+
switch (result.case) {
|
|
538
|
+
case "success":
|
|
539
|
+
case "failure": {
|
|
540
|
+
const value = result.value;
|
|
541
|
+
return {
|
|
542
|
+
...execResult,
|
|
543
|
+
result: {
|
|
544
|
+
case: result.case,
|
|
545
|
+
value: {
|
|
546
|
+
...value,
|
|
547
|
+
stdout: value.stdout ? sanitizeSurrogates(value.stdout) : value.stdout,
|
|
548
|
+
stderr: value.stderr ? sanitizeSurrogates(value.stderr) : value.stderr,
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
default:
|
|
554
|
+
return execResult;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
async function handleShellStreamArgs(args, execMsg, h2Request, execHandlers, onToolResult) {
|
|
558
|
+
const normalizedWorkingDirectory = args.workingDirectory || process.cwd();
|
|
559
|
+
const normalizedArgs = { ...args, workingDirectory: normalizedWorkingDirectory };
|
|
560
|
+
sendShellStreamEvent(h2Request, execMsg, { case: "start", value: create(ShellStreamStartSchema, {}) });
|
|
561
|
+
// Buffer for incomplete ANSI sequences across chunks.
|
|
562
|
+
let stdoutBuffer = "";
|
|
563
|
+
let stderrBuffer = "";
|
|
564
|
+
const incompleteEscapeRegex = /\x1b(|\[|\[\d*|\[\?|\[\?\d*|\]\d*;?)$/;
|
|
565
|
+
const flushStdout = () => {
|
|
566
|
+
if (stdoutBuffer) {
|
|
567
|
+
let safeEnd = stdoutBuffer.length;
|
|
568
|
+
const match = stdoutBuffer.match(incompleteEscapeRegex);
|
|
569
|
+
if (match && match[0].length > 0) {
|
|
570
|
+
safeEnd = stdoutBuffer.length - match[0].length;
|
|
571
|
+
}
|
|
572
|
+
const toSend = stdoutBuffer.slice(0, safeEnd);
|
|
573
|
+
const remaining = stdoutBuffer.slice(safeEnd);
|
|
574
|
+
if (toSend) {
|
|
575
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
576
|
+
case: "stdout",
|
|
577
|
+
value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(toSend) }),
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
stdoutBuffer = remaining;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
const flushStderr = () => {
|
|
584
|
+
if (stderrBuffer) {
|
|
585
|
+
let safeEnd = stderrBuffer.length;
|
|
586
|
+
const match = stderrBuffer.match(incompleteEscapeRegex);
|
|
587
|
+
if (match && match[0].length > 0) {
|
|
588
|
+
safeEnd = stderrBuffer.length - match[0].length;
|
|
589
|
+
}
|
|
590
|
+
const toSend = stderrBuffer.slice(0, safeEnd);
|
|
591
|
+
const remaining = stderrBuffer.slice(safeEnd);
|
|
592
|
+
if (toSend) {
|
|
593
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
594
|
+
case: "stderr",
|
|
595
|
+
value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(toSend) }),
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
stderrBuffer = remaining;
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
let stdoutFlushTimer = null;
|
|
602
|
+
let stderrFlushTimer = null;
|
|
603
|
+
const scheduleStdoutFlush = () => {
|
|
604
|
+
if (!stdoutFlushTimer) {
|
|
605
|
+
stdoutFlushTimer = setTimeout(() => {
|
|
606
|
+
stdoutFlushTimer = null;
|
|
607
|
+
flushStdout();
|
|
608
|
+
}, 100);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
const scheduleStderrFlush = () => {
|
|
612
|
+
if (!stderrFlushTimer) {
|
|
613
|
+
stderrFlushTimer = setTimeout(() => {
|
|
614
|
+
stderrFlushTimer = null;
|
|
615
|
+
flushStderr();
|
|
616
|
+
}, 100);
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
const streamCallbacks = {
|
|
620
|
+
onStdout(data) {
|
|
621
|
+
stdoutBuffer += data;
|
|
622
|
+
if (stdoutBuffer.includes("\n") || stdoutBuffer.length > 4096) {
|
|
623
|
+
if (stdoutFlushTimer) {
|
|
624
|
+
clearTimeout(stdoutFlushTimer);
|
|
625
|
+
stdoutFlushTimer = null;
|
|
626
|
+
}
|
|
627
|
+
flushStdout();
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
scheduleStdoutFlush();
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
onStderr(data) {
|
|
634
|
+
stderrBuffer += data;
|
|
635
|
+
if (stderrBuffer.includes("\n") || stderrBuffer.length > 4096) {
|
|
636
|
+
if (stderrFlushTimer) {
|
|
637
|
+
clearTimeout(stderrFlushTimer);
|
|
638
|
+
stderrFlushTimer = null;
|
|
639
|
+
}
|
|
640
|
+
flushStderr();
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
scheduleStderrFlush();
|
|
644
|
+
}
|
|
645
|
+
},
|
|
646
|
+
};
|
|
647
|
+
// Prefer the streaming handler — it forwards output chunks in real time.
|
|
648
|
+
const streamHandler = execHandlers?.shellStream?.bind(execHandlers);
|
|
649
|
+
const batchHandler = execHandlers?.shell?.bind(execHandlers);
|
|
650
|
+
const handler = streamHandler ? (shellArgs) => streamHandler(shellArgs, streamCallbacks) : batchHandler;
|
|
651
|
+
const { execResult } = await resolveExecHandler(normalizedArgs, handler, onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
|
|
652
|
+
// When using the batch handler (no shellStream), send buffered stdout/stderr
|
|
653
|
+
// after execution completes. With shellStream these were already sent live.
|
|
654
|
+
const sendBufferedOutput = !streamHandler;
|
|
655
|
+
const sanitizedExecResult = sanitizeShellExecResult(execResult);
|
|
656
|
+
if (stdoutFlushTimer)
|
|
657
|
+
clearTimeout(stdoutFlushTimer);
|
|
658
|
+
if (stderrFlushTimer)
|
|
659
|
+
clearTimeout(stderrFlushTimer);
|
|
660
|
+
flushStdout();
|
|
661
|
+
flushStderr();
|
|
662
|
+
sendShellStreamExitFromResult(h2Request, execMsg, sanitizedExecResult, sendBufferedOutput);
|
|
663
|
+
// Cursor can keep the turn pending when it receives only stream deltas.
|
|
664
|
+
// Send the final structured shellResult as completion acknowledgement.
|
|
665
|
+
sendExecClientMessage(h2Request, execMsg, "shellResult", sanitizedExecResult);
|
|
666
|
+
}
|
|
667
|
+
function sendShellStreamExitFromResult(h2Request, execMsg, execResult, sendBufferedOutput) {
|
|
668
|
+
const result = execResult.result;
|
|
669
|
+
switch (result.case) {
|
|
670
|
+
case "success": {
|
|
671
|
+
const value = result.value;
|
|
672
|
+
if (sendBufferedOutput) {
|
|
673
|
+
if (value.stdout) {
|
|
674
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
675
|
+
case: "stdout",
|
|
676
|
+
value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(value.stdout) }),
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (value.stderr) {
|
|
680
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
681
|
+
case: "stderr",
|
|
682
|
+
value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(value.stderr) }),
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
687
|
+
case: "exit",
|
|
688
|
+
value: create(ShellStreamExitSchema, {
|
|
689
|
+
code: value.exitCode,
|
|
690
|
+
cwd: value.workingDirectory,
|
|
691
|
+
aborted: false,
|
|
692
|
+
}),
|
|
693
|
+
});
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
case "failure": {
|
|
697
|
+
const value = result.value;
|
|
698
|
+
if (sendBufferedOutput) {
|
|
699
|
+
if (value.stdout) {
|
|
700
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
701
|
+
case: "stdout",
|
|
702
|
+
value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(value.stdout) }),
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
if (value.stderr) {
|
|
706
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
707
|
+
case: "stderr",
|
|
708
|
+
value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(value.stderr) }),
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
713
|
+
case: "exit",
|
|
714
|
+
value: create(ShellStreamExitSchema, {
|
|
715
|
+
code: value.exitCode,
|
|
716
|
+
cwd: value.workingDirectory,
|
|
717
|
+
aborted: value.aborted,
|
|
718
|
+
abortReason: value.abortReason,
|
|
719
|
+
}),
|
|
720
|
+
});
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
case "rejected": {
|
|
724
|
+
sendShellStreamEvent(h2Request, execMsg, { case: "rejected", value: result.value });
|
|
725
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
726
|
+
case: "exit",
|
|
727
|
+
value: create(ShellStreamExitSchema, {
|
|
728
|
+
code: 1,
|
|
729
|
+
cwd: result.value.workingDirectory,
|
|
730
|
+
aborted: false,
|
|
731
|
+
}),
|
|
732
|
+
});
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
case "timeout": {
|
|
736
|
+
const value = result.value;
|
|
737
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
738
|
+
case: "stderr",
|
|
739
|
+
value: create(ShellStreamStderrSchema, {
|
|
740
|
+
data: `Command timed out after ${value.timeoutMs}ms`,
|
|
741
|
+
}),
|
|
742
|
+
});
|
|
743
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
744
|
+
case: "exit",
|
|
745
|
+
value: create(ShellStreamExitSchema, {
|
|
746
|
+
code: 1,
|
|
747
|
+
cwd: value.workingDirectory,
|
|
748
|
+
aborted: true,
|
|
749
|
+
}),
|
|
750
|
+
});
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
case "permissionDenied": {
|
|
754
|
+
sendShellStreamEvent(h2Request, execMsg, { case: "permissionDenied", value: result.value });
|
|
755
|
+
sendShellStreamEvent(h2Request, execMsg, {
|
|
756
|
+
case: "exit",
|
|
757
|
+
value: create(ShellStreamExitSchema, {
|
|
758
|
+
code: 1,
|
|
759
|
+
cwd: result.value.workingDirectory,
|
|
760
|
+
aborted: false,
|
|
761
|
+
}),
|
|
762
|
+
});
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
default:
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
async function handleExecServerMessage(execMsg, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state) {
|
|
770
|
+
const execCase = execMsg.message.case;
|
|
771
|
+
log("exec", "dispatch", { execCase, execId: execMsg.execId, hasHandlers: !!execHandlers });
|
|
772
|
+
if (!execCase) {
|
|
773
|
+
// A frame carrying a oneof number this build's `agent.proto` does not
|
|
774
|
+
// model at all. Returning silently strands the exec id — the server
|
|
775
|
+
// waits on a reply that never comes.
|
|
776
|
+
log("warn", "unknownExecVariant", { id: execMsg.id, execId: execMsg.execId });
|
|
777
|
+
sendExecClientThrow(h2Request, execMsg, "Unknown exec message variant", "unknown_exec_variant");
|
|
778
|
+
sendExecClientStreamClose(h2Request, execMsg);
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
const stopExecHeartbeat = armCursorExecHeartbeat(h2Request, execMsg);
|
|
782
|
+
try {
|
|
783
|
+
await dispatchExecServerMessage({
|
|
784
|
+
execMsg,
|
|
785
|
+
h2Request,
|
|
786
|
+
execHandlers,
|
|
787
|
+
onToolResult,
|
|
788
|
+
requestContextTools,
|
|
789
|
+
output,
|
|
790
|
+
stream,
|
|
791
|
+
state,
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
catch (error) {
|
|
795
|
+
log("error", "execDispatch", {
|
|
796
|
+
error: error instanceof Error ? error.message : String(error),
|
|
797
|
+
id: execMsg.id,
|
|
798
|
+
execId: execMsg.execId,
|
|
799
|
+
});
|
|
800
|
+
sendExecClientThrow(h2Request, execMsg, "Local exec dispatch failed", "exec_dispatch_failed");
|
|
801
|
+
}
|
|
802
|
+
finally {
|
|
803
|
+
stopExecHeartbeat();
|
|
804
|
+
sendExecClientStreamClose(h2Request, execMsg);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
async function dispatchExecServerMessage(context) {
|
|
808
|
+
const { execMsg, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state } = context;
|
|
809
|
+
const execCase = execMsg.message.case;
|
|
810
|
+
if (!execCase)
|
|
811
|
+
throw new Error("Expected a recognized exec message");
|
|
812
|
+
if (execCase === "requestContextArgs") {
|
|
813
|
+
const requestContext = create(RequestContextSchema, {
|
|
814
|
+
rules: [],
|
|
815
|
+
repositoryInfo: [],
|
|
816
|
+
tools: requestContextTools,
|
|
817
|
+
gitRepos: [],
|
|
818
|
+
projectLayouts: [],
|
|
819
|
+
mcpInstructions: [],
|
|
820
|
+
fileContents: {},
|
|
821
|
+
customSubagents: [],
|
|
822
|
+
});
|
|
823
|
+
const requestContextResult = create(RequestContextResultSchema, {
|
|
824
|
+
result: {
|
|
825
|
+
case: "success",
|
|
826
|
+
value: create(RequestContextSuccessSchema, { requestContext }),
|
|
827
|
+
},
|
|
828
|
+
});
|
|
829
|
+
sendExecClientMessage(h2Request, execMsg, "requestContextResult", requestContextResult);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
switch (execCase) {
|
|
833
|
+
case "readArgs": {
|
|
834
|
+
const args = execMsg.message.value;
|
|
835
|
+
if (!args.toolCallId)
|
|
836
|
+
args.toolCallId = randomUUID();
|
|
837
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "read", {
|
|
838
|
+
path: args.path,
|
|
839
|
+
offset: args.offset,
|
|
840
|
+
limit: args.limit,
|
|
841
|
+
});
|
|
842
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.read?.bind(execHandlers), onToolResult, (toolResult) => buildReadResultFromToolResult(args.path, toolResult, args.offset !== undefined || args.limit !== undefined), (reason) => buildReadRejectedResult(args.path, reason), (error) => buildReadErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "read" });
|
|
843
|
+
sendExecClientMessage(h2Request, execMsg, "readResult", execResult);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
case "lsArgs": {
|
|
847
|
+
const args = execMsg.message.value;
|
|
848
|
+
if (!args.toolCallId)
|
|
849
|
+
args.toolCallId = randomUUID();
|
|
850
|
+
// The bridge maps `ls` onto the local `ls` tool; mirror that here so
|
|
851
|
+
// the synthesized block matches the toolResult's `toolName`.
|
|
852
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "ls", { path: piLsPath(args.path) });
|
|
853
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.ls?.bind(execHandlers), onToolResult, (toolResult) => buildLsResultFromToolResult(args.path, toolResult), (reason) => buildLsRejectedResult(args.path, reason), (error) => buildLsErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "ls" });
|
|
854
|
+
sendExecClientMessage(h2Request, execMsg, "lsResult", execResult);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
case "grepArgs": {
|
|
858
|
+
const args = execMsg.message.value;
|
|
859
|
+
if (!args.toolCallId)
|
|
860
|
+
args.toolCallId = randomUUID();
|
|
861
|
+
// Cursor's model sometimes emits `grepArgs` with an empty `pattern`
|
|
862
|
+
// and a non-empty `glob`, expecting grep to list files matching the
|
|
863
|
+
// glob. Reject that up front with an actionable error.
|
|
864
|
+
const emptyPatternError = emptyGrepPatternRejection(args.pattern, args.glob);
|
|
865
|
+
if (emptyPatternError !== null) {
|
|
866
|
+
sendExecClientMessage(h2Request, execMsg, "grepResult", buildGrepErrorResult(emptyPatternError));
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
// Mirror the coding-agent bridge's arg mapping so live UI (from
|
|
870
|
+
// `tool_execution_start`) and rebuilt transcript display identical args.
|
|
871
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "grep", {
|
|
872
|
+
pattern: args.pattern,
|
|
873
|
+
path: args.path || undefined,
|
|
874
|
+
glob: args.glob || undefined,
|
|
875
|
+
ignoreCase: args.caseInsensitive === true ? true : undefined,
|
|
876
|
+
});
|
|
877
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.grep?.bind(execHandlers), onToolResult, (toolResult) => buildGrepResultFromToolResult(args, toolResult), (reason) => buildGrepErrorResult(reason), (error) => buildGrepErrorResult(error), { toolCallId: args.toolCallId, toolName: "grep" });
|
|
878
|
+
sendExecClientMessage(h2Request, execMsg, "grepResult", execResult);
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
case "writeArgs": {
|
|
882
|
+
const args = execMsg.message.value;
|
|
883
|
+
if (!args.toolCallId)
|
|
884
|
+
args.toolCallId = randomUUID();
|
|
885
|
+
// Match the bridge: prefer `fileText`, fall back to decoded `fileBytes`.
|
|
886
|
+
const content = args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array());
|
|
887
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "write", {
|
|
888
|
+
path: args.path,
|
|
889
|
+
content,
|
|
890
|
+
});
|
|
891
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.write?.bind(execHandlers), onToolResult, (toolResult) => buildWriteResultFromToolResult({
|
|
892
|
+
path: args.path,
|
|
893
|
+
fileText: args.fileText,
|
|
894
|
+
fileBytes: args.fileBytes,
|
|
895
|
+
returnFileContentAfterWrite: args.returnFileContentAfterWrite,
|
|
896
|
+
}, toolResult), (reason) => buildWriteRejectedResult(args.path, reason), (error) => buildWriteErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "write" });
|
|
897
|
+
sendExecClientMessage(h2Request, execMsg, "writeResult", execResult);
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
case "deleteArgs": {
|
|
901
|
+
const args = execMsg.message.value;
|
|
902
|
+
if (!args.toolCallId)
|
|
903
|
+
args.toolCallId = randomUUID();
|
|
904
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "delete", { path: args.path });
|
|
905
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.delete?.bind(execHandlers), onToolResult, (toolResult) => buildDeleteResultFromToolResult(args.path, toolResult), (reason) => buildDeleteRejectedResult(args.path, reason), (error) => buildDeleteErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "delete" });
|
|
906
|
+
sendExecClientMessage(h2Request, execMsg, "deleteResult", execResult);
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
case "shellArgs": {
|
|
910
|
+
const args = execMsg.message.value;
|
|
911
|
+
if (!args.toolCallId)
|
|
912
|
+
args.toolCallId = randomUUID();
|
|
913
|
+
const normalizedArgs = { ...args, workingDirectory: args.workingDirectory || process.cwd() };
|
|
914
|
+
const shellTimeout = args.timeout && args.timeout > 0 ? args.timeout : undefined;
|
|
915
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
|
|
916
|
+
command: composeShellCommand(args.command, args.workingDirectory || undefined),
|
|
917
|
+
timeout: shellTimeout,
|
|
918
|
+
});
|
|
919
|
+
const { execResult } = await resolveExecHandler(normalizedArgs, execHandlers?.shell?.bind(execHandlers), onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
|
|
920
|
+
sendExecClientMessage(h2Request, execMsg, "shellResult", sanitizeShellExecResult(execResult));
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
case "shellStreamArgs": {
|
|
924
|
+
const args = execMsg.message.value;
|
|
925
|
+
if (!args.toolCallId)
|
|
926
|
+
args.toolCallId = randomUUID();
|
|
927
|
+
const shellStreamTimeout = args.timeout && args.timeout > 0 ? args.timeout : undefined;
|
|
928
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
|
|
929
|
+
command: composeShellCommand(args.command, args.workingDirectory || undefined),
|
|
930
|
+
timeout: shellStreamTimeout,
|
|
931
|
+
});
|
|
932
|
+
await handleShellStreamArgs(args, execMsg, h2Request, execHandlers, onToolResult);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
case "backgroundShellSpawnArgs": {
|
|
936
|
+
const args = execMsg.message.value;
|
|
937
|
+
const execResult = create(BackgroundShellSpawnResultSchema, {
|
|
938
|
+
result: {
|
|
939
|
+
case: "rejected",
|
|
940
|
+
value: create(ShellRejectedSchema, {
|
|
941
|
+
command: args.command,
|
|
942
|
+
workingDirectory: args.workingDirectory,
|
|
943
|
+
reason: "Not implemented",
|
|
944
|
+
isReadonly: false,
|
|
945
|
+
}),
|
|
946
|
+
},
|
|
947
|
+
});
|
|
948
|
+
sendExecClientMessage(h2Request, execMsg, "backgroundShellSpawnResult", execResult);
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
case "writeShellStdinArgs": {
|
|
952
|
+
const execResult = create(WriteShellStdinResultSchema, {
|
|
953
|
+
result: {
|
|
954
|
+
case: "error",
|
|
955
|
+
value: create(WriteShellStdinErrorSchema, {
|
|
956
|
+
error: "Not implemented",
|
|
957
|
+
}),
|
|
958
|
+
},
|
|
959
|
+
});
|
|
960
|
+
sendExecClientMessage(h2Request, execMsg, "writeShellStdinResult", execResult);
|
|
961
|
+
return;
|
|
962
|
+
}
|
|
963
|
+
case "fetchArgs": {
|
|
964
|
+
const args = execMsg.message.value;
|
|
965
|
+
const execResult = create(FetchResultSchema, {
|
|
966
|
+
result: {
|
|
967
|
+
case: "error",
|
|
968
|
+
value: create(FetchErrorSchema, {
|
|
969
|
+
url: args.url,
|
|
970
|
+
error: "Not implemented",
|
|
971
|
+
}),
|
|
972
|
+
},
|
|
973
|
+
});
|
|
974
|
+
sendExecClientMessage(h2Request, execMsg, "fetchResult", execResult);
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
case "diagnosticsArgs": {
|
|
978
|
+
const args = execMsg.message.value;
|
|
979
|
+
if (!args.toolCallId)
|
|
980
|
+
args.toolCallId = randomUUID();
|
|
981
|
+
const { execResult } = await resolveExecHandler(args, execHandlers?.diagnostics?.bind(execHandlers), onToolResult, (toolResult) => buildDiagnosticsResultFromToolResult(args.path, toolResult), (reason) => buildDiagnosticsRejectedResult(args.path, reason), (error) => buildDiagnosticsErrorResult(args.path, error),
|
|
982
|
+
// senpi has no local diagnostics tool; the frame is answered with a
|
|
983
|
+
// refusal and no block is synthesized (nothing ran).
|
|
984
|
+
null);
|
|
985
|
+
sendExecClientMessage(h2Request, execMsg, "diagnosticsResult", execResult);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
case "mcpArgs": {
|
|
989
|
+
const args = execMsg.message.value;
|
|
990
|
+
const mcpCall = decodeMcpCall(args);
|
|
991
|
+
// An approval probe, not an invocation: the frame asks whether the
|
|
992
|
+
// call would be permitted. Only a definite allow is approved; without
|
|
993
|
+
// a handler there is nothing to decide with, so it is refused. Either
|
|
994
|
+
// way no block is synthesized — nothing ran.
|
|
995
|
+
if (mcpCall.approvalOnly) {
|
|
996
|
+
const approved = (await execHandlers?.mcpApprovalPreflight?.(mcpCall)) === true;
|
|
997
|
+
sendExecClientMessage(h2Request, execMsg, "mcpResult", create(McpResultSchema, {
|
|
998
|
+
result: approved
|
|
999
|
+
? { case: "approved", value: create(McpApprovedSchema, {}) }
|
|
1000
|
+
: {
|
|
1001
|
+
case: "rejected",
|
|
1002
|
+
value: create(McpRejectedSchema, {
|
|
1003
|
+
reason: `Tool "${mcpCall.toolName || mcpCall.name}" is not approved to run without asking.`,
|
|
1004
|
+
}),
|
|
1005
|
+
},
|
|
1006
|
+
}));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
if (execHandlers?.mcp) {
|
|
1010
|
+
const existingBlock = output.content.find((block) => block.type === "toolCall" && block.id === mcpCall.toolCallId);
|
|
1011
|
+
if (existingBlock) {
|
|
1012
|
+
markCursorExecResolved(existingBlock);
|
|
1013
|
+
}
|
|
1014
|
+
else {
|
|
1015
|
+
synthesizeCursorExecToolCall(output, stream, state, mcpCall.toolCallId, mcpCall.toolName || mcpCall.name, mcpCall.args);
|
|
1016
|
+
state.resolvedMcpToolCallIds.add(mcpCall.toolCallId);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
const { execResult } = await resolveExecHandler(mcpCall, execHandlers?.mcp?.bind(execHandlers), onToolResult, (toolResult) => buildMcpResultFromToolResult(mcpCall, toolResult), (_reason) => buildMcpToolNotFoundResult(mcpCall), (error) => buildMcpErrorResult(error), execHandlers?.mcp ? { toolCallId: mcpCall.toolCallId, toolName: mcpCall.toolName } : null);
|
|
1020
|
+
sendExecClientMessage(h2Request, execMsg, "mcpResult", execResult);
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
case "listMcpResourcesExecArgs": {
|
|
1024
|
+
// This client hosts no MCP resources; the honest answer is an explicit
|
|
1025
|
+
// empty success. An unset-oneof result would read as "the call
|
|
1026
|
+
// produced nothing".
|
|
1027
|
+
const execResult = create(ListMcpResourcesExecResultSchema, {
|
|
1028
|
+
result: {
|
|
1029
|
+
case: "success",
|
|
1030
|
+
value: create(ListMcpResourcesSuccessSchema, { resources: [] }),
|
|
1031
|
+
},
|
|
1032
|
+
});
|
|
1033
|
+
sendExecClientMessage(h2Request, execMsg, "listMcpResourcesExecResult", execResult);
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
case "readMcpResourceExecArgs": {
|
|
1037
|
+
const args = execMsg.message.value;
|
|
1038
|
+
const execResult = create(ReadMcpResourceExecResultSchema, {
|
|
1039
|
+
result: { case: "notFound", value: create(ReadMcpResourceNotFoundSchema, { uri: args.uri }) },
|
|
1040
|
+
});
|
|
1041
|
+
sendExecClientMessage(h2Request, execMsg, "readMcpResourceExecResult", execResult);
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
case "recordScreenArgs": {
|
|
1045
|
+
const execResult = create(RecordScreenResultSchema, {
|
|
1046
|
+
result: { case: "failure", value: create(RecordScreenFailureSchema, { error: NOT_IMPLEMENTED }) },
|
|
1047
|
+
});
|
|
1048
|
+
sendExecClientMessage(h2Request, execMsg, "recordScreenResult", execResult);
|
|
1049
|
+
return;
|
|
1050
|
+
}
|
|
1051
|
+
case "computerUseArgs": {
|
|
1052
|
+
const execResult = create(ComputerUseResultSchema, {
|
|
1053
|
+
result: { case: "error", value: create(ComputerUseErrorSchema, { error: NOT_IMPLEMENTED }) },
|
|
1054
|
+
});
|
|
1055
|
+
sendExecClientMessage(h2Request, execMsg, "computerUseResult", execResult);
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
case "piReadArgs": {
|
|
1059
|
+
const args = execMsg.message.value;
|
|
1060
|
+
const toolCallId = randomUUID();
|
|
1061
|
+
const readArgs = piReadArgs(args.path, args.offset, args.limit);
|
|
1062
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "read", readArgs ?? { path: args.path, offset: args.offset, limit: 0 });
|
|
1063
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piRead?.bind(execHandlers), onToolResult, buildPiReadResult, buildPiReadError, buildPiReadError, { toolCallId, toolName: "read" });
|
|
1064
|
+
sendExecClientMessage(h2Request, execMsg, "piReadResult", execResult);
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
1067
|
+
case "piBashArgs": {
|
|
1068
|
+
const args = execMsg.message.value;
|
|
1069
|
+
const toolCallId = randomUUID();
|
|
1070
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "bash", {
|
|
1071
|
+
command: args.command,
|
|
1072
|
+
timeout: piTimeout(args.timeout),
|
|
1073
|
+
});
|
|
1074
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piBash?.bind(execHandlers), onToolResult, buildPiBashResult, buildPiBashError, buildPiBashError, { toolCallId, toolName: "bash" });
|
|
1075
|
+
sendExecClientMessage(h2Request, execMsg, "piBashResult", execResult);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
case "piEditArgs": {
|
|
1079
|
+
const args = execMsg.message.value;
|
|
1080
|
+
const toolCallId = randomUUID();
|
|
1081
|
+
// `PiEditReplacement[]` maps 1:1 onto the local `edit` tool's
|
|
1082
|
+
// `edits[{oldText,newText}]` shape.
|
|
1083
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "edit", {
|
|
1084
|
+
path: args.path,
|
|
1085
|
+
edits: args.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText })),
|
|
1086
|
+
});
|
|
1087
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piEdit?.bind(execHandlers), onToolResult, buildPiEditResult, buildPiEditRejected, buildPiEditError, { toolCallId, toolName: "edit" });
|
|
1088
|
+
sendExecClientMessage(h2Request, execMsg, "piEditResult", execResult);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
case "piWriteArgs": {
|
|
1092
|
+
const args = execMsg.message.value;
|
|
1093
|
+
const toolCallId = randomUUID();
|
|
1094
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "write", {
|
|
1095
|
+
path: args.path,
|
|
1096
|
+
content: args.content,
|
|
1097
|
+
});
|
|
1098
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piWrite?.bind(execHandlers), onToolResult, buildPiWriteResult, buildPiWriteRejected, buildPiWriteError, { toolCallId, toolName: "write" });
|
|
1099
|
+
sendExecClientMessage(h2Request, execMsg, "piWriteResult", execResult);
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
case "piGrepArgs": {
|
|
1103
|
+
const args = execMsg.message.value;
|
|
1104
|
+
const toolCallId = randomUUID();
|
|
1105
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "grep", {
|
|
1106
|
+
pattern: args.pattern,
|
|
1107
|
+
path: args.path || undefined,
|
|
1108
|
+
glob: args.glob || undefined,
|
|
1109
|
+
ignoreCase: args.ignoreCase === true ? true : undefined,
|
|
1110
|
+
literal: args.literal === true ? true : undefined,
|
|
1111
|
+
context: args.context,
|
|
1112
|
+
limit: piLimit(args.limit),
|
|
1113
|
+
});
|
|
1114
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piGrep?.bind(execHandlers), onToolResult, buildPiGrepResult, buildPiGrepError, buildPiGrepError, { toolCallId, toolName: "grep" });
|
|
1115
|
+
sendExecClientMessage(h2Request, execMsg, "piGrepResult", execResult);
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
case "piFindArgs": {
|
|
1119
|
+
const args = execMsg.message.value;
|
|
1120
|
+
const toolCallId = randomUUID();
|
|
1121
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "find", {
|
|
1122
|
+
pattern: args.pattern,
|
|
1123
|
+
path: args.path || undefined,
|
|
1124
|
+
limit: piLimit(args.limit),
|
|
1125
|
+
});
|
|
1126
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piFind?.bind(execHandlers), onToolResult, buildPiFindResult, buildPiFindError, buildPiFindError, { toolCallId, toolName: "find" });
|
|
1127
|
+
sendExecClientMessage(h2Request, execMsg, "piFindResult", execResult);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
case "piLsArgs": {
|
|
1131
|
+
const args = execMsg.message.value;
|
|
1132
|
+
const toolCallId = randomUUID();
|
|
1133
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "ls", {
|
|
1134
|
+
path: piLsPath(args.path),
|
|
1135
|
+
limit: piLimit(args.limit),
|
|
1136
|
+
});
|
|
1137
|
+
const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piLs?.bind(execHandlers), onToolResult, buildPiLsResult, buildPiLsError, buildPiLsError, { toolCallId, toolName: "ls" });
|
|
1138
|
+
sendExecClientMessage(h2Request, execMsg, "piLsResult", execResult);
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
case "miniSweAgentBashArgs": {
|
|
1142
|
+
// Same `ShellArgs`/`ShellResult` pair as `shellArgs`, under its own
|
|
1143
|
+
// frame number, so the existing shell handler answers it unchanged.
|
|
1144
|
+
const args = execMsg.message.value;
|
|
1145
|
+
if (!args.toolCallId)
|
|
1146
|
+
args.toolCallId = randomUUID();
|
|
1147
|
+
const normalizedArgs = { ...args, workingDirectory: args.workingDirectory || process.cwd() };
|
|
1148
|
+
synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
|
|
1149
|
+
command: composeShellCommand(args.command, args.workingDirectory || undefined),
|
|
1150
|
+
timeout: args.timeout && args.timeout > 0 ? args.timeout : undefined,
|
|
1151
|
+
});
|
|
1152
|
+
const { execResult } = await resolveExecHandler(normalizedArgs, execHandlers?.shell?.bind(execHandlers), onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
|
|
1153
|
+
sendExecClientMessage(h2Request, execMsg, "miniSweAgentBashResult", sanitizeShellExecResult(execResult));
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
case "redactedReadArgs": {
|
|
1157
|
+
// The server expects the client to strip secrets from the content
|
|
1158
|
+
// first. No redaction is implemented here, and serving a plain read
|
|
1159
|
+
// would hand back exactly the unredacted bytes the frame exists to
|
|
1160
|
+
// withhold.
|
|
1161
|
+
const args = execMsg.message.value;
|
|
1162
|
+
sendExecClientMessage(h2Request, execMsg, "redactedReadResult", buildReadErrorResult(args.path, "Secret redaction is not implemented by this client"));
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
case "mcpStateExecArgs": {
|
|
1166
|
+
const args = execMsg.message.value;
|
|
1167
|
+
sendExecClientMessage(h2Request, execMsg, "mcpStateExecResult", buildMcpStateResult(requestContextTools, args.serverIdentifiers));
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
case "executeHookArgs": {
|
|
1171
|
+
const args = execMsg.message.value;
|
|
1172
|
+
const execResult = buildNeutralHookResult(args.request);
|
|
1173
|
+
if (!execResult) {
|
|
1174
|
+
sendExecClientThrow(h2Request, execMsg, `Unsupported hook request: ${args.request?.request.case ?? "unset"}`, "unknown_hook_request");
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
sendExecClientMessage(h2Request, execMsg, "executeHookResult", execResult);
|
|
1178
|
+
return;
|
|
1179
|
+
}
|
|
1180
|
+
case "subagentArgs": {
|
|
1181
|
+
const execResult = create(SubagentResultSchema, {
|
|
1182
|
+
result: {
|
|
1183
|
+
case: "error",
|
|
1184
|
+
value: create(SubagentErrorSchema, { error: `Subagents are ${NOT_IMPLEMENTED_SUFFIX}` }),
|
|
1185
|
+
},
|
|
1186
|
+
});
|
|
1187
|
+
sendExecClientMessage(h2Request, execMsg, "subagentResult", execResult);
|
|
1188
|
+
return;
|
|
1189
|
+
}
|
|
1190
|
+
case "subagentAwaitArgs": {
|
|
1191
|
+
// No subagent was ever spawned, so every awaited id is genuinely unknown.
|
|
1192
|
+
const args = execMsg.message.value;
|
|
1193
|
+
const execResult = create(SubagentAwaitResultSchema, {
|
|
1194
|
+
result: {
|
|
1195
|
+
case: "notFound",
|
|
1196
|
+
value: create(SubagentAwaitNotFoundSchema, { agentId: args.agentId }),
|
|
1197
|
+
},
|
|
1198
|
+
});
|
|
1199
|
+
sendExecClientMessage(h2Request, execMsg, "subagentAwaitResult", execResult);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
case "forceBackgroundShellArgs": {
|
|
1203
|
+
// Backgrounding targets a running tool call by id. This client runs
|
|
1204
|
+
// every shell to completion in band, so there is never one to move.
|
|
1205
|
+
const execResult = create(ForceBackgroundShellResultSchema, {
|
|
1206
|
+
status: ForceBackgroundShellStatus.NOT_FOUND,
|
|
1207
|
+
});
|
|
1208
|
+
sendExecClientMessage(h2Request, execMsg, "forceBackgroundShellResult", execResult);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
case "forceBackgroundSubagentArgs": {
|
|
1212
|
+
const execResult = create(ForceBackgroundSubagentResultSchema, {
|
|
1213
|
+
status: ForceBackgroundSubagentStatus.NOT_FOUND,
|
|
1214
|
+
});
|
|
1215
|
+
sendExecClientMessage(h2Request, execMsg, "forceBackgroundSubagentResult", execResult);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
case "smartModeClassifierArgs": {
|
|
1219
|
+
// The classifier decides whether a risky action needs approval.
|
|
1220
|
+
// Answering `ALLOW` would silently wave through actions the server
|
|
1221
|
+
// asked us to judge, so the honest answer is that no classifier
|
|
1222
|
+
// exists here.
|
|
1223
|
+
const execResult = create(SmartModeClassifierResultSchema, {
|
|
1224
|
+
result: {
|
|
1225
|
+
case: "error",
|
|
1226
|
+
value: create(SmartModeClassifierErrorSchema, {
|
|
1227
|
+
error: `Smart-mode classification is ${NOT_IMPLEMENTED_SUFFIX}`,
|
|
1228
|
+
}),
|
|
1229
|
+
},
|
|
1230
|
+
});
|
|
1231
|
+
sendExecClientMessage(h2Request, execMsg, "smartModeClassifierResult", execResult);
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
case "canvasDiagnosticsArgs": {
|
|
1235
|
+
const args = execMsg.message.value;
|
|
1236
|
+
const execResult = create(CanvasDiagnosticsResultSchema, {
|
|
1237
|
+
result: {
|
|
1238
|
+
case: "error",
|
|
1239
|
+
value: create(CanvasDiagnosticsErrorSchema, {
|
|
1240
|
+
path: args.path,
|
|
1241
|
+
error: `Canvas diagnostics are ${NOT_IMPLEMENTED_SUFFIX}`,
|
|
1242
|
+
}),
|
|
1243
|
+
},
|
|
1244
|
+
});
|
|
1245
|
+
sendExecClientMessage(h2Request, execMsg, "canvasDiagnosticsResult", execResult);
|
|
1246
|
+
return;
|
|
1247
|
+
}
|
|
1248
|
+
case "shellAllowlistPrecheckArgs": {
|
|
1249
|
+
// The prechecks ask "is this pre-approved, so may it skip the approval
|
|
1250
|
+
// prompt?". This client keeps no allowlist, so the answer is always
|
|
1251
|
+
// no: `false` costs an approval round-trip, `true` would grant one
|
|
1252
|
+
// that was never configured.
|
|
1253
|
+
sendExecClientMessage(h2Request, execMsg, "shellAllowlistPrecheckResult", create(ShellAllowlistPrecheckResultSchema, { allowlisted: false }));
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
case "mcpAllowlistPrecheckArgs": {
|
|
1257
|
+
sendExecClientMessage(h2Request, execMsg, "mcpAllowlistPrecheckResult", create(McpAllowlistPrecheckResultSchema, { allowlisted: false }));
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
case "webFetchAllowlistPrecheckArgs": {
|
|
1261
|
+
sendExecClientMessage(h2Request, execMsg, "webFetchAllowlistPrecheckResult", create(WebFetchAllowlistPrecheckResultSchema, { allowlisted: false }));
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
case "conversationSearchArgs": {
|
|
1265
|
+
// Cursor conversation history lives server-side; this client keeps no
|
|
1266
|
+
// local index of it to search. The streamed envelope announces this
|
|
1267
|
+
// call but the interaction decoder builds no block for it, so the
|
|
1268
|
+
// block and its paired result are synthesized here.
|
|
1269
|
+
const args = execMsg.message.value;
|
|
1270
|
+
const toolCallId = args.toolCallId || randomUUID();
|
|
1271
|
+
const error = `Conversation search is ${NOT_IMPLEMENTED_SUFFIX}`;
|
|
1272
|
+
synthesizeCursorExecToolCall(output, stream, state, toolCallId, "search_conversations", {
|
|
1273
|
+
query: args.query,
|
|
1274
|
+
limit: args.limit,
|
|
1275
|
+
});
|
|
1276
|
+
await pairSynthesizedExecResult(state, onToolResult, toolCallId, "search_conversations", error);
|
|
1277
|
+
const execResult = create(ConversationSearchResultSchema, {
|
|
1278
|
+
result: { case: "error", value: create(ConversationSearchErrorSchema, { error }) },
|
|
1279
|
+
});
|
|
1280
|
+
sendExecClientMessage(h2Request, execMsg, "conversationSearchResult", execResult);
|
|
1281
|
+
return;
|
|
1282
|
+
}
|
|
1283
|
+
case "agentStoreConflictArgs": {
|
|
1284
|
+
// The agent store is Cursor's own on-disk journal; this client never
|
|
1285
|
+
// writes one, so it has no conflict events to replay.
|
|
1286
|
+
const execResult = create(AgentStoreConflictResultSchema, {
|
|
1287
|
+
result: {
|
|
1288
|
+
case: "error",
|
|
1289
|
+
value: create(AgentStoreConflictErrorSchema, {
|
|
1290
|
+
error: `Agent store conflicts are ${NOT_IMPLEMENTED_SUFFIX}`,
|
|
1291
|
+
}),
|
|
1292
|
+
},
|
|
1293
|
+
});
|
|
1294
|
+
sendExecClientMessage(h2Request, execMsg, "agentStoreConflictResult", execResult);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
case "gitDiffRequest": {
|
|
1298
|
+
// `GetDiffResponse` has no error variant, so any in-band answer is a
|
|
1299
|
+
// claim that a diff was computed; a `throw` is the only truthful reply.
|
|
1300
|
+
sendExecClientThrow(h2Request, execMsg, `Git diff is ${NOT_IMPLEMENTED_SUFFIX}`, "exec_variant_unsupported");
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
default: {
|
|
1304
|
+
// A frame number this build recognises structurally but has no answer
|
|
1305
|
+
// for. Distinct from the unset-case path above: there the client
|
|
1306
|
+
// cannot even name the frame.
|
|
1307
|
+
log("warn", "unhandledExecMessage", { execCase });
|
|
1308
|
+
sendExecClientThrow(h2Request, execMsg, `No handler for exec message of type ${execCase}`, "exec_variant_unsupported");
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
function armCursorExecHeartbeat(h2Request, execMsg) {
|
|
1313
|
+
return armExecHeartbeat({
|
|
1314
|
+
intervalMs: EXEC_HEARTBEAT_INTERVAL_MS,
|
|
1315
|
+
isClosed: () => h2Request.closed,
|
|
1316
|
+
writeHeartbeat: (onComplete) => {
|
|
1317
|
+
const controlMessage = create(ExecClientControlMessageSchema, {
|
|
1318
|
+
message: {
|
|
1319
|
+
case: "heartbeat",
|
|
1320
|
+
value: create(ExecClientHeartbeatSchema, { id: execMsg.id }),
|
|
1321
|
+
},
|
|
1322
|
+
});
|
|
1323
|
+
const clientMessage = create(AgentClientMessageSchema, {
|
|
1324
|
+
message: { case: "execClientControlMessage", value: controlMessage },
|
|
1325
|
+
});
|
|
1326
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)), onComplete);
|
|
1327
|
+
log("execClientControl", "heartbeat", { id: execMsg.id, execId: execMsg.execId });
|
|
1328
|
+
},
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
/**
|
|
1332
|
+
* Send one typed answer on the exec channel.
|
|
1333
|
+
*
|
|
1334
|
+
* `ExecClientMessage["message"]` is a discriminated union pairing each case
|
|
1335
|
+
* with its own result type, so the generic is keyed on the case: passing a
|
|
1336
|
+
* `ReadResult` under `"shellResult"` is a compile error rather than a wire
|
|
1337
|
+
* message the server rejects at runtime.
|
|
1338
|
+
*/
|
|
1339
|
+
function sendExecClientMessage(h2Request, execMsg, messageCase, value) {
|
|
1340
|
+
const execClientMessage = create(ExecClientMessageSchema, {
|
|
1341
|
+
id: execMsg.id,
|
|
1342
|
+
execId: execMsg.execId,
|
|
1343
|
+
message: { case: messageCase, value },
|
|
1344
|
+
});
|
|
1345
|
+
const clientMessage = create(AgentClientMessageSchema, {
|
|
1346
|
+
message: { case: "execClientMessage", value: execClientMessage },
|
|
1347
|
+
});
|
|
1348
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
|
|
1349
|
+
log("execClientMessage", messageCase);
|
|
1350
|
+
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Fail one exec frame in band.
|
|
1353
|
+
*
|
|
1354
|
+
* `ExecClientThrow` is the protocol's failure channel for a frame that cannot
|
|
1355
|
+
* be answered at all — as opposed to a frame answered with its own typed
|
|
1356
|
+
* error variant, which means "the tool ran and failed". The server surfaces
|
|
1357
|
+
* the error to the model instead of blocking on a reply that never comes.
|
|
1358
|
+
*/
|
|
1359
|
+
function sendExecClientThrow(h2Request, execMsg, error, errorCode) {
|
|
1360
|
+
const controlMessage = create(ExecClientControlMessageSchema, {
|
|
1361
|
+
message: {
|
|
1362
|
+
case: "throw",
|
|
1363
|
+
value: create(ExecClientThrowSchema, { id: execMsg.id, error, errorCode }),
|
|
1364
|
+
},
|
|
1365
|
+
});
|
|
1366
|
+
const clientMessage = create(AgentClientMessageSchema, {
|
|
1367
|
+
message: { case: "execClientControlMessage", value: controlMessage },
|
|
1368
|
+
});
|
|
1369
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
|
|
1370
|
+
log("execClientControl", "throw", { id: execMsg.id, execId: execMsg.execId, error, errorCode });
|
|
1371
|
+
}
|
|
1372
|
+
function sendExecClientStreamClose(h2Request, execMsg) {
|
|
1373
|
+
const closeMessage = create(ExecClientControlMessageSchema, {
|
|
1374
|
+
message: {
|
|
1375
|
+
case: "streamClose",
|
|
1376
|
+
value: create(ExecClientStreamCloseSchema, {
|
|
1377
|
+
id: execMsg.id,
|
|
1378
|
+
}),
|
|
1379
|
+
},
|
|
1380
|
+
});
|
|
1381
|
+
const clientMessage = create(AgentClientMessageSchema, {
|
|
1382
|
+
message: { case: "execClientControlMessage", value: closeMessage },
|
|
1383
|
+
});
|
|
1384
|
+
h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
|
|
1385
|
+
log("execClientControl", "streamClose", { id: execMsg.id, execId: execMsg.execId });
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* Exported for tests: dispatches one exec frame onto its handler.
|
|
1389
|
+
*
|
|
1390
|
+
* Every exit pairs a `toolResult`. The synthesized block was already marked
|
|
1391
|
+
* `kCursorExecResolved` before this runs, so the agent loop emits no
|
|
1392
|
+
* placeholder for it: a path that returns without a result leaves the call
|
|
1393
|
+
* unpaired and transcript rebuilds strip the whole interaction. The three
|
|
1394
|
+
* result-less paths — no handler installed, a handler that produced nothing,
|
|
1395
|
+
* and a thrown handler — therefore synthesize one from the same text the
|
|
1396
|
+
* server sees in `execResult`.
|
|
1397
|
+
*
|
|
1398
|
+
* `pairing` is required so a new callsite cannot silently recreate the
|
|
1399
|
+
* orphan, and nullable for the callers whose block is NOT pre-resolved (MCP
|
|
1400
|
+
* without an `mcp` handler, which the agent loop runs locally and pairs
|
|
1401
|
+
* itself, and frames that never synthesize a block).
|
|
1402
|
+
*/
|
|
1403
|
+
export async function resolveExecHandler(args, handler, onToolResult, buildFromToolResult, buildRejected, buildError, pairing) {
|
|
1404
|
+
const pair = async (text, isError) => {
|
|
1405
|
+
if (!pairing)
|
|
1406
|
+
return undefined;
|
|
1407
|
+
const synthesized = {
|
|
1408
|
+
role: "toolResult",
|
|
1409
|
+
toolCallId: pairing.toolCallId,
|
|
1410
|
+
toolName: pairing.toolName,
|
|
1411
|
+
content: [{ type: "text", text }],
|
|
1412
|
+
isError,
|
|
1413
|
+
timestamp: Date.now(),
|
|
1414
|
+
};
|
|
1415
|
+
return await applyToolResultHandler(synthesized, onToolResult);
|
|
1416
|
+
};
|
|
1417
|
+
if (!handler) {
|
|
1418
|
+
const reason = "Tool not available";
|
|
1419
|
+
return { execResult: buildRejected(reason), toolResult: await pair(reason, true) };
|
|
1420
|
+
}
|
|
1421
|
+
try {
|
|
1422
|
+
const handlerResult = await handler(args);
|
|
1423
|
+
const { execResult, toolResult } = splitExecHandlerResult(handlerResult);
|
|
1424
|
+
const finalToolResult = await applyToolResultHandler(toolResult, onToolResult);
|
|
1425
|
+
if (execResult) {
|
|
1426
|
+
// TResult-only is a supported return form, so the transcript entry
|
|
1427
|
+
// has to be synthesized here, derived from the raw result so the two
|
|
1428
|
+
// views stay consistent.
|
|
1429
|
+
return {
|
|
1430
|
+
execResult,
|
|
1431
|
+
toolResult: finalToolResult ?? (await pair(...describeExecResult(execResult))),
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
if (finalToolResult) {
|
|
1435
|
+
return { execResult: buildFromToolResult(finalToolResult), toolResult: finalToolResult };
|
|
1436
|
+
}
|
|
1437
|
+
const reason = "Tool returned no result";
|
|
1438
|
+
return { execResult: buildRejected(reason), toolResult: await pair(reason, true) };
|
|
1439
|
+
}
|
|
1440
|
+
catch (error) {
|
|
1441
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1442
|
+
return { execResult: buildError(message), toolResult: await pair(message, true) };
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Derive the transcript state of an exec result the handler returned in the
|
|
1447
|
+
* TResult-only form, which carries no `toolResult` to copy it from.
|
|
1448
|
+
*
|
|
1449
|
+
* Every exec result in `agent.proto` is a `oneof result` whose success
|
|
1450
|
+
* variant is named `success` — the rest are failures. MCP is the one shape
|
|
1451
|
+
* where `success` is not enough: `McpSuccess.is_error` carries an
|
|
1452
|
+
* application-level tool failure inside the success variant.
|
|
1453
|
+
*/
|
|
1454
|
+
function describeExecResult(execResult) {
|
|
1455
|
+
const result = execResult?.result;
|
|
1456
|
+
const variant = result?.case;
|
|
1457
|
+
if (variant === "success") {
|
|
1458
|
+
const success = result?.value;
|
|
1459
|
+
if (!success?.isError)
|
|
1460
|
+
return ["Tool produced no transcript result", false];
|
|
1461
|
+
return [mcpContentToText(success.content) || "MCP tool reported an error", true];
|
|
1462
|
+
}
|
|
1463
|
+
if (!variant)
|
|
1464
|
+
return ["Tool produced no transcript result", false];
|
|
1465
|
+
const value = result?.value;
|
|
1466
|
+
return [value?.error || value?.reason || `Tool call ${variant}`, true];
|
|
1467
|
+
}
|
|
1468
|
+
/** Flatten `McpSuccess.content` into transcript text. */
|
|
1469
|
+
function mcpContentToText(content) {
|
|
1470
|
+
if (!Array.isArray(content))
|
|
1471
|
+
return "";
|
|
1472
|
+
const parts = [];
|
|
1473
|
+
for (const item of content) {
|
|
1474
|
+
const inner = item?.content;
|
|
1475
|
+
if (inner?.case === "text" && inner.value?.text)
|
|
1476
|
+
parts.push(inner.value.text);
|
|
1477
|
+
}
|
|
1478
|
+
return parts.join("\n");
|
|
1479
|
+
}
|
|
1480
|
+
function splitExecHandlerResult(result) {
|
|
1481
|
+
if (isToolResultMessage(result)) {
|
|
1482
|
+
return { toolResult: result };
|
|
1483
|
+
}
|
|
1484
|
+
if (result && typeof result === "object") {
|
|
1485
|
+
const record = result;
|
|
1486
|
+
if ("execResult" in record) {
|
|
1487
|
+
const { execResult, toolResult } = record;
|
|
1488
|
+
return { execResult, toolResult };
|
|
1489
|
+
}
|
|
1490
|
+
if ("toolResult" in record && !isToolResultMessage(record)) {
|
|
1491
|
+
const { result: execResult, toolResult } = record;
|
|
1492
|
+
return { execResult, toolResult };
|
|
1493
|
+
}
|
|
1494
|
+
if ("result" in record && !("$typeName" in record)) {
|
|
1495
|
+
const { result: execResult, toolResult } = record;
|
|
1496
|
+
return { execResult, toolResult };
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
return { execResult: result };
|
|
1500
|
+
}
|
|
1501
|
+
function isToolResultMessage(value) {
|
|
1502
|
+
return !!value && typeof value === "object" && value.role === "toolResult";
|
|
1503
|
+
}
|
|
1504
|
+
async function applyToolResultHandler(toolResult, onToolResult) {
|
|
1505
|
+
if (!toolResult || !onToolResult) {
|
|
1506
|
+
return toolResult;
|
|
1507
|
+
}
|
|
1508
|
+
const updated = await onToolResult(toolResult);
|
|
1509
|
+
return updated ?? toolResult;
|
|
1510
|
+
}
|
|
1511
|
+
function toolResultToText(toolResult) {
|
|
1512
|
+
return toolResult.content.map((item) => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n");
|
|
1513
|
+
}
|
|
1514
|
+
function toolResultWasTruncated(toolResult) {
|
|
1515
|
+
if (!toolResult.details || typeof toolResult.details !== "object") {
|
|
1516
|
+
return false;
|
|
1517
|
+
}
|
|
1518
|
+
const truncation = toolResult.details.truncation;
|
|
1519
|
+
return !!truncation?.truncated;
|
|
1520
|
+
}
|
|
1521
|
+
function toolResultDetailBoolean(toolResult, key) {
|
|
1522
|
+
if (!toolResult.details || typeof toolResult.details !== "object") {
|
|
1523
|
+
return false;
|
|
1524
|
+
}
|
|
1525
|
+
const value = toolResult.details[key];
|
|
1526
|
+
return typeof value === "boolean" ? value : false;
|
|
1527
|
+
}
|
|
1528
|
+
/** The file's own line count, when the tool recorded one. */
|
|
1529
|
+
function readTotalLinesFromDetails(toolResult) {
|
|
1530
|
+
const details = toolResult.details;
|
|
1531
|
+
if (!details || typeof details !== "object")
|
|
1532
|
+
return undefined;
|
|
1533
|
+
const direct = "totalLines" in details ? details.totalLines : undefined;
|
|
1534
|
+
if (typeof direct === "number" && Number.isFinite(direct))
|
|
1535
|
+
return direct;
|
|
1536
|
+
const meta = "meta" in details ? details.meta : undefined;
|
|
1537
|
+
if (!meta || typeof meta !== "object")
|
|
1538
|
+
return undefined;
|
|
1539
|
+
const truncation = "truncation" in meta ? meta.truncation : undefined;
|
|
1540
|
+
if (!truncation || typeof truncation !== "object")
|
|
1541
|
+
return undefined;
|
|
1542
|
+
const totalLines = "totalLines" in truncation ? truncation.totalLines : undefined;
|
|
1543
|
+
return typeof totalLines === "number" && Number.isFinite(totalLines) ? totalLines : undefined;
|
|
1544
|
+
}
|
|
1545
|
+
function readFileSizeFromDetails(toolResult) {
|
|
1546
|
+
const details = toolResult.details;
|
|
1547
|
+
if (!details || typeof details !== "object" || !("fileSize" in details))
|
|
1548
|
+
return undefined;
|
|
1549
|
+
const { fileSize } = details;
|
|
1550
|
+
return typeof fileSize === "number" && Number.isSafeInteger(fileSize) && fileSize >= 0 ? fileSize : undefined;
|
|
1551
|
+
}
|
|
1552
|
+
function buildReadResultFromToolResult(path, toolResult, rangeApplied = false) {
|
|
1553
|
+
const text = toolResultToText(toolResult);
|
|
1554
|
+
if (toolResult.isError) {
|
|
1555
|
+
return buildReadErrorResult(path, text || "Read failed");
|
|
1556
|
+
}
|
|
1557
|
+
// Counting the payload is only the file's length when the payload is the
|
|
1558
|
+
// whole file: under a windowed read, answering a 20-line page of a 100-line
|
|
1559
|
+
// file with `total_lines: 20` tells a paginating server it reached the end.
|
|
1560
|
+
const totalLines = readTotalLinesFromDetails(toolResult) ?? (rangeApplied ? 0 : text ? text.split("\n").length : 0);
|
|
1561
|
+
return create(ReadResultSchema, {
|
|
1562
|
+
result: {
|
|
1563
|
+
case: "success",
|
|
1564
|
+
value: create(ReadSuccessSchema, {
|
|
1565
|
+
path,
|
|
1566
|
+
totalLines,
|
|
1567
|
+
fileSize: BigInt(readFileSizeFromDetails(toolResult) ?? Buffer.byteLength(text, "utf-8")),
|
|
1568
|
+
truncated: toolResultWasTruncated(toolResult),
|
|
1569
|
+
output: { case: "content", value: text },
|
|
1570
|
+
rangeApplied,
|
|
1571
|
+
}),
|
|
1572
|
+
},
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
function buildReadErrorResult(path, error) {
|
|
1576
|
+
return create(ReadResultSchema, {
|
|
1577
|
+
result: {
|
|
1578
|
+
case: "error",
|
|
1579
|
+
value: create(ReadErrorSchema, { path, error }),
|
|
1580
|
+
},
|
|
1581
|
+
});
|
|
1582
|
+
}
|
|
1583
|
+
function buildReadRejectedResult(path, reason) {
|
|
1584
|
+
return create(ReadResultSchema, {
|
|
1585
|
+
result: {
|
|
1586
|
+
case: "rejected",
|
|
1587
|
+
value: create(ReadRejectedSchema, { path, reason }),
|
|
1588
|
+
},
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
function buildWriteResultFromToolResult(args, toolResult) {
|
|
1592
|
+
const text = toolResultToText(toolResult);
|
|
1593
|
+
if (toolResult.isError) {
|
|
1594
|
+
return buildWriteErrorResult(args.path, text || "Write failed");
|
|
1595
|
+
}
|
|
1596
|
+
const fileText = args.fileText ?? "";
|
|
1597
|
+
const fileSize = args.fileBytes?.length ?? Buffer.byteLength(fileText, "utf-8");
|
|
1598
|
+
const linesCreated = fileText ? fileText.split("\n").length : 0;
|
|
1599
|
+
return create(WriteResultSchema, {
|
|
1600
|
+
result: {
|
|
1601
|
+
case: "success",
|
|
1602
|
+
value: create(WriteSuccessSchema, {
|
|
1603
|
+
path: args.path,
|
|
1604
|
+
linesCreated,
|
|
1605
|
+
fileSize,
|
|
1606
|
+
fileContentAfterWrite: args.returnFileContentAfterWrite ? fileText : undefined,
|
|
1607
|
+
}),
|
|
1608
|
+
},
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
function buildWriteErrorResult(path, error) {
|
|
1612
|
+
return create(WriteResultSchema, {
|
|
1613
|
+
result: {
|
|
1614
|
+
case: "error",
|
|
1615
|
+
value: create(WriteErrorSchema, { path, error }),
|
|
1616
|
+
},
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
function buildWriteRejectedResult(path, reason) {
|
|
1620
|
+
return create(WriteResultSchema, {
|
|
1621
|
+
result: {
|
|
1622
|
+
case: "rejected",
|
|
1623
|
+
value: create(WriteRejectedSchema, { path, reason }),
|
|
1624
|
+
},
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
function buildDeleteResultFromToolResult(path, toolResult) {
|
|
1628
|
+
const text = toolResultToText(toolResult);
|
|
1629
|
+
if (toolResult.isError) {
|
|
1630
|
+
return buildDeleteErrorResult(path, text || "Delete failed");
|
|
1631
|
+
}
|
|
1632
|
+
return create(DeleteResultSchema, {
|
|
1633
|
+
result: {
|
|
1634
|
+
case: "success",
|
|
1635
|
+
value: create(DeleteSuccessSchema, {
|
|
1636
|
+
path,
|
|
1637
|
+
deletedFile: path,
|
|
1638
|
+
fileSize: BigInt(0),
|
|
1639
|
+
prevContent: "",
|
|
1640
|
+
}),
|
|
1641
|
+
},
|
|
1642
|
+
});
|
|
1643
|
+
}
|
|
1644
|
+
function buildDeleteErrorResult(path, error) {
|
|
1645
|
+
return create(DeleteResultSchema, {
|
|
1646
|
+
result: {
|
|
1647
|
+
case: "error",
|
|
1648
|
+
value: create(DeleteErrorSchema, { path, error }),
|
|
1649
|
+
},
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
function buildDeleteRejectedResult(path, reason) {
|
|
1653
|
+
return create(DeleteResultSchema, {
|
|
1654
|
+
result: {
|
|
1655
|
+
case: "rejected",
|
|
1656
|
+
value: create(DeleteRejectedSchema, { path, reason }),
|
|
1657
|
+
},
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
function buildShellResultFromToolResult(args, toolResult) {
|
|
1661
|
+
const output = toolResultToText(toolResult);
|
|
1662
|
+
if (toolResult.isError) {
|
|
1663
|
+
return buildShellFailureResult(args.command, args.workingDirectory, output || "Shell failed");
|
|
1664
|
+
}
|
|
1665
|
+
return create(ShellResultSchema, {
|
|
1666
|
+
result: {
|
|
1667
|
+
case: "success",
|
|
1668
|
+
value: create(ShellSuccessSchema, {
|
|
1669
|
+
command: args.command,
|
|
1670
|
+
workingDirectory: args.workingDirectory,
|
|
1671
|
+
exitCode: 0,
|
|
1672
|
+
signal: "",
|
|
1673
|
+
stdout: output,
|
|
1674
|
+
stderr: "",
|
|
1675
|
+
executionTime: 0,
|
|
1676
|
+
}),
|
|
1677
|
+
},
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
function buildShellFailureResult(command, workingDirectory, error) {
|
|
1681
|
+
return create(ShellResultSchema, {
|
|
1682
|
+
result: {
|
|
1683
|
+
case: "failure",
|
|
1684
|
+
value: create(ShellFailureSchema, {
|
|
1685
|
+
command,
|
|
1686
|
+
workingDirectory,
|
|
1687
|
+
exitCode: 1,
|
|
1688
|
+
signal: "",
|
|
1689
|
+
stdout: "",
|
|
1690
|
+
stderr: error,
|
|
1691
|
+
executionTime: 0,
|
|
1692
|
+
aborted: false,
|
|
1693
|
+
}),
|
|
1694
|
+
},
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
function buildShellRejectedResult(command, workingDirectory, reason) {
|
|
1698
|
+
return create(ShellResultSchema, {
|
|
1699
|
+
result: {
|
|
1700
|
+
case: "rejected",
|
|
1701
|
+
value: create(ShellRejectedSchema, {
|
|
1702
|
+
command,
|
|
1703
|
+
workingDirectory,
|
|
1704
|
+
reason,
|
|
1705
|
+
isReadonly: false,
|
|
1706
|
+
}),
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1710
|
+
function buildLsResultFromToolResult(path, toolResult) {
|
|
1711
|
+
const text = toolResultToText(toolResult);
|
|
1712
|
+
if (toolResult.isError) {
|
|
1713
|
+
return buildLsErrorResult(path, text || "Ls failed");
|
|
1714
|
+
}
|
|
1715
|
+
const rootPath = path || ".";
|
|
1716
|
+
const entries = text
|
|
1717
|
+
.split("\n")
|
|
1718
|
+
.map((line) => line.trim())
|
|
1719
|
+
.filter((line) => line.length > 0 && !line.startsWith("["));
|
|
1720
|
+
const childrenDirs = [];
|
|
1721
|
+
const childrenFiles = [];
|
|
1722
|
+
for (const entry of entries) {
|
|
1723
|
+
const name = entry.split(" (")[0];
|
|
1724
|
+
if (name.endsWith("/")) {
|
|
1725
|
+
const dirName = name.slice(0, -1);
|
|
1726
|
+
childrenDirs.push(create(LsDirectoryTreeNodeSchema, {
|
|
1727
|
+
absPath: `${rootPath.replace(/\/$/, "")}/${dirName}`,
|
|
1728
|
+
childrenDirs: [],
|
|
1729
|
+
childrenFiles: [],
|
|
1730
|
+
childrenWereProcessed: false,
|
|
1731
|
+
fullSubtreeExtensionCounts: {},
|
|
1732
|
+
numFiles: 0,
|
|
1733
|
+
}));
|
|
1734
|
+
}
|
|
1735
|
+
else {
|
|
1736
|
+
childrenFiles.push(create(LsDirectoryTreeNode_FileSchema, { name }));
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
const root = create(LsDirectoryTreeNodeSchema, {
|
|
1740
|
+
absPath: rootPath,
|
|
1741
|
+
childrenDirs,
|
|
1742
|
+
childrenFiles,
|
|
1743
|
+
childrenWereProcessed: true,
|
|
1744
|
+
fullSubtreeExtensionCounts: {},
|
|
1745
|
+
numFiles: childrenFiles.length,
|
|
1746
|
+
});
|
|
1747
|
+
return create(LsResultSchema, {
|
|
1748
|
+
result: {
|
|
1749
|
+
case: "success",
|
|
1750
|
+
value: create(LsSuccessSchema, { directoryTreeRoot: root }),
|
|
1751
|
+
},
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
function buildLsErrorResult(path, error) {
|
|
1755
|
+
return create(LsResultSchema, {
|
|
1756
|
+
result: {
|
|
1757
|
+
case: "error",
|
|
1758
|
+
value: create(LsErrorSchema, { path, error }),
|
|
1759
|
+
},
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
function buildLsRejectedResult(path, reason) {
|
|
1763
|
+
return create(LsResultSchema, {
|
|
1764
|
+
result: {
|
|
1765
|
+
case: "rejected",
|
|
1766
|
+
value: create(LsRejectedSchema, { path, reason }),
|
|
1767
|
+
},
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
function buildGrepResultFromToolResult(args, toolResult) {
|
|
1771
|
+
const text = toolResultToText(toolResult);
|
|
1772
|
+
if (toolResult.isError) {
|
|
1773
|
+
return buildGrepErrorResult(text || "Grep failed");
|
|
1774
|
+
}
|
|
1775
|
+
const outputMode = args.outputMode || "content";
|
|
1776
|
+
const clientTruncated = toolResultDetailBoolean(toolResult, "truncated");
|
|
1777
|
+
const lines = text
|
|
1778
|
+
.split("\n")
|
|
1779
|
+
.map((line) => line.trimEnd())
|
|
1780
|
+
.filter((line) => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches"));
|
|
1781
|
+
const workspaceKey = args.path || ".";
|
|
1782
|
+
let unionResult;
|
|
1783
|
+
if (outputMode === "files_with_matches") {
|
|
1784
|
+
const files = lines;
|
|
1785
|
+
unionResult = create(GrepUnionResultSchema, {
|
|
1786
|
+
result: {
|
|
1787
|
+
case: "files",
|
|
1788
|
+
value: create(GrepFilesResultSchema, {
|
|
1789
|
+
files,
|
|
1790
|
+
totalFiles: files.length,
|
|
1791
|
+
clientTruncated,
|
|
1792
|
+
ripgrepTruncated: false,
|
|
1793
|
+
offsetApplied: args.offset,
|
|
1794
|
+
}),
|
|
1795
|
+
},
|
|
1796
|
+
});
|
|
1797
|
+
}
|
|
1798
|
+
else if (outputMode === "count") {
|
|
1799
|
+
const counts = lines
|
|
1800
|
+
.map((line) => {
|
|
1801
|
+
const separatorIndex = line.lastIndexOf(":");
|
|
1802
|
+
if (separatorIndex === -1) {
|
|
1803
|
+
return null;
|
|
1804
|
+
}
|
|
1805
|
+
const file = line.slice(0, separatorIndex);
|
|
1806
|
+
const count = Number.parseInt(line.slice(separatorIndex + 1), 10);
|
|
1807
|
+
if (!file || Number.isNaN(count)) {
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
return create(GrepFileCountSchema, { file, count });
|
|
1811
|
+
})
|
|
1812
|
+
.filter((entry) => entry !== null);
|
|
1813
|
+
const totalMatches = counts.reduce((sum, entry) => sum + entry.count, 0);
|
|
1814
|
+
unionResult = create(GrepUnionResultSchema, {
|
|
1815
|
+
result: {
|
|
1816
|
+
case: "count",
|
|
1817
|
+
value: create(GrepCountResultSchema, {
|
|
1818
|
+
counts,
|
|
1819
|
+
totalFiles: counts.length,
|
|
1820
|
+
totalMatches,
|
|
1821
|
+
clientTruncated,
|
|
1822
|
+
ripgrepTruncated: false,
|
|
1823
|
+
offsetApplied: args.offset,
|
|
1824
|
+
}),
|
|
1825
|
+
},
|
|
1826
|
+
});
|
|
1827
|
+
}
|
|
1828
|
+
else {
|
|
1829
|
+
const matchMap = new Map();
|
|
1830
|
+
let totalMatchedLines = 0;
|
|
1831
|
+
for (const line of lines) {
|
|
1832
|
+
const matchLine = line.match(/^(.+?):(\d+):\s?(.*)$/);
|
|
1833
|
+
const contextLine = line.match(/^(.+?)-(\d+)-\s?(.*)$/);
|
|
1834
|
+
const match = matchLine ?? contextLine;
|
|
1835
|
+
if (!match) {
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1838
|
+
const [, file, lineNumber, content] = match;
|
|
1839
|
+
const isContextLine = Boolean(contextLine);
|
|
1840
|
+
const list = matchMap.get(file) ?? [];
|
|
1841
|
+
list.push({ line: Number(lineNumber), content, isContextLine });
|
|
1842
|
+
matchMap.set(file, list);
|
|
1843
|
+
if (!isContextLine) {
|
|
1844
|
+
totalMatchedLines += 1;
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
const matches = Array.from(matchMap.entries()).map(([file, fileMatches]) => create(GrepFileMatchSchema, {
|
|
1848
|
+
file,
|
|
1849
|
+
matches: fileMatches.map((entry) => create(GrepContentMatchSchema, {
|
|
1850
|
+
lineNumber: entry.line,
|
|
1851
|
+
content: entry.content,
|
|
1852
|
+
contentTruncated: false,
|
|
1853
|
+
isContextLine: entry.isContextLine,
|
|
1854
|
+
})),
|
|
1855
|
+
}));
|
|
1856
|
+
const totalLines = matches.reduce((sum, entry) => sum + entry.matches.length, 0);
|
|
1857
|
+
unionResult = create(GrepUnionResultSchema, {
|
|
1858
|
+
result: {
|
|
1859
|
+
case: "content",
|
|
1860
|
+
value: create(GrepContentResultSchema, {
|
|
1861
|
+
matches,
|
|
1862
|
+
totalLines,
|
|
1863
|
+
totalMatchedLines,
|
|
1864
|
+
clientTruncated,
|
|
1865
|
+
ripgrepTruncated: false,
|
|
1866
|
+
offsetApplied: args.offset,
|
|
1867
|
+
}),
|
|
1868
|
+
},
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
return create(GrepResultSchema, {
|
|
1872
|
+
result: {
|
|
1873
|
+
case: "success",
|
|
1874
|
+
value: create(GrepSuccessSchema, {
|
|
1875
|
+
pattern: args.pattern,
|
|
1876
|
+
path: args.path || "",
|
|
1877
|
+
outputMode,
|
|
1878
|
+
workspaceResults: { [workspaceKey]: unionResult },
|
|
1879
|
+
}),
|
|
1880
|
+
},
|
|
1881
|
+
});
|
|
1882
|
+
}
|
|
1883
|
+
function buildGrepErrorResult(error) {
|
|
1884
|
+
return create(GrepResultSchema, {
|
|
1885
|
+
result: {
|
|
1886
|
+
case: "error",
|
|
1887
|
+
value: create(GrepErrorSchema, { error }),
|
|
1888
|
+
},
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Reject a Cursor exec-channel `grepArgs` frame whose `pattern` is empty or
|
|
1893
|
+
* whitespace-only. Returns an actionable error message (with a `glob`-aware
|
|
1894
|
+
* hint when the model likely meant to list files) or `null` when the pattern
|
|
1895
|
+
* is valid and grep should run. Exported for tests.
|
|
1896
|
+
*/
|
|
1897
|
+
export function emptyGrepPatternRejection(pattern, glob) {
|
|
1898
|
+
if (pattern && pattern.trim().length > 0)
|
|
1899
|
+
return null;
|
|
1900
|
+
if (glob && glob.length > 0) {
|
|
1901
|
+
return (`grep pattern is required (received an empty pattern). To list files matching "${glob}", ` +
|
|
1902
|
+
`pass a non-empty regex (e.g. ".") and set path to that glob, or use the ls/read tool instead.`);
|
|
1903
|
+
}
|
|
1904
|
+
return "grep pattern is required (received an empty pattern).";
|
|
1905
|
+
}
|
|
1906
|
+
function buildDiagnosticsResultFromToolResult(path, toolResult) {
|
|
1907
|
+
const text = toolResultToText(toolResult);
|
|
1908
|
+
if (toolResult.isError) {
|
|
1909
|
+
return buildDiagnosticsErrorResult(path, text || "Diagnostics failed");
|
|
1910
|
+
}
|
|
1911
|
+
return create(DiagnosticsResultSchema, {
|
|
1912
|
+
result: {
|
|
1913
|
+
case: "success",
|
|
1914
|
+
value: create(DiagnosticsSuccessSchema, {
|
|
1915
|
+
path,
|
|
1916
|
+
diagnostics: [],
|
|
1917
|
+
totalDiagnostics: 0,
|
|
1918
|
+
}),
|
|
1919
|
+
},
|
|
1920
|
+
});
|
|
1921
|
+
}
|
|
1922
|
+
function buildDiagnosticsErrorResult(_path, error) {
|
|
1923
|
+
return create(DiagnosticsResultSchema, {
|
|
1924
|
+
result: {
|
|
1925
|
+
case: "error",
|
|
1926
|
+
value: create(DiagnosticsErrorSchema, { error }),
|
|
1927
|
+
},
|
|
1928
|
+
});
|
|
1929
|
+
}
|
|
1930
|
+
function buildDiagnosticsRejectedResult(path, reason) {
|
|
1931
|
+
return create(DiagnosticsResultSchema, {
|
|
1932
|
+
result: {
|
|
1933
|
+
case: "rejected",
|
|
1934
|
+
value: create(DiagnosticsRejectedSchema, { path, reason }),
|
|
1935
|
+
},
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
function parseToolArgsJson(text) {
|
|
1939
|
+
const trimmed = text.trim();
|
|
1940
|
+
if (!trimmed) {
|
|
1941
|
+
return text;
|
|
1942
|
+
}
|
|
1943
|
+
try {
|
|
1944
|
+
return parseJsonWithRepair(trimmed);
|
|
1945
|
+
}
|
|
1946
|
+
catch {
|
|
1947
|
+
return text;
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
function decodeMcpArgValue(value) {
|
|
1951
|
+
try {
|
|
1952
|
+
const parsedValue = fromBinary(ValueSchema, value);
|
|
1953
|
+
const jsonValue = toJson(ValueSchema, parsedValue);
|
|
1954
|
+
if (typeof jsonValue === "string") {
|
|
1955
|
+
return parseToolArgsJson(jsonValue);
|
|
1956
|
+
}
|
|
1957
|
+
return jsonValue;
|
|
1958
|
+
}
|
|
1959
|
+
catch { }
|
|
1960
|
+
const text = new TextDecoder().decode(value);
|
|
1961
|
+
return parseToolArgsJson(text);
|
|
1962
|
+
}
|
|
1963
|
+
function decodeMcpArgsMap(args) {
|
|
1964
|
+
if (!args) {
|
|
1965
|
+
return undefined;
|
|
1966
|
+
}
|
|
1967
|
+
const decoded = {};
|
|
1968
|
+
for (const [key, value] of Object.entries(args)) {
|
|
1969
|
+
decoded[key] = decodeMcpArgValue(value);
|
|
1970
|
+
}
|
|
1971
|
+
return decoded;
|
|
1972
|
+
}
|
|
1973
|
+
function decodeMcpCall(args) {
|
|
1974
|
+
const decodedArgs = {};
|
|
1975
|
+
for (const [key, value] of Object.entries(args.args ?? {})) {
|
|
1976
|
+
decodedArgs[key] = decodeMcpArgValue(value);
|
|
1977
|
+
}
|
|
1978
|
+
return {
|
|
1979
|
+
name: args.name,
|
|
1980
|
+
providerIdentifier: args.providerIdentifier,
|
|
1981
|
+
toolName: args.toolName || args.name,
|
|
1982
|
+
toolCallId: args.toolCallId,
|
|
1983
|
+
args: decodedArgs,
|
|
1984
|
+
rawArgs: args.args ?? {},
|
|
1985
|
+
approvalOnly: args.smartModeApprovalOnly === true,
|
|
1986
|
+
};
|
|
1987
|
+
}
|
|
1988
|
+
/**
|
|
1989
|
+
* Map Cursor's `TodoStatus` enum onto display statuses. Cancelled (4) maps to
|
|
1990
|
+
* `abandoned` rather than collapsing to `pending`, which would resurrect a
|
|
1991
|
+
* task the model explicitly cancelled.
|
|
1992
|
+
*/
|
|
1993
|
+
function mapTodoStatusValue(status) {
|
|
1994
|
+
switch (status) {
|
|
1995
|
+
case 2:
|
|
1996
|
+
return "in_progress";
|
|
1997
|
+
case 3:
|
|
1998
|
+
return "completed";
|
|
1999
|
+
case 4:
|
|
2000
|
+
return "abandoned";
|
|
2001
|
+
default:
|
|
2002
|
+
return "pending";
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
function selectTodoCalls(toolCall) {
|
|
2006
|
+
const oneof = toolCall.tool;
|
|
2007
|
+
if (oneof?.case === "updateTodosToolCall")
|
|
2008
|
+
return { update: oneof.value };
|
|
2009
|
+
if (oneof?.case === "readTodosToolCall")
|
|
2010
|
+
return { read: oneof.value };
|
|
2011
|
+
return { update: toolCall.updateTodosToolCall, read: toolCall.readTodosToolCall };
|
|
2012
|
+
}
|
|
2013
|
+
function mapTodoSnapshot(todos) {
|
|
2014
|
+
return todos.map((todo) => ({
|
|
2015
|
+
content: typeof todo.content === "string" ? todo.content : "",
|
|
2016
|
+
status: mapTodoStatusValue(typeof todo.status === "number" ? todo.status : undefined),
|
|
2017
|
+
}));
|
|
2018
|
+
}
|
|
2019
|
+
/**
|
|
2020
|
+
* `ToolCall.tool` is a protobuf oneof: a wire-decoded message exposes the
|
|
2021
|
+
* variant as `{ case, value }` and NEVER as a flattened property. The flat
|
|
2022
|
+
* fallback is kept for hand-shaped test fixtures.
|
|
2023
|
+
*/
|
|
2024
|
+
function selectMcpCall(toolCall) {
|
|
2025
|
+
const oneof = toolCall?.tool;
|
|
2026
|
+
if (oneof?.case === "mcpToolCall")
|
|
2027
|
+
return oneof.value;
|
|
2028
|
+
return toolCall?.mcpToolCall;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* The streamed `ToolCall` variants whose block the exec channel owns.
|
|
2032
|
+
*
|
|
2033
|
+
* Each of these is announced on the interaction stream AND dispatched as its
|
|
2034
|
+
* own `ExecServerMessage` frame, so the block is synthesized once, by the
|
|
2035
|
+
* exec handler, which is the side that has the result.
|
|
2036
|
+
*/
|
|
2037
|
+
const EXEC_OWNED_TOOL_CALL_CASES = new Set([
|
|
2038
|
+
"piReadToolCall",
|
|
2039
|
+
"piBashToolCall",
|
|
2040
|
+
"piEditToolCall",
|
|
2041
|
+
"piWriteToolCall",
|
|
2042
|
+
"piGrepToolCall",
|
|
2043
|
+
"piFindToolCall",
|
|
2044
|
+
"piLsToolCall",
|
|
2045
|
+
"listMcpResourcesToolCall",
|
|
2046
|
+
"readMcpResourceToolCall",
|
|
2047
|
+
]);
|
|
2048
|
+
function isExecOwnedToolCall(toolCall) {
|
|
2049
|
+
const variant = toolCall?.tool?.case;
|
|
2050
|
+
return variant !== undefined && EXEC_OWNED_TOOL_CALL_CASES.has(variant);
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Close every tool-call block still open when the stream ends.
|
|
2054
|
+
*
|
|
2055
|
+
* Not just the last one started: with interleaved calls several can be open
|
|
2056
|
+
* at once, and an unclosed block leaves its live card animating and its call
|
|
2057
|
+
* unpaired. Only blocks fed by a streamed argument buffer get reparsed.
|
|
2058
|
+
* Server-owned blocks (`connect-scm`, `todo`) are also paired here: they are
|
|
2059
|
+
* stamped resolved the moment they open, so only their completion frame pairs
|
|
2060
|
+
* a result — a transport that closes before that frame would leave the call
|
|
2061
|
+
* unpaired. MCP blocks are excluded even when resolved: the exec dispatch
|
|
2062
|
+
* that marked them owns their result.
|
|
2063
|
+
*/
|
|
2064
|
+
export function flushOpenToolCalls(output, stream, state) {
|
|
2065
|
+
const openBlocks = new Set(state.openToolCalls.values());
|
|
2066
|
+
if (state.currentToolCall)
|
|
2067
|
+
openBlocks.add(state.currentToolCall);
|
|
2068
|
+
for (const block of openBlocks) {
|
|
2069
|
+
const idx = output.content.indexOf(block);
|
|
2070
|
+
const partialJson = block[kStreamingPartialJson];
|
|
2071
|
+
if (partialJson !== undefined) {
|
|
2072
|
+
block.arguments = parseStreamingJson(partialJson);
|
|
2073
|
+
clearStreamingPartialJson(block);
|
|
2074
|
+
}
|
|
2075
|
+
const kind = block[kStreamingBlockKind];
|
|
2076
|
+
if (kind === "connect-scm" || kind === "todo") {
|
|
2077
|
+
void state.onToolResult?.({
|
|
2078
|
+
role: "toolResult",
|
|
2079
|
+
toolCallId: block.id,
|
|
2080
|
+
toolName: block.name,
|
|
2081
|
+
content: [{ type: "text", text: "The connection to Cursor closed before this call completed." }],
|
|
2082
|
+
isError: true,
|
|
2083
|
+
timestamp: Date.now(),
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: block, partial: output });
|
|
2087
|
+
}
|
|
2088
|
+
state.openToolCalls.clear();
|
|
2089
|
+
state.setToolCall(null);
|
|
2090
|
+
}
|
|
2091
|
+
/**
|
|
2092
|
+
* Retain a freshly opened streamed tool-call block, keyed by the interaction
|
|
2093
|
+
* envelope's `call_id`. `currentToolCall` is still set as the fallback for
|
|
2094
|
+
* frames that carry no `call_id`.
|
|
2095
|
+
*/
|
|
2096
|
+
function retainStreamedCall(state, block, envelopeId) {
|
|
2097
|
+
if (envelopeId)
|
|
2098
|
+
state.openToolCalls.set(envelopeId, block);
|
|
2099
|
+
state.setToolCall(block);
|
|
2100
|
+
}
|
|
2101
|
+
/**
|
|
2102
|
+
* The open block a streamed update addresses, or `null` to ignore the update.
|
|
2103
|
+
*
|
|
2104
|
+
* Cursor interleaves calls: `start A, start B, complete A` is legal, so the
|
|
2105
|
+
* update must reach block A even though B opened last. An id naming no open
|
|
2106
|
+
* block is ignored rather than misapplied. A missing id falls back to the
|
|
2107
|
+
* current block.
|
|
2108
|
+
*/
|
|
2109
|
+
function resolveStreamedCall(state, envelopeId) {
|
|
2110
|
+
if (!envelopeId)
|
|
2111
|
+
return state.currentToolCall;
|
|
2112
|
+
const keyed = state.openToolCalls.get(envelopeId);
|
|
2113
|
+
if (keyed)
|
|
2114
|
+
return keyed;
|
|
2115
|
+
const current = state.currentToolCall;
|
|
2116
|
+
return current && current[kStreamingEnvelopeId] === undefined ? current : null;
|
|
2117
|
+
}
|
|
2118
|
+
/** Release a settled block from both the keyed map and the current slot. */
|
|
2119
|
+
function releaseStreamedCall(state, block) {
|
|
2120
|
+
const envelopeId = block[kStreamingEnvelopeId];
|
|
2121
|
+
if (envelopeId)
|
|
2122
|
+
state.openToolCalls.delete(envelopeId);
|
|
2123
|
+
if (state.currentToolCall === block)
|
|
2124
|
+
state.setToolCall(null);
|
|
2125
|
+
}
|
|
2126
|
+
function selectConnectScmCall(toolCall) {
|
|
2127
|
+
const oneof = toolCall?.tool;
|
|
2128
|
+
if (oneof?.case === "connectScmToolCall")
|
|
2129
|
+
return oneof.value;
|
|
2130
|
+
return toolCall?.connectScmToolCall;
|
|
2131
|
+
}
|
|
2132
|
+
function selectConnectScmRepository(call) {
|
|
2133
|
+
const target = call?.args?.target;
|
|
2134
|
+
if (target?.case === "github")
|
|
2135
|
+
return target.value?.repository;
|
|
2136
|
+
return call?.args?.github?.repository;
|
|
2137
|
+
}
|
|
2138
|
+
/** Render a settled `ConnectScmResult` as the text of its paired tool result. */
|
|
2139
|
+
function describeConnectScmResult(call) {
|
|
2140
|
+
const result = call?.result?.result;
|
|
2141
|
+
switch (result?.case) {
|
|
2142
|
+
case "success":
|
|
2143
|
+
return { text: "SCM connected", isError: false };
|
|
2144
|
+
case "error":
|
|
2145
|
+
return { text: result.value?.error || "SCM connection failed", isError: true };
|
|
2146
|
+
case "rejected":
|
|
2147
|
+
return { text: result.value?.reason || "SCM connection rejected", isError: true };
|
|
2148
|
+
default:
|
|
2149
|
+
return { text: "SCM connection reported no result", isError: true };
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
/**
|
|
2153
|
+
* Extract the authoritative full todo list from a completed native todo call.
|
|
2154
|
+
* Only `result.success.todos` is authoritative; filtered/partial/ambiguous
|
|
2155
|
+
* responses return `null`, which the caller MUST treat as "nothing to mirror".
|
|
2156
|
+
*/
|
|
2157
|
+
function extractTodoSnapshot(toolCall) {
|
|
2158
|
+
const { update, read } = selectTodoCalls(toolCall);
|
|
2159
|
+
if (read && ((read.args?.statusFilter?.length ?? 0) > 0 || (read.args?.idFilter?.length ?? 0) > 0)) {
|
|
2160
|
+
return null;
|
|
2161
|
+
}
|
|
2162
|
+
const call = update ?? read;
|
|
2163
|
+
if (!call)
|
|
2164
|
+
return null;
|
|
2165
|
+
const result = call.result?.result;
|
|
2166
|
+
if (result?.case !== "success")
|
|
2167
|
+
return null;
|
|
2168
|
+
const todos = result.value?.todos;
|
|
2169
|
+
if (!todos)
|
|
2170
|
+
return null;
|
|
2171
|
+
const totalCount = result.value?.totalCount;
|
|
2172
|
+
if (typeof totalCount === "number" && totalCount !== todos.length) {
|
|
2173
|
+
return null;
|
|
2174
|
+
}
|
|
2175
|
+
if (read && todos.length === 0) {
|
|
2176
|
+
return null;
|
|
2177
|
+
}
|
|
2178
|
+
const mapped = mapTodoSnapshot(todos);
|
|
2179
|
+
if (mapped.some((todo) => todo.content.length === 0))
|
|
2180
|
+
return null;
|
|
2181
|
+
return {
|
|
2182
|
+
todos: mapped,
|
|
2183
|
+
merged: result.value?.wasMerge === true,
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
/** Error text when the server itself rejected the call. */
|
|
2187
|
+
function extractTodoError(toolCall) {
|
|
2188
|
+
const { update, read } = selectTodoCalls(toolCall);
|
|
2189
|
+
const result = (update ?? read)?.result?.result;
|
|
2190
|
+
if (result?.case !== "error")
|
|
2191
|
+
return null;
|
|
2192
|
+
const error = result.value?.error;
|
|
2193
|
+
return typeof error === "string" && error.length > 0 ? error : "Todo operation failed";
|
|
2194
|
+
}
|
|
2195
|
+
/** Args echoed onto the synthesized display block, for rendering only. */
|
|
2196
|
+
function buildTodoDisplayArgs(toolCall) {
|
|
2197
|
+
const args = selectTodoCalls(toolCall).update?.args;
|
|
2198
|
+
return {
|
|
2199
|
+
todos: args?.todos ? mapTodoSnapshot(args.todos) : [],
|
|
2200
|
+
merge: args?.merge === true ? true : undefined,
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Paired result for a server-resolved native todo call. Nothing else would
|
|
2205
|
+
* produce a `toolResult` for the block — and transcript rebuilds strip any
|
|
2206
|
+
* `toolCall` left unpaired.
|
|
2207
|
+
*/
|
|
2208
|
+
function buildTodoToolResult(toolCallId, snapshot, error) {
|
|
2209
|
+
const text = error ?? (snapshot ? formatTodoSnapshotSummary(snapshot.todos) : "Todo snapshot not mirrored");
|
|
2210
|
+
return {
|
|
2211
|
+
role: "toolResult",
|
|
2212
|
+
toolCallId,
|
|
2213
|
+
toolName: "todo",
|
|
2214
|
+
content: [{ type: "text", text }],
|
|
2215
|
+
isError: error !== null,
|
|
2216
|
+
timestamp: Date.now(),
|
|
2217
|
+
};
|
|
2218
|
+
}
|
|
2219
|
+
function formatTodoSnapshotSummary(todos) {
|
|
2220
|
+
if (todos.length === 0)
|
|
2221
|
+
return "No todos";
|
|
2222
|
+
const done = todos.filter((todo) => todo.status === "completed").length;
|
|
2223
|
+
return `${done}/${todos.length} tasks completed`;
|
|
2224
|
+
}
|
|
2225
|
+
function buildMcpResultFromToolResult(_mcpCall, toolResult) {
|
|
2226
|
+
if (toolResult.isError) {
|
|
2227
|
+
return buildMcpErrorResult(toolResultToText(toolResult) || "MCP tool failed");
|
|
2228
|
+
}
|
|
2229
|
+
const content = toolResult.content.map((item) => {
|
|
2230
|
+
if (item.type === "image") {
|
|
2231
|
+
return create(McpToolResultContentItemSchema, {
|
|
2232
|
+
content: {
|
|
2233
|
+
case: "image",
|
|
2234
|
+
value: create(McpImageContentSchema, {
|
|
2235
|
+
data: Uint8Array.from(Buffer.from(item.data, "base64")),
|
|
2236
|
+
mimeType: item.mimeType,
|
|
2237
|
+
}),
|
|
2238
|
+
},
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
return create(McpToolResultContentItemSchema, {
|
|
2242
|
+
content: {
|
|
2243
|
+
case: "text",
|
|
2244
|
+
value: create(McpTextContentSchema, { text: item.text }),
|
|
2245
|
+
},
|
|
2246
|
+
});
|
|
2247
|
+
});
|
|
2248
|
+
return create(McpResultSchema, {
|
|
2249
|
+
result: {
|
|
2250
|
+
case: "success",
|
|
2251
|
+
value: create(McpSuccessSchema, {
|
|
2252
|
+
content,
|
|
2253
|
+
isError: false,
|
|
2254
|
+
}),
|
|
2255
|
+
},
|
|
2256
|
+
});
|
|
2257
|
+
}
|
|
2258
|
+
function buildMcpToolNotFoundResult(mcpCall) {
|
|
2259
|
+
return create(McpResultSchema, {
|
|
2260
|
+
result: {
|
|
2261
|
+
case: "toolNotFound",
|
|
2262
|
+
value: create(McpToolNotFoundSchema, { name: mcpCall.toolName, availableTools: [] }),
|
|
2263
|
+
},
|
|
2264
|
+
});
|
|
2265
|
+
}
|
|
2266
|
+
function buildMcpErrorResult(error) {
|
|
2267
|
+
return create(McpResultSchema, {
|
|
2268
|
+
result: {
|
|
2269
|
+
case: "error",
|
|
2270
|
+
value: create(McpErrorSchema, { error }),
|
|
2271
|
+
},
|
|
2272
|
+
});
|
|
2273
|
+
}
|
|
2274
|
+
/**
|
|
2275
|
+
* Merge the decoded completion-frame `McpArgs` map into the args assembled
|
|
2276
|
+
* from streamed `args_text_delta` snapshots.
|
|
2277
|
+
*
|
|
2278
|
+
* The completion frame is authoritative for the scalars it carries — but it
|
|
2279
|
+
* can omit oversized parameters entirely and can downgrade a structured value
|
|
2280
|
+
* to its raw string fallback. Rules per key:
|
|
2281
|
+
* - completion key absent → keep the streamed value.
|
|
2282
|
+
* - completion is a string while the streamed value is structured → keep the
|
|
2283
|
+
* streamed value (the completion frame downgraded it).
|
|
2284
|
+
* - otherwise → completion wins.
|
|
2285
|
+
*/
|
|
2286
|
+
export function mergeCursorMcpToolCallArgs(streamed, completion) {
|
|
2287
|
+
const merged = { ...(streamed ?? {}) };
|
|
2288
|
+
if (!completion)
|
|
2289
|
+
return merged;
|
|
2290
|
+
for (const [key, completionValue] of Object.entries(completion)) {
|
|
2291
|
+
const streamedValue = merged[key];
|
|
2292
|
+
if (typeof completionValue === "string" && streamedValue !== null && typeof streamedValue === "object") {
|
|
2293
|
+
continue;
|
|
2294
|
+
}
|
|
2295
|
+
merged[key] = completionValue;
|
|
2296
|
+
}
|
|
2297
|
+
return merged;
|
|
2298
|
+
}
|
|
2299
|
+
function endCurrentTextBlock(output, stream, state) {
|
|
2300
|
+
const block = state.currentTextBlock;
|
|
2301
|
+
if (!block)
|
|
2302
|
+
return;
|
|
2303
|
+
const idx = output.content.indexOf(block);
|
|
2304
|
+
stream.push({
|
|
2305
|
+
type: "text_end",
|
|
2306
|
+
contentIndex: idx,
|
|
2307
|
+
content: block.text,
|
|
2308
|
+
partial: output,
|
|
2309
|
+
});
|
|
2310
|
+
state.setTextBlock(null);
|
|
2311
|
+
}
|
|
2312
|
+
function endCurrentThinkingBlock(output, stream, state) {
|
|
2313
|
+
const block = state.currentThinkingBlock;
|
|
2314
|
+
if (!block)
|
|
2315
|
+
return;
|
|
2316
|
+
const idx = output.content.indexOf(block);
|
|
2317
|
+
stream.push({
|
|
2318
|
+
type: "thinking_end",
|
|
2319
|
+
contentIndex: idx,
|
|
2320
|
+
content: block.thinking,
|
|
2321
|
+
partial: output,
|
|
2322
|
+
});
|
|
2323
|
+
state.setThinkingBlock(null);
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Synthesize a completed `toolCall` content block for a Cursor exec-channel
|
|
2327
|
+
* native tool or for an MCP exec frame whose corresponding interaction block
|
|
2328
|
+
* is absent.
|
|
2329
|
+
*
|
|
2330
|
+
* Args arrive complete on the exec message, so the block opens and closes in
|
|
2331
|
+
* one step. Without this the persisted assistant message carries only
|
|
2332
|
+
* text/thinking blocks, and on replay the following `toolResult` messages
|
|
2333
|
+
* have no matching `toolCall.id`.
|
|
2334
|
+
*
|
|
2335
|
+
* The block is stamped with {@link kCursorExecResolved} so the agent loop's
|
|
2336
|
+
* execution pass skips it — Cursor's server-driven exec channel already ran
|
|
2337
|
+
* the tool via the bridge and buffered the result. Exported for tests.
|
|
2338
|
+
*/
|
|
2339
|
+
export function synthesizeCursorExecToolCall(output, stream, state, toolCallId, toolName, args) {
|
|
2340
|
+
endCurrentTextBlock(output, stream, state);
|
|
2341
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
2342
|
+
const block = {
|
|
2343
|
+
type: "toolCall",
|
|
2344
|
+
id: toolCallId,
|
|
2345
|
+
name: toolName,
|
|
2346
|
+
arguments: omitUndefinedArgs(args),
|
|
2347
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2348
|
+
[kStreamingBlockKind]: "cursor-exec",
|
|
2349
|
+
[kCursorExecResolved]: true,
|
|
2350
|
+
};
|
|
2351
|
+
output.content.push(block);
|
|
2352
|
+
const idx = output.content.length - 1;
|
|
2353
|
+
stream.push({ type: "toolcall_start", contentIndex: idx, partial: output });
|
|
2354
|
+
stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: block, partial: output });
|
|
2355
|
+
}
|
|
2356
|
+
/**
|
|
2357
|
+
* Pair a `toolResult` for a synthesized block the client answered itself,
|
|
2358
|
+
* without ever consulting a handler. Frames answered from a fixed verdict —
|
|
2359
|
+
* no handler, no local execution — still need the pair: the block was stamped
|
|
2360
|
+
* {@link kCursorExecResolved}, so the agent loop emits no placeholder for it
|
|
2361
|
+
* and transcript rebuilds strip an unpaired call.
|
|
2362
|
+
*/
|
|
2363
|
+
async function pairSynthesizedExecResult(state, onToolResult, toolCallId, toolName, text, isError = true) {
|
|
2364
|
+
const synthesized = {
|
|
2365
|
+
role: "toolResult",
|
|
2366
|
+
toolCallId,
|
|
2367
|
+
toolName,
|
|
2368
|
+
content: [{ type: "text", text }],
|
|
2369
|
+
isError,
|
|
2370
|
+
timestamp: Date.now(),
|
|
2371
|
+
};
|
|
2372
|
+
const sink = onToolResult ?? state.onToolResult;
|
|
2373
|
+
if (!sink)
|
|
2374
|
+
return;
|
|
2375
|
+
await sink(synthesized);
|
|
2376
|
+
}
|
|
2377
|
+
/**
|
|
2378
|
+
* Throttle threshold for mid-stream argument JSON parses: reparse only when
|
|
2379
|
+
* the buffer grew by this much since the last successful parse, keeping total
|
|
2380
|
+
* parse work bounded while the authoritative full parse runs on completion.
|
|
2381
|
+
*/
|
|
2382
|
+
const STREAMING_ARGS_REPARSE_DELTA = 1024;
|
|
2383
|
+
/** Exported for tests: drives one Cursor interaction update through the streaming state machine. */
|
|
2384
|
+
export function processInteractionUpdate(update, output, stream, state, usageState) {
|
|
2385
|
+
const updateCase = update.message?.case;
|
|
2386
|
+
log("interactionUpdate", updateCase);
|
|
2387
|
+
if (updateCase === "textDelta") {
|
|
2388
|
+
const delta = update.message.value.text || "";
|
|
2389
|
+
if (!state.currentTextBlock) {
|
|
2390
|
+
const block = {
|
|
2391
|
+
type: "text",
|
|
2392
|
+
text: "",
|
|
2393
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2394
|
+
};
|
|
2395
|
+
output.content.push(block);
|
|
2396
|
+
state.setTextBlock(block);
|
|
2397
|
+
stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
|
|
2398
|
+
}
|
|
2399
|
+
state.currentTextBlock.text += delta;
|
|
2400
|
+
const idx = output.content.indexOf(state.currentTextBlock);
|
|
2401
|
+
stream.push({ type: "text_delta", contentIndex: idx, delta, partial: output });
|
|
2402
|
+
}
|
|
2403
|
+
else if (updateCase === "thinkingDelta") {
|
|
2404
|
+
const delta = update.message.value.text || "";
|
|
2405
|
+
if (!state.currentThinkingBlock) {
|
|
2406
|
+
const block = {
|
|
2407
|
+
type: "thinking",
|
|
2408
|
+
thinking: "",
|
|
2409
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2410
|
+
};
|
|
2411
|
+
output.content.push(block);
|
|
2412
|
+
state.setThinkingBlock(block);
|
|
2413
|
+
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
2414
|
+
}
|
|
2415
|
+
state.currentThinkingBlock.thinking += delta;
|
|
2416
|
+
const idx = output.content.indexOf(state.currentThinkingBlock);
|
|
2417
|
+
stream.push({ type: "thinking_delta", contentIndex: idx, delta, partial: output });
|
|
2418
|
+
}
|
|
2419
|
+
else if (updateCase === "thinkingCompleted") {
|
|
2420
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
2421
|
+
}
|
|
2422
|
+
else if (updateCase === "toolCallStarted" && selectConnectScmCall(update.message.value.toolCall)) {
|
|
2423
|
+
// `connect_scm` is resolved entirely server-side and has NO exec frame:
|
|
2424
|
+
// the streamed pair is the only signal this client sees. The
|
|
2425
|
+
// authoritative outcome rides on the COMPLETION's `result` oneof, so the
|
|
2426
|
+
// block is opened here and settled there.
|
|
2427
|
+
endCurrentTextBlock(output, stream, state);
|
|
2428
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
2429
|
+
const scmCall = selectConnectScmCall(update.message.value.toolCall);
|
|
2430
|
+
const repository = selectConnectScmRepository(scmCall);
|
|
2431
|
+
const block = {
|
|
2432
|
+
type: "toolCall",
|
|
2433
|
+
id: scmCall?.args?.toolCallId || update.message.value.callId || randomUUID(),
|
|
2434
|
+
name: "connect_scm",
|
|
2435
|
+
arguments: repository ? { owner: repository.owner, repo: repository.repo } : {},
|
|
2436
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2437
|
+
[kStreamingBlockKind]: "connect-scm",
|
|
2438
|
+
[kStreamingEnvelopeId]: update.message.value.callId || undefined,
|
|
2439
|
+
[kCursorExecResolved]: true,
|
|
2440
|
+
};
|
|
2441
|
+
output.content.push(block);
|
|
2442
|
+
retainStreamedCall(state, block, update.message.value.callId);
|
|
2443
|
+
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
2444
|
+
}
|
|
2445
|
+
else if (updateCase === "toolCallStarted" && isExecOwnedToolCall(update.message.value.toolCall)) {
|
|
2446
|
+
// The exec channel already synthesized this block (and marked it
|
|
2447
|
+
// resolved) when it ran the tool locally, so the streamed announcement
|
|
2448
|
+
// must not create a second one.
|
|
2449
|
+
endCurrentTextBlock(output, stream, state);
|
|
2450
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
2451
|
+
log("exec", "streamedToolCallOwnedByExec", { case: update.message.value.toolCall?.tool?.case });
|
|
2452
|
+
}
|
|
2453
|
+
else if (updateCase === "toolCallStarted") {
|
|
2454
|
+
endCurrentTextBlock(output, stream, state);
|
|
2455
|
+
endCurrentThinkingBlock(output, stream, state);
|
|
2456
|
+
const toolCall = update.message.value.toolCall;
|
|
2457
|
+
if (toolCall) {
|
|
2458
|
+
const mcpCall = selectMcpCall(toolCall);
|
|
2459
|
+
if (mcpCall) {
|
|
2460
|
+
const args = mcpCall.args || {};
|
|
2461
|
+
const id = args.toolCallId || randomUUID();
|
|
2462
|
+
const resolvedByExec = state.resolvedMcpToolCallIds.delete(id);
|
|
2463
|
+
if (resolvedByExec && output.content.some((block) => block.type === "toolCall" && block.id === id)) {
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
const block = {
|
|
2467
|
+
type: "toolCall",
|
|
2468
|
+
id,
|
|
2469
|
+
// Same precedence as `decodeMcpCall` (`toolName || name`), which
|
|
2470
|
+
// is what the exec channel pairs its result under.
|
|
2471
|
+
name: args.toolName || args.name || "",
|
|
2472
|
+
arguments: {},
|
|
2473
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2474
|
+
[kStreamingPartialJson]: "",
|
|
2475
|
+
[kStreamingBlockKind]: "mcp",
|
|
2476
|
+
[kStreamingEnvelopeId]: update.message.value.callId || undefined,
|
|
2477
|
+
};
|
|
2478
|
+
if (resolvedByExec) {
|
|
2479
|
+
markCursorExecResolved(block);
|
|
2480
|
+
}
|
|
2481
|
+
output.content.push(block);
|
|
2482
|
+
retainStreamedCall(state, block, update.message.value.callId);
|
|
2483
|
+
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
2484
|
+
return;
|
|
2485
|
+
}
|
|
2486
|
+
// Cursor resolves `update_todos` / `read_todos` server-side and
|
|
2487
|
+
// settles them on the tool call's `result`. Both blocks are stamped
|
|
2488
|
+
// resolved so the agent loop never runs them locally.
|
|
2489
|
+
const todoCalls = selectTodoCalls(toolCall);
|
|
2490
|
+
if (todoCalls.update || todoCalls.read) {
|
|
2491
|
+
const callId = update.message.value.callId || randomUUID();
|
|
2492
|
+
const block = {
|
|
2493
|
+
type: "toolCall",
|
|
2494
|
+
id: callId,
|
|
2495
|
+
name: "todo",
|
|
2496
|
+
arguments: buildTodoDisplayArgs(toolCall),
|
|
2497
|
+
[kStreamingBlockIndex]: output.content.length,
|
|
2498
|
+
[kStreamingBlockKind]: "todo",
|
|
2499
|
+
[kStreamingEnvelopeId]: update.message.value.callId || undefined,
|
|
2500
|
+
[kCursorExecResolved]: true,
|
|
2501
|
+
};
|
|
2502
|
+
output.content.push(block);
|
|
2503
|
+
retainStreamedCall(state, block, update.message.value.callId);
|
|
2504
|
+
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
else if (updateCase === "toolCallDelta" || updateCase === "partialToolCall") {
|
|
2509
|
+
// An argument delta belonging to a different call must not be appended
|
|
2510
|
+
// to this block's buffer, which would corrupt the JSON both parse.
|
|
2511
|
+
const target = resolveStreamedCall(state, update.message.value.callId);
|
|
2512
|
+
if (target?.[kStreamingBlockKind] === "mcp") {
|
|
2513
|
+
// Cursor's `args_text_delta` is "aggregated args text so far": each
|
|
2514
|
+
// delta is a cumulative snapshot. Strip the prefix we already have to
|
|
2515
|
+
// recover the new suffix; fall back to treating the value as an
|
|
2516
|
+
// incremental fragment when it doesn't extend the buffer.
|
|
2517
|
+
const snapshot = update.message.value.argsTextDelta || "";
|
|
2518
|
+
const current = target[kStreamingPartialJson] ?? "";
|
|
2519
|
+
const chunk = snapshot.startsWith(current) ? snapshot.slice(current.length) : snapshot;
|
|
2520
|
+
if (chunk.length === 0) {
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
const nextBuffer = current + chunk;
|
|
2524
|
+
target[kStreamingPartialJson] = nextBuffer;
|
|
2525
|
+
// Throttle mid-stream parses to keep total parse work bounded; the
|
|
2526
|
+
// authoritative full parse runs in `toolCallCompleted`.
|
|
2527
|
+
const lastParseLen = target[kStreamingLastParseLen] ?? 0;
|
|
2528
|
+
if (nextBuffer.length - lastParseLen >= STREAMING_ARGS_REPARSE_DELTA) {
|
|
2529
|
+
target.arguments = parseStreamingJson(nextBuffer);
|
|
2530
|
+
target[kStreamingLastParseLen] = nextBuffer.length;
|
|
2531
|
+
}
|
|
2532
|
+
const idx = output.content.indexOf(target);
|
|
2533
|
+
stream.push({ type: "toolcall_delta", contentIndex: idx, delta: chunk, partial: output });
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
else if (updateCase === "toolCallCompleted") {
|
|
2537
|
+
// Correlate on the envelope's `call_id`, NOT the block id: MCP, Pi and
|
|
2538
|
+
// SCM blocks are filed under the id inside the call's `args`, and that
|
|
2539
|
+
// need not equal the envelope id.
|
|
2540
|
+
const settled = resolveStreamedCall(state, update.message.value.callId);
|
|
2541
|
+
if (settled) {
|
|
2542
|
+
const toolCall = update.message.value.toolCall;
|
|
2543
|
+
if (settled[kStreamingBlockKind] === "mcp") {
|
|
2544
|
+
// Authoritative full parse of the accumulated argument buffer.
|
|
2545
|
+
const partial = settled[kStreamingPartialJson];
|
|
2546
|
+
if (partial !== undefined) {
|
|
2547
|
+
settled.arguments = parseStreamingJson(partial);
|
|
2548
|
+
}
|
|
2549
|
+
const decodedArgs = decodeMcpArgsMap(selectMcpCall(toolCall)?.args?.args);
|
|
2550
|
+
settled.arguments = mergeCursorMcpToolCallArgs(settled.arguments, decodedArgs);
|
|
2551
|
+
}
|
|
2552
|
+
else if (settled[kStreamingBlockKind] === "connect-scm") {
|
|
2553
|
+
// The authoritative outcome arrives only here. Late args are merged
|
|
2554
|
+
// too — a start frame may announce the call before the target
|
|
2555
|
+
// repository is known.
|
|
2556
|
+
const scmCall = selectConnectScmCall(toolCall);
|
|
2557
|
+
const repository = selectConnectScmRepository(scmCall);
|
|
2558
|
+
if (repository) {
|
|
2559
|
+
settled.arguments = { owner: repository.owner, repo: repository.repo };
|
|
2560
|
+
}
|
|
2561
|
+
const { text, isError } = describeConnectScmResult(scmCall);
|
|
2562
|
+
void state.onToolResult?.({
|
|
2563
|
+
role: "toolResult",
|
|
2564
|
+
toolCallId: settled.id,
|
|
2565
|
+
toolName: "connect_scm",
|
|
2566
|
+
content: [{ type: "text", text }],
|
|
2567
|
+
isError,
|
|
2568
|
+
timestamp: Date.now(),
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
else if (settled[kStreamingBlockKind] === "todo") {
|
|
2572
|
+
// Only the server's success snapshot is authoritative. A completion
|
|
2573
|
+
// frame whose optional `toolCall` is absent still settles: the block
|
|
2574
|
+
// is already marked resolved, so nothing downstream pairs it.
|
|
2575
|
+
const snapshot = toolCall ? extractTodoSnapshot(toolCall) : null;
|
|
2576
|
+
const error = toolCall ? extractTodoError(toolCall) : null;
|
|
2577
|
+
if (snapshot) {
|
|
2578
|
+
settled.arguments = { todos: snapshot.todos, merged: snapshot.merged };
|
|
2579
|
+
}
|
|
2580
|
+
void state.onToolResult?.(buildTodoToolResult(settled.id, snapshot, error));
|
|
2581
|
+
}
|
|
2582
|
+
const idx = output.content.indexOf(settled);
|
|
2583
|
+
clearStreamingPartialJson(settled);
|
|
2584
|
+
stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: settled, partial: output });
|
|
2585
|
+
releaseStreamedCall(state, settled);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
else if (updateCase === "turnEnded") {
|
|
2589
|
+
output.stopReason = "stop";
|
|
2590
|
+
}
|
|
2591
|
+
else if (updateCase === "tokenDelta") {
|
|
2592
|
+
const tokenDelta = update.message.value;
|
|
2593
|
+
usageState.sawTokenDelta = true;
|
|
2594
|
+
output.usage.output += tokenDelta.tokens || 0;
|
|
2595
|
+
output.usage.totalTokens = output.usage.input + output.usage.output;
|
|
2596
|
+
}
|
|
2597
|
+
}
|
|
2598
|
+
function createBlobId(data) {
|
|
2599
|
+
return new Uint8Array(createHash("sha256").update(data).digest());
|
|
2600
|
+
}
|
|
2601
|
+
function storeCursorBlob(blobStore, data) {
|
|
2602
|
+
const blobId = createBlobId(data);
|
|
2603
|
+
blobStore.set(Buffer.from(blobId).toString("hex"), data);
|
|
2604
|
+
return blobId;
|
|
2605
|
+
}
|
|
2606
|
+
function readCursorBlob(blobStore, blobId) {
|
|
2607
|
+
const data = blobStore.get(Buffer.from(blobId).toString("hex"));
|
|
2608
|
+
if (!data) {
|
|
2609
|
+
throw new Error("Cursor blob not found");
|
|
2610
|
+
}
|
|
2611
|
+
return data;
|
|
2612
|
+
}
|
|
2613
|
+
/**
|
|
2614
|
+
* Local tools Cursor already drives natively over the exec channel, so
|
|
2615
|
+
* advertising them again as MCP tools would give the model two ways to call
|
|
2616
|
+
* the same thing.
|
|
2617
|
+
*/
|
|
2618
|
+
const CURSOR_NATIVE_TOOL_NAMES = new Set(["bash", "read", "write", "delete", "ls", "grep", "todo"]);
|
|
2619
|
+
/** Strip TypeBox symbol metadata down to a plain JSON Schema document. */
|
|
2620
|
+
function toolParametersToJsonSchema(tool) {
|
|
2621
|
+
try {
|
|
2622
|
+
return JSON.parse(JSON.stringify(tool.parameters));
|
|
2623
|
+
}
|
|
2624
|
+
catch {
|
|
2625
|
+
return { type: "object", properties: {}, required: [] };
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
export function buildMcpToolDefinitions(tools) {
|
|
2629
|
+
if (!tools || tools.length === 0) {
|
|
2630
|
+
return [];
|
|
2631
|
+
}
|
|
2632
|
+
const advertisedTools = tools.filter((tool) => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name));
|
|
2633
|
+
if (advertisedTools.length === 0) {
|
|
2634
|
+
return [];
|
|
2635
|
+
}
|
|
2636
|
+
return advertisedTools.map((tool) => {
|
|
2637
|
+
const jsonSchema = toolParametersToJsonSchema(tool);
|
|
2638
|
+
const schemaValue = jsonSchema && typeof jsonSchema === "object"
|
|
2639
|
+
? jsonSchema
|
|
2640
|
+
: { type: "object", properties: {}, required: [] };
|
|
2641
|
+
const inputSchema = toBinary(ValueSchema, fromJson(ValueSchema, schemaValue));
|
|
2642
|
+
return create(McpToolDefinitionSchema, {
|
|
2643
|
+
name: tool.name,
|
|
2644
|
+
description: tool.description || "",
|
|
2645
|
+
providerIdentifier: "pi-agent",
|
|
2646
|
+
toolName: tool.name,
|
|
2647
|
+
inputSchema,
|
|
2648
|
+
});
|
|
2649
|
+
});
|
|
2650
|
+
}
|
|
2651
|
+
/** Extract text content from a user message. */
|
|
2652
|
+
function extractUserMessageText(msg) {
|
|
2653
|
+
if (msg.role !== "user")
|
|
2654
|
+
return "";
|
|
2655
|
+
const content = msg.content;
|
|
2656
|
+
if (typeof content === "string")
|
|
2657
|
+
return content.trim();
|
|
2658
|
+
const text = content
|
|
2659
|
+
.filter((c) => c.type === "text")
|
|
2660
|
+
.map((c) => c.text)
|
|
2661
|
+
.join("\n");
|
|
2662
|
+
return text.trim();
|
|
2663
|
+
}
|
|
2664
|
+
function hasUserMessageImages(msg) {
|
|
2665
|
+
return msg.role === "user" && Array.isArray(msg.content) && msg.content.some((item) => item.type === "image");
|
|
2666
|
+
}
|
|
2667
|
+
function buildCursorRootPromptContent(content) {
|
|
2668
|
+
if (typeof content === "string") {
|
|
2669
|
+
const text = content.trim();
|
|
2670
|
+
return text ? [{ type: "text", text }] : [];
|
|
2671
|
+
}
|
|
2672
|
+
const parts = [];
|
|
2673
|
+
for (const item of content) {
|
|
2674
|
+
if (item.type === "text") {
|
|
2675
|
+
const text = item.text.trim();
|
|
2676
|
+
if (text) {
|
|
2677
|
+
parts.push({ type: "text", text });
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
else {
|
|
2681
|
+
parts.push({ type: "image", image: `data:${item.mimeType};base64,${item.data}`, mediaType: item.mimeType });
|
|
2682
|
+
}
|
|
2683
|
+
}
|
|
2684
|
+
return parts;
|
|
2685
|
+
}
|
|
2686
|
+
function cursorUserContentKey(content) {
|
|
2687
|
+
if (typeof content === "string") {
|
|
2688
|
+
return content.trim();
|
|
2689
|
+
}
|
|
2690
|
+
const hash = createHash("sha256");
|
|
2691
|
+
for (const item of content) {
|
|
2692
|
+
hash.update(item.type);
|
|
2693
|
+
if (item.type === "text") {
|
|
2694
|
+
hash.update(item.text);
|
|
2695
|
+
}
|
|
2696
|
+
else {
|
|
2697
|
+
hash.update(item.mimeType);
|
|
2698
|
+
hash.update(item.data);
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
return hash.digest("hex");
|
|
2702
|
+
}
|
|
2703
|
+
function buildCursorAssistantContent(msg) {
|
|
2704
|
+
const content = [];
|
|
2705
|
+
for (const item of msg.content) {
|
|
2706
|
+
if (item.type === "text") {
|
|
2707
|
+
if (item.text)
|
|
2708
|
+
content.push({ type: "text", text: item.text });
|
|
2709
|
+
}
|
|
2710
|
+
else if (item.type === "toolCall") {
|
|
2711
|
+
content.push({
|
|
2712
|
+
type: "tool-call",
|
|
2713
|
+
toolCallId: item.id,
|
|
2714
|
+
toolName: item.name,
|
|
2715
|
+
args: item.arguments,
|
|
2716
|
+
});
|
|
2717
|
+
}
|
|
2718
|
+
// Thinking is never replayed: Cursor manages reasoning server-side and
|
|
2719
|
+
// foreign/hidden reasoning must not leak into history as native thinking.
|
|
2720
|
+
}
|
|
2721
|
+
return content;
|
|
2722
|
+
}
|
|
2723
|
+
/**
|
|
2724
|
+
* Index of the last user message in `messages`, or -1 if none. Used to
|
|
2725
|
+
* exclude the current user turn from history builders — it goes in the
|
|
2726
|
+
* `userMessageAction`, not in history structures.
|
|
2727
|
+
*/
|
|
2728
|
+
function findLastUserMessageIndex(messages) {
|
|
2729
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
2730
|
+
if (messages[i].role === "user") {
|
|
2731
|
+
return i;
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
return -1;
|
|
2735
|
+
}
|
|
2736
|
+
/**
|
|
2737
|
+
* Build one Cursor system-message JSON blob per system prompt. When no system
|
|
2738
|
+
* prompt is provided, returns a single default greeting so we never emit an
|
|
2739
|
+
* empty `rootPromptMessagesJson` head.
|
|
2740
|
+
*/
|
|
2741
|
+
export function buildCursorSystemPromptJsons(systemPrompt) {
|
|
2742
|
+
const trimmed = systemPrompt?.trim();
|
|
2743
|
+
if (!trimmed) {
|
|
2744
|
+
return [JSON.stringify({ role: "system", content: "You are a helpful assistant." })];
|
|
2745
|
+
}
|
|
2746
|
+
return [JSON.stringify({ role: "system", content: trimmed })];
|
|
2747
|
+
}
|
|
2748
|
+
/**
|
|
2749
|
+
* Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
|
|
2750
|
+
* system prompt plus prior conversation history, as JSON blobs matching
|
|
2751
|
+
* Cursor's internal Vercel-AI-SDK-shaped message format.
|
|
2752
|
+
*
|
|
2753
|
+
* Cursor's server uses `rootPromptMessagesJson` (not `turns[]`) to build the
|
|
2754
|
+
* actual model prompt; `turns[]` is UI/display metadata. The active user
|
|
2755
|
+
* message is excluded because it is sent in the action.
|
|
2756
|
+
*/
|
|
2757
|
+
function buildRootPromptMessagesJson(messages, systemPromptIds, blobStore, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
|
|
2758
|
+
const entries = [...systemPromptIds];
|
|
2759
|
+
const pushJson = (obj) => {
|
|
2760
|
+
const bytes = new TextEncoder().encode(JSON.stringify(obj));
|
|
2761
|
+
entries.push(storeCursorBlob(blobStore, bytes));
|
|
2762
|
+
};
|
|
2763
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2764
|
+
if (i === activeUserMessageIndex)
|
|
2765
|
+
break;
|
|
2766
|
+
const msg = messages[i];
|
|
2767
|
+
if (msg.role === "user") {
|
|
2768
|
+
const content = buildCursorRootPromptContent(msg.content);
|
|
2769
|
+
if (content.length === 0)
|
|
2770
|
+
continue;
|
|
2771
|
+
pushJson({ role: "user", content });
|
|
2772
|
+
}
|
|
2773
|
+
else if (msg.role === "assistant") {
|
|
2774
|
+
const content = buildCursorAssistantContent(msg);
|
|
2775
|
+
if (content.length === 0)
|
|
2776
|
+
continue;
|
|
2777
|
+
pushJson({ role: "assistant", content });
|
|
2778
|
+
}
|
|
2779
|
+
else if (msg.role === "toolResult") {
|
|
2780
|
+
// Emit even when the result text is empty: the assistant `tool-call`
|
|
2781
|
+
// is already in history, so dropping the pair would replay an
|
|
2782
|
+
// orphaned call.
|
|
2783
|
+
pushJson({
|
|
2784
|
+
role: "tool",
|
|
2785
|
+
id: msg.toolCallId,
|
|
2786
|
+
content: [
|
|
2787
|
+
{
|
|
2788
|
+
type: "tool-result",
|
|
2789
|
+
toolName: msg.toolName,
|
|
2790
|
+
toolCallId: msg.toolCallId,
|
|
2791
|
+
result: toolResultToText(msg),
|
|
2792
|
+
...(msg.isError ? { isError: true } : {}),
|
|
2793
|
+
},
|
|
2794
|
+
],
|
|
2795
|
+
});
|
|
2796
|
+
}
|
|
2797
|
+
}
|
|
2798
|
+
return entries;
|
|
2799
|
+
}
|
|
2800
|
+
function isPlainRecord(value) {
|
|
2801
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
2802
|
+
return false;
|
|
2803
|
+
const prototype = Object.getPrototypeOf(value);
|
|
2804
|
+
return prototype === Object.prototype || prototype === null;
|
|
2805
|
+
}
|
|
2806
|
+
function isJsonValue(value) {
|
|
2807
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
2808
|
+
return true;
|
|
2809
|
+
if (typeof value === "number")
|
|
2810
|
+
return Number.isFinite(value);
|
|
2811
|
+
if (Array.isArray(value))
|
|
2812
|
+
return value.every(isJsonValue);
|
|
2813
|
+
if (!isPlainRecord(value))
|
|
2814
|
+
return false;
|
|
2815
|
+
for (const key in value) {
|
|
2816
|
+
if (!isJsonValue(value[key]))
|
|
2817
|
+
return false;
|
|
2818
|
+
}
|
|
2819
|
+
return true;
|
|
2820
|
+
}
|
|
2821
|
+
function encodeCursorMcpArguments(toolCall) {
|
|
2822
|
+
const encoded = {};
|
|
2823
|
+
for (const name in toolCall.arguments) {
|
|
2824
|
+
const value = toolCall.arguments[name];
|
|
2825
|
+
if (value === undefined)
|
|
2826
|
+
continue;
|
|
2827
|
+
if (!isJsonValue(value)) {
|
|
2828
|
+
throw new Error(`Cursor tool argument ${toolCall.name}.${name} is not JSON-serializable`);
|
|
2829
|
+
}
|
|
2830
|
+
encoded[name] = toBinary(ValueSchema, fromJson(ValueSchema, value));
|
|
2831
|
+
}
|
|
2832
|
+
return encoded;
|
|
2833
|
+
}
|
|
2834
|
+
function createCursorMcpResult(result) {
|
|
2835
|
+
if (result.isError) {
|
|
2836
|
+
return create(McpToolResultSchema, {
|
|
2837
|
+
result: {
|
|
2838
|
+
case: "error",
|
|
2839
|
+
value: create(McpToolErrorSchema, { error: toolResultToText(result) }),
|
|
2840
|
+
},
|
|
2841
|
+
});
|
|
2842
|
+
}
|
|
2843
|
+
return create(McpToolResultSchema, {
|
|
2844
|
+
result: {
|
|
2845
|
+
case: "success",
|
|
2846
|
+
value: create(McpSuccessSchema, {
|
|
2847
|
+
content: result.content.map((item) => item.type === "text"
|
|
2848
|
+
? create(McpToolResultContentItemSchema, {
|
|
2849
|
+
content: { case: "text", value: create(McpTextContentSchema, { text: item.text }) },
|
|
2850
|
+
})
|
|
2851
|
+
: create(McpToolResultContentItemSchema, {
|
|
2852
|
+
content: {
|
|
2853
|
+
case: "image",
|
|
2854
|
+
value: create(McpImageContentSchema, {
|
|
2855
|
+
data: Uint8Array.from(Buffer.from(item.data, "base64")),
|
|
2856
|
+
mimeType: item.mimeType,
|
|
2857
|
+
}),
|
|
2858
|
+
},
|
|
2859
|
+
})),
|
|
2860
|
+
}),
|
|
2861
|
+
},
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2864
|
+
function createCursorToolCallStep(toolCall, result) {
|
|
2865
|
+
const mcpCall = create(McpToolCallSchema, {
|
|
2866
|
+
args: create(McpArgsSchema, {
|
|
2867
|
+
name: toolCall.name,
|
|
2868
|
+
args: encodeCursorMcpArguments(toolCall),
|
|
2869
|
+
toolCallId: toolCall.id,
|
|
2870
|
+
providerIdentifier: "pi-agent",
|
|
2871
|
+
toolName: toolCall.name,
|
|
2872
|
+
}),
|
|
2873
|
+
...(result ? { result: createCursorMcpResult(result) } : {}),
|
|
2874
|
+
});
|
|
2875
|
+
return create(ConversationStepSchema, {
|
|
2876
|
+
message: {
|
|
2877
|
+
case: "toolCall",
|
|
2878
|
+
value: create(ToolCallSchema, {
|
|
2879
|
+
tool: { case: "mcpToolCall", value: mcpCall },
|
|
2880
|
+
toolCallId: toolCall.id,
|
|
2881
|
+
}),
|
|
2882
|
+
},
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
/**
|
|
2886
|
+
* Convert context.messages to Cursor's ConversationTurnStructure blob IDs.
|
|
2887
|
+
* Groups messages into turns: each turn is a user message followed by the
|
|
2888
|
+
* assistant's response. Excludes the active user message (which goes in the
|
|
2889
|
+
* action).
|
|
2890
|
+
*/
|
|
2891
|
+
function buildConversationTurns(messages, blobStore, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
|
|
2892
|
+
const turns = [];
|
|
2893
|
+
const historyEnd = activeUserMessageIndex >= 0 ? activeUserMessageIndex : messages.length;
|
|
2894
|
+
const toolResults = new Map();
|
|
2895
|
+
const pairedToolCallIds = new Set();
|
|
2896
|
+
for (let index = 0; index < historyEnd; index++) {
|
|
2897
|
+
const message = messages[index];
|
|
2898
|
+
if (message.role === "toolResult") {
|
|
2899
|
+
toolResults.set(message.toolCallId, message);
|
|
2900
|
+
}
|
|
2901
|
+
else if (message.role === "assistant") {
|
|
2902
|
+
for (const item of message.content) {
|
|
2903
|
+
if (item.type === "toolCall")
|
|
2904
|
+
pairedToolCallIds.add(item.id);
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
let i = 0;
|
|
2909
|
+
while (i < messages.length) {
|
|
2910
|
+
const msg = messages[i];
|
|
2911
|
+
if (msg.role !== "user") {
|
|
2912
|
+
i++;
|
|
2913
|
+
continue;
|
|
2914
|
+
}
|
|
2915
|
+
if (i === activeUserMessageIndex)
|
|
2916
|
+
break;
|
|
2917
|
+
const userText = extractUserMessageText(msg);
|
|
2918
|
+
if (userText.length === 0 && !hasUserMessageImages(msg)) {
|
|
2919
|
+
i++;
|
|
2920
|
+
continue;
|
|
2921
|
+
}
|
|
2922
|
+
const userMessage = createCursorUserMessage(msg.content, userText, deterministicUuid(`u:${turns.length}:${cursorUserContentKey(msg.content)}`));
|
|
2923
|
+
const userMessageBlobId = storeCursorBlob(blobStore, toBinary(UserMessageSchema, userMessage));
|
|
2924
|
+
const stepBlobIds = [];
|
|
2925
|
+
i++;
|
|
2926
|
+
while (i < messages.length && messages[i].role !== "user") {
|
|
2927
|
+
const stepMsg = messages[i];
|
|
2928
|
+
if (stepMsg.role === "assistant") {
|
|
2929
|
+
for (const item of stepMsg.content) {
|
|
2930
|
+
let step;
|
|
2931
|
+
if (item.type === "text") {
|
|
2932
|
+
if (!item.text)
|
|
2933
|
+
continue;
|
|
2934
|
+
step = create(ConversationStepSchema, {
|
|
2935
|
+
message: {
|
|
2936
|
+
case: "assistantMessage",
|
|
2937
|
+
value: create(AssistantMessageSchema, { text: item.text }),
|
|
2938
|
+
},
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
else if (item.type === "thinking") {
|
|
2942
|
+
// Foreign/hidden reasoning never leaks into Cursor's turn
|
|
2943
|
+
// history as native thinking.
|
|
2944
|
+
continue;
|
|
2945
|
+
}
|
|
2946
|
+
else if (item.type === "toolCall") {
|
|
2947
|
+
step = createCursorToolCallStep(item, toolResults.get(item.id));
|
|
2948
|
+
}
|
|
2949
|
+
else {
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
stepBlobIds.push(storeCursorBlob(blobStore, toBinary(ConversationStepSchema, step)));
|
|
2953
|
+
}
|
|
2954
|
+
}
|
|
2955
|
+
else if (stepMsg.role === "toolResult" && !pairedToolCallIds.has(stepMsg.toolCallId)) {
|
|
2956
|
+
const text = toolResultToText(stepMsg);
|
|
2957
|
+
if (text) {
|
|
2958
|
+
const prefix = stepMsg.isError ? "[Tool Error]" : "[Tool Result]";
|
|
2959
|
+
const step = create(ConversationStepSchema, {
|
|
2960
|
+
message: {
|
|
2961
|
+
case: "assistantMessage",
|
|
2962
|
+
value: create(AssistantMessageSchema, { text: `${prefix}\n${text}` }),
|
|
2963
|
+
},
|
|
2964
|
+
});
|
|
2965
|
+
stepBlobIds.push(storeCursorBlob(blobStore, toBinary(ConversationStepSchema, step)));
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
i++;
|
|
2969
|
+
}
|
|
2970
|
+
const agentTurn = create(AgentConversationTurnStructureSchema, {
|
|
2971
|
+
userMessage: userMessageBlobId,
|
|
2972
|
+
steps: stepBlobIds,
|
|
2973
|
+
});
|
|
2974
|
+
const turn = create(ConversationTurnStructureSchema, {
|
|
2975
|
+
turn: {
|
|
2976
|
+
case: "agentConversationTurn",
|
|
2977
|
+
value: agentTurn,
|
|
2978
|
+
},
|
|
2979
|
+
});
|
|
2980
|
+
turns.push(storeCursorBlob(blobStore, toBinary(ConversationTurnStructureSchema, turn)));
|
|
2981
|
+
}
|
|
2982
|
+
return turns;
|
|
2983
|
+
}
|
|
2984
|
+
/** Exported for tests: decodes Cursor history blobs built from conversation messages. */
|
|
2985
|
+
export function buildCursorHistoryForTest(messages, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
|
|
2986
|
+
const blobStore = new Map();
|
|
2987
|
+
const rootPromptMessagesJson = buildRootPromptMessagesJson(messages, [], blobStore, activeUserMessageIndex).map((blobId) => JSON.parse(new TextDecoder().decode(readCursorBlob(blobStore, blobId))));
|
|
2988
|
+
const turnUserMessagesJson = [];
|
|
2989
|
+
const turnStepMessagesJson = [];
|
|
2990
|
+
for (const turnBlobId of buildConversationTurns(messages, blobStore, activeUserMessageIndex)) {
|
|
2991
|
+
const turn = fromBinary(ConversationTurnStructureSchema, readCursorBlob(blobStore, turnBlobId));
|
|
2992
|
+
if (turn.turn.case !== "agentConversationTurn") {
|
|
2993
|
+
continue;
|
|
2994
|
+
}
|
|
2995
|
+
const userMessage = fromBinary(UserMessageSchema, readCursorBlob(blobStore, turn.turn.value.userMessage));
|
|
2996
|
+
turnUserMessagesJson.push(toJson(UserMessageSchema, userMessage));
|
|
2997
|
+
turnStepMessagesJson.push(turn.turn.value.steps.map((stepBlobId) => {
|
|
2998
|
+
const step = fromBinary(ConversationStepSchema, readCursorBlob(blobStore, stepBlobId));
|
|
2999
|
+
return toJson(ConversationStepSchema, step);
|
|
3000
|
+
}));
|
|
3001
|
+
}
|
|
3002
|
+
return { rootPromptMessagesJson, turnUserMessagesJson, turnStepMessagesJson };
|
|
3003
|
+
}
|
|
3004
|
+
function createCursorUserMessage(content, text, messageId = randomUUID()) {
|
|
3005
|
+
const images = typeof content === "string" ? [] : extractImages(content);
|
|
3006
|
+
return create(UserMessageSchema, {
|
|
3007
|
+
text,
|
|
3008
|
+
messageId,
|
|
3009
|
+
...(images.length > 0
|
|
3010
|
+
? {
|
|
3011
|
+
selectedContext: create(SelectedContextSchema, {
|
|
3012
|
+
selectedImages: images,
|
|
3013
|
+
}),
|
|
3014
|
+
}
|
|
3015
|
+
: {}),
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
function extractImages(content) {
|
|
3019
|
+
return content
|
|
3020
|
+
.filter((item) => item.type === "image")
|
|
3021
|
+
.map((image) => create(SelectedImageSchema, {
|
|
3022
|
+
uuid: randomUUID(),
|
|
3023
|
+
mimeType: image.mimeType,
|
|
3024
|
+
dataOrBlobId: {
|
|
3025
|
+
case: "data",
|
|
3026
|
+
value: Uint8Array.from(Buffer.from(image.data, "base64")),
|
|
3027
|
+
},
|
|
3028
|
+
}));
|
|
3029
|
+
}
|
|
3030
|
+
async function buildGrpcRequest(model, context, options, state) {
|
|
3031
|
+
const blobStore = state.blobStore;
|
|
3032
|
+
const systemPromptIds = buildCursorSystemPromptJsons(context.systemPrompt).map((json) => storeCursorBlob(blobStore, new TextEncoder().encode(json)));
|
|
3033
|
+
const activeUserMessageIndex = context.messages.length - 1;
|
|
3034
|
+
const activeMessage = context.messages[activeUserMessageIndex];
|
|
3035
|
+
const activeUserMessage = activeMessage?.role === "user" ? activeMessage : undefined;
|
|
3036
|
+
let userContent;
|
|
3037
|
+
let userText = "";
|
|
3038
|
+
let hasUserImages = false;
|
|
3039
|
+
if (activeUserMessage) {
|
|
3040
|
+
userContent = activeUserMessage.content;
|
|
3041
|
+
if (typeof userContent === "string") {
|
|
3042
|
+
userText = userContent.trim();
|
|
3043
|
+
}
|
|
3044
|
+
else {
|
|
3045
|
+
userText = extractText(userContent);
|
|
3046
|
+
hasUserImages = hasImages(userContent);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
const action = create(ConversationActionSchema, {
|
|
3050
|
+
action: userContent && (userText.trim().length > 0 || hasUserImages)
|
|
3051
|
+
? {
|
|
3052
|
+
case: "userMessageAction",
|
|
3053
|
+
value: create(UserMessageActionSchema, {
|
|
3054
|
+
userMessage: createCursorUserMessage(userContent, userText),
|
|
3055
|
+
}),
|
|
3056
|
+
}
|
|
3057
|
+
: {
|
|
3058
|
+
case: "resumeAction",
|
|
3059
|
+
value: create(ResumeActionSchema, {}),
|
|
3060
|
+
},
|
|
3061
|
+
});
|
|
3062
|
+
// Build conversation turns from prior messages, excluding only the active
|
|
3063
|
+
// user message when the request is sending one. Resume actions must
|
|
3064
|
+
// preserve trailing tool results.
|
|
3065
|
+
const turns = buildConversationTurns(context.messages, blobStore, activeUserMessage ? activeUserMessageIndex : -1);
|
|
3066
|
+
// Cursor's server uses `rootPromptMessagesJson` (not `turns[]`) to build
|
|
3067
|
+
// the actual model prompt; without it multi-turn conversations lose prior
|
|
3068
|
+
// context.
|
|
3069
|
+
const rootPromptMessagesJson = buildRootPromptMessagesJson(context.messages, systemPromptIds, blobStore, activeUserMessage ? activeUserMessageIndex : -1);
|
|
3070
|
+
// Preserve cached non-history state fields (todos, file states, summaries)
|
|
3071
|
+
// when the system prompt is unchanged; otherwise start fresh.
|
|
3072
|
+
const cachedPromptHead = state.conversationState?.rootPromptMessagesJson?.slice(0, systemPromptIds.length) ?? [];
|
|
3073
|
+
const hasMatchingPrompt = cachedPromptHead.length === systemPromptIds.length &&
|
|
3074
|
+
systemPromptIds.every((id, idx) => Buffer.from(cachedPromptHead[idx]).equals(id));
|
|
3075
|
+
const baseState = state.conversationState && hasMatchingPrompt
|
|
3076
|
+
? state.conversationState
|
|
3077
|
+
: create(ConversationStateStructureSchema, {
|
|
3078
|
+
rootPromptMessagesJson: systemPromptIds,
|
|
3079
|
+
turns: [],
|
|
3080
|
+
todos: [],
|
|
3081
|
+
pendingToolCalls: [],
|
|
3082
|
+
previousWorkspaceUris: [],
|
|
3083
|
+
fileStates: {},
|
|
3084
|
+
fileStatesV2: {},
|
|
3085
|
+
summaryArchives: [],
|
|
3086
|
+
turnTimings: [],
|
|
3087
|
+
subagentStates: {},
|
|
3088
|
+
selfSummaryCount: 0,
|
|
3089
|
+
readPaths: [],
|
|
3090
|
+
});
|
|
3091
|
+
// Always override `rootPromptMessagesJson` and `turns` with content freshly
|
|
3092
|
+
// built from `context.messages`: the server-echoed checkpoint replaces
|
|
3093
|
+
// historical user entries with empty placeholders.
|
|
3094
|
+
const conversationState = create(ConversationStateStructureSchema, {
|
|
3095
|
+
...baseState,
|
|
3096
|
+
rootPromptMessagesJson,
|
|
3097
|
+
turns,
|
|
3098
|
+
});
|
|
3099
|
+
const wireModelId = model.upstreamModelId ?? model.id;
|
|
3100
|
+
const cursorMaxMode = model.compat?.cursorMaxMode === true;
|
|
3101
|
+
const modelDetails = create(ModelDetailsSchema, {
|
|
3102
|
+
modelId: wireModelId,
|
|
3103
|
+
displayModelId: model.id,
|
|
3104
|
+
displayName: model.name,
|
|
3105
|
+
...(cursorMaxMode ? { maxMode: true } : undefined),
|
|
3106
|
+
});
|
|
3107
|
+
const requestedModel = create(RequestedModelSchema, {
|
|
3108
|
+
modelId: wireModelId,
|
|
3109
|
+
maxMode: cursorMaxMode,
|
|
3110
|
+
});
|
|
3111
|
+
const runRequest = create(AgentRunRequestSchema, {
|
|
3112
|
+
conversationState,
|
|
3113
|
+
action,
|
|
3114
|
+
modelDetails,
|
|
3115
|
+
requestedModel,
|
|
3116
|
+
conversationId: state.conversationId,
|
|
3117
|
+
});
|
|
3118
|
+
if (options?.customSystemPrompt) {
|
|
3119
|
+
runRequest.customSystemPrompt = options.customSystemPrompt;
|
|
3120
|
+
}
|
|
3121
|
+
await options?.onPayload?.(runRequest, model);
|
|
3122
|
+
const clientMessage = create(AgentClientMessageSchema, {
|
|
3123
|
+
message: { case: "runRequest", value: runRequest },
|
|
3124
|
+
});
|
|
3125
|
+
const requestBytes = toBinary(AgentClientMessageSchema, clientMessage);
|
|
3126
|
+
log("info", "builtRunRequest", {
|
|
3127
|
+
bytes: requestBytes.length,
|
|
3128
|
+
tools: context.tools?.length ?? 0,
|
|
3129
|
+
});
|
|
3130
|
+
return { requestBytes, blobStore, conversationState };
|
|
3131
|
+
}
|
|
3132
|
+
function hasImages(content) {
|
|
3133
|
+
return content.some((item) => item.type === "image");
|
|
3134
|
+
}
|
|
3135
|
+
function extractText(content) {
|
|
3136
|
+
return content
|
|
3137
|
+
.filter((c) => c.type === "text")
|
|
3138
|
+
.map((c) => c.text)
|
|
3139
|
+
.join("\n");
|
|
3140
|
+
}
|
|
3141
|
+
// ---------------------------------------------------------------------------
|
|
3142
|
+
// Model discovery (GetUsableModels)
|
|
3143
|
+
// ---------------------------------------------------------------------------
|
|
3144
|
+
const CURSOR_GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels";
|
|
3145
|
+
const CURSOR_DEFAULT_CONTEXT_WINDOW = 200_000;
|
|
3146
|
+
const CURSOR_DEFAULT_MAX_TOKENS = 64_000;
|
|
3147
|
+
/**
|
|
3148
|
+
* `GetUsableModels` carries no context-window field, so the 1M ceiling is
|
|
3149
|
+
* recovered from the signals Cursor does send: display-name "1M" labels and
|
|
3150
|
+
* the max-mode flag on Claude/Gemini families.
|
|
3151
|
+
*/
|
|
3152
|
+
const CURSOR_1M_CONTEXT_WINDOW = 1_000_000;
|
|
3153
|
+
const CURSOR_1M_NAME_PATTERN = /\b1m\b/i;
|
|
3154
|
+
const CURSOR_MAX_MODE_1M_ID_PATTERN = /claude|gemini/;
|
|
3155
|
+
/**
|
|
3156
|
+
* Model-id families whose native catalogs are multimodal. Cursor-only or
|
|
3157
|
+
* text-only families (`composer-*`, `grok-code-*`) stay outside this pattern.
|
|
3158
|
+
*/
|
|
3159
|
+
const CURSOR_MULTIMODAL_ID_PATTERN = /claude|gemini|gpt-|codex/;
|
|
3160
|
+
/**
|
|
3161
|
+
* Fetches Cursor models through `GetUsableModels` (unary protobuf over
|
|
3162
|
+
* HTTP/2; the ALB rejects HTTP/1.1 with 464) and normalizes them.
|
|
3163
|
+
*
|
|
3164
|
+
* Returns `null` on request/decode failures; `[]` only when the endpoint
|
|
3165
|
+
* responds successfully with no usable models.
|
|
3166
|
+
*/
|
|
3167
|
+
export async function fetchCursorUsableModels(options) {
|
|
3168
|
+
const timeoutMs = options.timeoutMs ?? 10_000;
|
|
3169
|
+
try {
|
|
3170
|
+
const requestPayload = create(GetUsableModelsRequestSchema, { customModelIds: [] });
|
|
3171
|
+
const body = toBinary(GetUsableModelsRequestSchema, requestPayload);
|
|
3172
|
+
const baseUrl = (options.baseUrl ?? CURSOR_API_URL).replace(/\/+$/, "");
|
|
3173
|
+
const responseBuffer = await new Promise((resolve) => {
|
|
3174
|
+
const client = http2.connect(baseUrl);
|
|
3175
|
+
const timer = setTimeout(() => {
|
|
3176
|
+
client.destroy();
|
|
3177
|
+
resolve(null);
|
|
3178
|
+
}, timeoutMs);
|
|
3179
|
+
const abort = () => {
|
|
3180
|
+
clearTimeout(timer);
|
|
3181
|
+
client.destroy();
|
|
3182
|
+
resolve(null);
|
|
3183
|
+
};
|
|
3184
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
3185
|
+
client.on("error", () => {
|
|
3186
|
+
clearTimeout(timer);
|
|
3187
|
+
resolve(null);
|
|
3188
|
+
});
|
|
3189
|
+
const req = client.request({
|
|
3190
|
+
":method": "POST",
|
|
3191
|
+
":path": CURSOR_GET_USABLE_MODELS_PATH,
|
|
3192
|
+
"content-type": "application/proto",
|
|
3193
|
+
te: "trailers",
|
|
3194
|
+
authorization: `Bearer ${options.apiKey}`,
|
|
3195
|
+
"x-ghost-mode": "true",
|
|
3196
|
+
"x-cursor-client-version": CURSOR_CLIENT_VERSION,
|
|
3197
|
+
"x-cursor-client-type": "cli",
|
|
3198
|
+
});
|
|
3199
|
+
const chunks = [];
|
|
3200
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
3201
|
+
req.on("end", () => {
|
|
3202
|
+
clearTimeout(timer);
|
|
3203
|
+
client.close();
|
|
3204
|
+
resolve(new Uint8Array(Buffer.concat(chunks)));
|
|
3205
|
+
});
|
|
3206
|
+
req.on("error", () => {
|
|
3207
|
+
clearTimeout(timer);
|
|
3208
|
+
client.close();
|
|
3209
|
+
resolve(null);
|
|
3210
|
+
});
|
|
3211
|
+
req.on("response", (headers) => {
|
|
3212
|
+
const status = Number(headers[":status"] ?? 0);
|
|
3213
|
+
if (status < 200 || status >= 300) {
|
|
3214
|
+
clearTimeout(timer);
|
|
3215
|
+
client.close();
|
|
3216
|
+
resolve(null);
|
|
3217
|
+
}
|
|
3218
|
+
});
|
|
3219
|
+
if (body.length > 0) {
|
|
3220
|
+
req.end(Buffer.from(body));
|
|
3221
|
+
}
|
|
3222
|
+
else {
|
|
3223
|
+
req.end();
|
|
3224
|
+
}
|
|
3225
|
+
});
|
|
3226
|
+
if (!responseBuffer || responseBuffer.length === 0) {
|
|
3227
|
+
return null;
|
|
3228
|
+
}
|
|
3229
|
+
const decoded = decodeGetUsableModelsResponse(responseBuffer);
|
|
3230
|
+
if (!decoded) {
|
|
3231
|
+
return null;
|
|
3232
|
+
}
|
|
3233
|
+
const byId = new Map();
|
|
3234
|
+
for (const model of decoded.models ?? []) {
|
|
3235
|
+
const id = model.modelId?.trim();
|
|
3236
|
+
if (!id)
|
|
3237
|
+
continue;
|
|
3238
|
+
const name = pickModelDisplayName(model, id);
|
|
3239
|
+
const labeled1M = CURSOR_1M_NAME_PATTERN.test(id) ||
|
|
3240
|
+
[model.displayName, model.displayNameShort, model.displayModelId, ...(model.aliases ?? [])].some((candidate) => typeof candidate === "string" && CURSOR_1M_NAME_PATTERN.test(candidate));
|
|
3241
|
+
const maxMode = model.maxMode === true;
|
|
3242
|
+
const contextWindow = labeled1M || (maxMode && CURSOR_MAX_MODE_1M_ID_PATTERN.test(id))
|
|
3243
|
+
? CURSOR_1M_CONTEXT_WINDOW
|
|
3244
|
+
: CURSOR_DEFAULT_CONTEXT_WINDOW;
|
|
3245
|
+
byId.set(id, {
|
|
3246
|
+
id,
|
|
3247
|
+
name,
|
|
3248
|
+
reasoning: Boolean(model.thinkingDetails),
|
|
3249
|
+
input: CURSOR_MULTIMODAL_ID_PATTERN.test(id.toLowerCase()) ? ["text", "image"] : ["text"],
|
|
3250
|
+
contextWindow,
|
|
3251
|
+
maxTokens: CURSOR_DEFAULT_MAX_TOKENS,
|
|
3252
|
+
cursorMaxMode: maxMode,
|
|
3253
|
+
});
|
|
3254
|
+
}
|
|
3255
|
+
return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
3256
|
+
}
|
|
3257
|
+
catch {
|
|
3258
|
+
return null;
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
function decodeGetUsableModelsResponse(payload) {
|
|
3262
|
+
const framedBody = decodeConnectUnaryBody(payload);
|
|
3263
|
+
if (framedBody) {
|
|
3264
|
+
try {
|
|
3265
|
+
return fromBinary(GetUsableModelsResponseSchema, framedBody);
|
|
3266
|
+
}
|
|
3267
|
+
catch {
|
|
3268
|
+
return null;
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
try {
|
|
3272
|
+
return fromBinary(GetUsableModelsResponseSchema, payload);
|
|
3273
|
+
}
|
|
3274
|
+
catch {
|
|
3275
|
+
return null;
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
function decodeConnectUnaryBody(payload) {
|
|
3279
|
+
if (payload.length < 5) {
|
|
3280
|
+
return null;
|
|
3281
|
+
}
|
|
3282
|
+
let offset = 0;
|
|
3283
|
+
while (offset + 5 <= payload.length) {
|
|
3284
|
+
const flags = payload[offset];
|
|
3285
|
+
const view = new DataView(payload.buffer, payload.byteOffset + offset, payload.byteLength - offset);
|
|
3286
|
+
const messageLength = view.getUint32(1, false);
|
|
3287
|
+
const frameEnd = offset + 5 + messageLength;
|
|
3288
|
+
if (frameEnd > payload.length) {
|
|
3289
|
+
return null;
|
|
3290
|
+
}
|
|
3291
|
+
const compressionFlagSet = (flags & 0b0000_0001) !== 0;
|
|
3292
|
+
if (compressionFlagSet) {
|
|
3293
|
+
return null;
|
|
3294
|
+
}
|
|
3295
|
+
const endStreamFlagSet = (flags & 0b0000_0010) !== 0;
|
|
3296
|
+
if (!endStreamFlagSet) {
|
|
3297
|
+
return payload.subarray(offset + 5, frameEnd);
|
|
3298
|
+
}
|
|
3299
|
+
offset = frameEnd;
|
|
3300
|
+
}
|
|
3301
|
+
return null;
|
|
3302
|
+
}
|
|
3303
|
+
function pickModelDisplayName(model, fallbackId) {
|
|
3304
|
+
const candidates = [model.displayName, model.displayNameShort, model.displayModelId, ...(model.aliases ?? [])];
|
|
3305
|
+
for (const candidate of candidates) {
|
|
3306
|
+
if (typeof candidate !== "string") {
|
|
3307
|
+
continue;
|
|
3308
|
+
}
|
|
3309
|
+
const trimmed = candidate.trim();
|
|
3310
|
+
if (trimmed) {
|
|
3311
|
+
return trimmed;
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
return fallbackId;
|
|
3315
|
+
}
|
|
3316
|
+
//# sourceMappingURL=cursor-agent.js.map
|