@cursor/july 0.1.96 → 0.1.99
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/AGENTS.md +1 -1
- package/README.md +3 -2
- package/dist/bin/agent-serve.js +13 -3
- package/dist/channels/github/github-channel.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +16 -0
- package/dist/channels/origin/api.d.ts +15 -4
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +19 -3
- package/dist/channels/origin/binding.d.ts +11 -0
- package/dist/channels/origin/binding.d.ts.map +1 -1
- package/dist/channels/origin/binding.js +41 -25
- package/dist/channels/origin/index.d.ts +1 -0
- package/dist/channels/origin/index.d.ts.map +1 -1
- package/dist/channels/origin/index.js +1 -0
- package/dist/channels/origin/origin-channel.d.ts +2 -0
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +40 -4
- package/dist/channels/origin/reactions.d.ts +56 -0
- package/dist/channels/origin/reactions.d.ts.map +1 -0
- package/dist/channels/origin/reactions.js +67 -0
- package/dist/channels/origin/types.d.ts +11 -0
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/approvals.d.ts +16 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -1
- package/dist/channels/slack/approvals.js +86 -0
- package/dist/channels/slack/cli-project.d.ts +19 -0
- package/dist/channels/slack/cli-project.d.ts.map +1 -0
- package/dist/channels/slack/cli-project.js +234 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +142 -63
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +1 -1
- package/dist/channels/slack/init.d.ts +3 -2
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +5 -5
- package/dist/channels/slack/interactive.d.ts +2 -1
- package/dist/channels/slack/interactive.d.ts.map +1 -1
- package/dist/channels/slack/interactive.js +15 -4
- package/dist/channels/slack/live-delivery.d.ts +1 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +4 -2
- package/dist/channels/slack/manifest.d.ts +1 -1
- package/dist/channels/slack/manifest.js +1 -1
- package/dist/channels/slack/progress-delivery.d.ts +28 -0
- package/dist/channels/slack/progress-delivery.d.ts.map +1 -0
- package/dist/channels/slack/progress-delivery.js +115 -0
- package/dist/channels/slack/setup.d.ts +1 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +10 -6
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +1 -0
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
- package/dist/channels/slack/tool-approval-events.js +91 -1
- package/dist/channels/slack/types.d.ts +9 -1
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.jXleCmOc.js → app.BplEAZ3S.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DpuI4i7Z.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.8LhyglIT.js → VPLocalSearchBox.CHXyv4wP.js} +1 -1
- package/dist/docs/assets/chunks/{theme.9ged-pr2.js → theme.BTcsZmjQ.js} +2 -2
- package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
- package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
- package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
- package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
- package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.CCnG-nJx.js → reference_cli.md.DfoeyvL0.js} +9 -6
- package/dist/docs/assets/{reference_cli.md.CCnG-nJx.lean.js → reference_cli.md.DfoeyvL0.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
- package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.BEJx9XVj.js} +2 -2
- package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.CvAHsdSp.js} +10 -2
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.CvAHsdSp.lean.js} +1 -1
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
- package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
- package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/deployment.md +5 -4
- package/dist/docs/design/runtime-abstraction.md +1757 -0
- package/dist/docs/evals.html +4 -4
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-runtime.html +4 -4
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +4 -4
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/mcp-oauth.html +8 -8
- package/dist/docs/guides/mcp-oauth.md +26 -8
- package/dist/docs/guides/opentelemetry.html +4 -4
- package/dist/docs/guides/slack.html +17 -33
- package/dist/docs/guides/slack.md +92 -201
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +5 -3
- package/dist/docs/llms-full.txt +2065 -637
- package/dist/docs/llms.txt +3 -2
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/agent-config.md +2 -2
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +13 -10
- package/dist/docs/reference/cli.md +32 -16
- package/dist/docs/reference/connections.html +9 -8
- package/dist/docs/reference/connections.md +6 -3
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +7 -7
- package/dist/docs/reference/http-api.md +3 -1
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/sessions.md +4 -2
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +15 -7
- package/dist/docs/reference/tools.md +34 -2
- package/dist/docs/scaffolding-agents.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +28 -0
- package/dist/docs/templates/agents-md.md +105 -0
- package/dist/docs/templates/code-wiki.html +5 -5
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +5 -5
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +2 -2
- package/dist/evals/run-facts.d.ts.map +1 -1
- package/dist/evals/run-facts.js +27 -0
- package/dist/files-backends/cursor-hosted.d.ts +3 -2
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +3 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +15 -9
- package/dist/internal/approval-gate.d.ts +12 -13
- package/dist/internal/approval-gate.d.ts.map +1 -1
- package/dist/internal/approval-gate.js +16 -21
- package/dist/internal/as-of.d.ts +19 -0
- package/dist/internal/as-of.d.ts.map +1 -0
- package/dist/internal/as-of.js +82 -0
- package/dist/internal/chat-client.js +10 -0
- package/dist/internal/cli-deploy.d.ts +6 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +149 -21
- package/dist/internal/cli-mcp-oauth.js +8 -18
- package/dist/internal/cli-slack-manual.d.ts +24 -2
- package/dist/internal/cli-slack-manual.d.ts.map +1 -1
- package/dist/internal/cli-slack-manual.js +251 -20
- package/dist/internal/cli-slack.d.ts +9 -2
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +29 -82
- package/dist/internal/cursor/account-mcp.d.ts +1 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
- package/dist/internal/cursor/account-mcp.js +17 -0
- package/dist/internal/cursor-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -1
- package/dist/internal/cursor-event-relay.js +5 -6
- package/dist/internal/deploy-client.d.ts +17 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +31 -10
- package/dist/internal/env-file.d.ts +19 -0
- package/dist/internal/env-file.d.ts.map +1 -0
- package/dist/internal/env-file.js +90 -0
- package/dist/internal/framework-file-storage.d.ts +11 -17
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +17 -40
- package/dist/internal/framework-storage-selection.d.ts +13 -11
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +37 -26
- package/dist/internal/hosted-admission-adapter.d.ts +28 -0
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -0
- package/dist/internal/hosted-admission-adapter.js +7 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +13 -1
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +8 -1
- package/dist/internal/hosted-delivery.d.ts +21 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +101 -34
- package/dist/internal/hosted-execution-diag.d.ts +59 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
- package/dist/internal/hosted-execution-diag.js +187 -0
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +85 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +2 -0
- package/dist/internal/logs-client.d.ts +5 -0
- package/dist/internal/logs-client.d.ts.map +1 -1
- package/dist/internal/logs-client.js +5 -0
- package/dist/internal/mcp-endpoint.js +2 -3
- package/dist/internal/mcp-host.d.ts +10 -0
- package/dist/internal/mcp-host.d.ts.map +1 -1
- package/dist/internal/mcp-host.js +57 -8
- package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
- package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-on-demand.js +287 -0
- package/dist/internal/mcp-oauth-persist.d.ts +42 -0
- package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
- package/dist/internal/mcp-oauth-persist.js +92 -0
- package/dist/internal/mcp-oauth.d.ts +1 -0
- package/dist/internal/mcp-oauth.d.ts.map +1 -1
- package/dist/internal/mcp-oauth.js +10 -2
- package/dist/internal/null-kv-storage.d.ts +48 -0
- package/dist/internal/null-kv-storage.d.ts.map +1 -0
- package/dist/internal/null-kv-storage.js +58 -0
- package/dist/internal/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +22 -9
- package/dist/internal/server.d.ts +6 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +123 -33
- package/dist/internal/session-engine.d.ts +88 -3
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +538 -46
- package/dist/internal/session-run-log.d.ts +120 -0
- package/dist/internal/session-run-log.d.ts.map +1 -0
- package/dist/internal/session-run-log.js +359 -0
- package/dist/internal/sessions-client.d.ts +9 -7
- package/dist/internal/sessions-client.d.ts.map +1 -1
- package/dist/internal/sessions-client.js +3 -1
- package/dist/internal/storage-coordinator.d.ts +13 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +37 -6
- package/dist/internal/storage-roles.d.ts +5 -0
- package/dist/internal/storage-roles.d.ts.map +1 -1
- package/dist/internal/stream-progress.d.ts.map +1 -1
- package/dist/internal/stream-progress.js +10 -0
- package/dist/internal/tool-policy.d.ts +29 -9
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +24 -0
- package/dist/internal/trajectory.d.ts.map +1 -1
- package/dist/internal/trajectory.js +10 -0
- package/dist/internal/turn-outbox.d.ts +24 -0
- package/dist/internal/turn-outbox.d.ts.map +1 -0
- package/dist/internal/turn-outbox.js +80 -0
- package/dist/playground/assets/index-D7rGZNjy.css +1 -0
- package/dist/playground/assets/index-jzNbJENa.js +69 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +1 -1
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +12 -0
- package/dist/storage-protocol.d.ts +8 -0
- package/dist/storage-protocol.d.ts.map +1 -1
- package/dist/storage-protocol.js +8 -0
- package/dist/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/tools.d.ts +12 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/types.d.ts +88 -7
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +5 -3
- package/docs/deployment.md +5 -4
- package/docs/design/runtime-abstraction.md +1757 -0
- package/docs/guides/mcp-oauth.md +26 -8
- package/docs/guides/slack.md +93 -202
- package/docs/reference/agent-config.md +2 -2
- package/docs/reference/cli.md +32 -16
- package/docs/reference/connections.md +6 -3
- package/docs/reference/http-api.md +3 -1
- package/docs/reference/sessions.md +4 -2
- package/docs/reference/tools.md +34 -2
- package/docs/templates/agents-md.md +110 -0
- package/docs/troubleshooting.md +2 -2
- package/package.json +1 -1
- package/skills/create-agent/SKILL.md +2 -1
- package/skills/mcp-auth/SKILL.md +6 -3
- package/skills/setup-slack/SKILL.md +23 -13
- package/src/bin/agent-serve.ts +12 -1
- package/src/channels/github/github-channel.ts +17 -0
- package/src/channels/origin/api.ts +24 -5
- package/src/channels/origin/binding.ts +55 -26
- package/src/channels/origin/index.ts +1 -0
- package/src/channels/origin/origin-channel.ts +78 -10
- package/src/channels/origin/reactions.ts +82 -0
- package/src/channels/origin/types.ts +11 -0
- package/src/channels/slack/approvals.ts +105 -0
- package/src/channels/slack/cli-project.ts +267 -0
- package/src/channels/slack/defaults.ts +165 -74
- package/src/channels/slack/dispatch.ts +2 -0
- package/src/channels/slack/init.ts +10 -5
- package/src/channels/slack/interactive.ts +35 -9
- package/src/channels/slack/live-delivery.ts +5 -3
- package/src/channels/slack/manifest.ts +1 -1
- package/src/channels/slack/progress-delivery.ts +133 -0
- package/src/channels/slack/setup.ts +10 -6
- package/src/channels/slack/slack-channel.ts +1 -0
- package/src/channels/slack/tool-approval-events.ts +93 -0
- package/src/channels/slack/types.ts +9 -1
- package/src/evals/run-facts.ts +32 -0
- package/src/files-backends/cursor-hosted.ts +3 -2
- package/src/index.ts +4 -0
- package/src/internal/advertise-tools.ts +31 -10
- package/src/internal/approval-gate.ts +20 -29
- package/src/internal/as-of.ts +107 -0
- package/src/internal/chat-client.ts +19 -0
- package/src/internal/cli-deploy.ts +203 -13
- package/src/internal/cli-mcp-oauth.ts +7 -21
- package/src/internal/cli-slack-manual.ts +350 -18
- package/src/internal/cli-slack.ts +40 -84
- package/src/internal/cursor/account-mcp.ts +21 -0
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/cursor-event-relay.ts +5 -6
- package/src/internal/deploy-client.ts +43 -0
- package/src/internal/env-file.ts +84 -0
- package/src/internal/framework-file-storage.ts +18 -46
- package/src/internal/framework-storage-selection.ts +41 -25
- package/src/internal/hosted-admission-adapter.ts +42 -0
- package/src/internal/hosted-delivery-protocol.ts +14 -2
- package/src/internal/hosted-delivery.ts +169 -44
- package/src/internal/hosted-execution-diag.ts +252 -0
- package/src/internal/http-channel.ts +107 -0
- package/src/internal/init-project.ts +2 -0
- package/src/internal/logs-client.ts +5 -0
- package/src/internal/mcp-endpoint.ts +13 -4
- package/src/internal/mcp-host.ts +77 -11
- package/src/internal/mcp-oauth-on-demand.ts +352 -0
- package/src/internal/mcp-oauth-persist.ts +127 -0
- package/src/internal/mcp-oauth.ts +17 -1
- package/src/internal/null-kv-storage.ts +74 -0
- package/src/internal/otel/collector.ts +30 -0
- package/src/internal/server.ts +159 -11
- package/src/internal/session-engine.ts +691 -57
- package/src/internal/session-run-log.ts +584 -0
- package/src/internal/sessions-client.ts +17 -8
- package/src/internal/storage-coordinator.ts +49 -9
- package/src/internal/storage-roles.ts +7 -0
- package/src/internal/stream-progress.ts +19 -0
- package/src/internal/tool-policy.ts +49 -9
- package/src/internal/trajectory.ts +10 -0
- package/src/internal/turn-outbox.ts +85 -0
- package/src/storage-backends/cursor-hosted.ts +13 -1
- package/src/storage-protocol.ts +10 -0
- package/src/storage.ts +12 -0
- package/src/tools.ts +12 -2
- package/src/types.ts +93 -8
- package/templates/agents-md/README.md +39 -0
- package/templates/agents-md/agent/agent.ts +8 -0
- package/templates/agents-md/agent/channels/github.ts +10 -0
- package/templates/agents-md/agent/channels/queue.ts +28 -0
- package/templates/agents-md/agent/channels/slack.ts +9 -0
- package/templates/agents-md/agent/instructions.md +39 -0
- package/templates/agents-md/agent/lib/collect.ts +439 -0
- package/templates/agents-md/agent/lib/config.ts +8 -0
- package/templates/agents-md/agent/lib/paths.ts +51 -0
- package/templates/agents-md/agent/schedules/weekly.ts +17 -0
- package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
- package/templates/agents-md/evals/agents.eval.ts +135 -0
- package/templates/agents-md/evals/evals.config.ts +5 -0
- package/templates/agents-md/init.json +38 -0
- package/templates/agents-md/package.json +18 -0
- package/templates/agents-md/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.DFoGuETX.js +0 -1
- package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
- package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
- package/dist/docs/assets/index.md.CI93Ltlb.js +0 -5
- package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
- package/dist/docs/design/agsh.md +0 -406
- package/dist/playground/assets/index-B1DDvBeC.css +0 -1
- package/dist/playground/assets/index-Cx9CclK_.js +0 -88
- package/docs/design/agsh.md +0 -406
- /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.CI93Ltlb.lean.js → index.md.CVeRUOeZ.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
- /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.BEJx9XVj.lean.js} +0 -0
- /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
|
@@ -15,6 +15,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
15
15
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
16
16
|
});
|
|
17
17
|
};
|
|
18
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
19
|
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
19
20
|
import { dirname, join } from "node:path";
|
|
20
21
|
import { mergeABInstructions, resolveABMaxPlaygroundSessions, resolveABSplit, } from "../ab.js";
|
|
@@ -24,13 +25,14 @@ import { SlackAskDedupeStore, SlackNudgeStore, } from "../channels/slack/nudge-s
|
|
|
24
25
|
import { resolveDedicatedSlackHostBinding } from "../channels/slack/slack-channel.js";
|
|
25
26
|
import { AGENT_SERVE_USER_STORE_FILES_SOURCE_ID, agentStoreKeys, } from "../files.js";
|
|
26
27
|
import { cursorHostedFiles, isCursorHostedFilesAvailable, isCursorUserStoreFilesAvailable, } from "../files-backends/cursor-hosted.js";
|
|
27
|
-
import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
|
|
28
|
+
import { DEFAULT_MODEL, DIRECT_TOOL_CALL_CHANNEL_ID, isCursorAccountTransport, isHostOauthConnectionTransport, isSymbolicConnectionTransport, } from "../types.js";
|
|
28
29
|
import { ABCollector } from "./ab-collector.js";
|
|
29
30
|
import { foldABStream } from "./ab-fold.js";
|
|
30
31
|
import { buildABSnapshot } from "./ab-snapshot.js";
|
|
31
|
-
import { resolveAdvertisedTools } from "./advertise-tools.js";
|
|
32
|
+
import { resolveAdvertisedTools, } from "./advertise-tools.js";
|
|
32
33
|
import { ApprovalGate } from "./approval-gate.js";
|
|
33
34
|
import { ArtifactsStore } from "./artifacts-store.js";
|
|
35
|
+
import { assertTimeArgs, normalizeAsOf, sessionClock } from "./as-of.js";
|
|
34
36
|
import { SessionAuthError, samePrincipal } from "./auth.js";
|
|
35
37
|
import { mergeCloudOptions, resolveSessionRuntime } from "./cloud-merge.js";
|
|
36
38
|
import { applyCloudUsageToEvents, cloudSdkAgentIdFromEvents, isUnpricedCompletedTurn, mapSdkTokenUsage, } from "./cloud-turn-cost.js";
|
|
@@ -47,7 +49,8 @@ import { createHostPlatforms, createHostSlackClient, } from "./host-platforms.js
|
|
|
47
49
|
import { HostedAdmissionLocalSendError, isDuringHostedAdmission, } from "./hosted-admission-context.js";
|
|
48
50
|
import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, parseContinuationToken, } from "./ids.js";
|
|
49
51
|
import { validateToolInput } from "./json-schema.js";
|
|
50
|
-
import { McpHost } from "./mcp-host.js";
|
|
52
|
+
import { isMcpNeedsAuthError, McpHost } from "./mcp-host.js";
|
|
53
|
+
import { McpOauthOnDemandHub } from "./mcp-oauth-on-demand.js";
|
|
51
54
|
import { shouldStartProcessMemoryTelemetry, startProcessMemoryTelemetry, } from "./process-memory-telemetry.js";
|
|
52
55
|
import { buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX, } from "./prompt-context.js";
|
|
53
56
|
import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
|
|
@@ -56,11 +59,12 @@ import { SessionStore } from "./session-store.js";
|
|
|
56
59
|
import { syncSkillsToStore } from "./skills-sync.js";
|
|
57
60
|
import { steeringEnabled } from "./steering.js";
|
|
58
61
|
import { StorageCoordinator } from "./storage-coordinator.js";
|
|
59
|
-
import { decideToolCall,
|
|
62
|
+
import { decideToolCall, directCallEffect } from "./tool-policy.js";
|
|
60
63
|
import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
|
|
61
64
|
import { estimateTurnCostFromApi, roundCostUsd, } from "./turn-cost.js";
|
|
62
65
|
import { TurnGovernor } from "./turn-governor.js";
|
|
63
66
|
import { TurnLeaseStore } from "./turn-lease-store.js";
|
|
67
|
+
import { TurnOutbox } from "./turn-outbox.js";
|
|
64
68
|
import { buildAgentsMdContent, buildCloudMemorySection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, writeWorkspaceFiles, } from "./workspace.js";
|
|
65
69
|
export const HTTP_CHANNEL_ID = "http";
|
|
66
70
|
export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
|
|
@@ -134,6 +138,19 @@ export class ApprovalInterruptedError extends Error {
|
|
|
134
138
|
this.name = "ApprovalInterruptedError";
|
|
135
139
|
}
|
|
136
140
|
}
|
|
141
|
+
export class AuthNotFoundError extends Error {
|
|
142
|
+
constructor(sessionId, callId) {
|
|
143
|
+
super(`No pending MCP auth for session ${sessionId} call ${callId}`);
|
|
144
|
+
this.name = "AuthNotFoundError";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
export class AuthInterruptedError extends Error {
|
|
148
|
+
constructor(sessionId, callId) {
|
|
149
|
+
super(`MCP auth for session ${sessionId} call ${callId} is no longer live (host may have restarted while the tool was parked)`);
|
|
150
|
+
this.name = "AuthInterruptedError";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
const mcpAuthAls = new AsyncLocalStorage();
|
|
137
154
|
export class SessionEngine {
|
|
138
155
|
constructor(options) {
|
|
139
156
|
var _a, _b, _c, _d, _e;
|
|
@@ -143,6 +160,13 @@ export class SessionEngine {
|
|
|
143
160
|
/** Sessions with a turn being admitted (before the run promise exists). */
|
|
144
161
|
this.pendingTurns = new Set();
|
|
145
162
|
this.activeTurns = new Map();
|
|
163
|
+
/**
|
|
164
|
+
* Sessions whose workspace a turn in this process has materialized. A
|
|
165
|
+
* session is busy from admit — before the governor slot and the turn's
|
|
166
|
+
* own materialize — so a read running alongside a turn consults this,
|
|
167
|
+
* not busy, to know whether the workspace is ready to be left alone.
|
|
168
|
+
*/
|
|
169
|
+
this.materializedWorkspaces = new Set();
|
|
146
170
|
/** Context used by the cloud turn's HTTP MCP callbacks. */
|
|
147
171
|
this.activeToolContexts = new Map();
|
|
148
172
|
/**
|
|
@@ -179,6 +203,8 @@ export class SessionEngine {
|
|
|
179
203
|
this.dispatchChains = new Map();
|
|
180
204
|
this.backgroundWork = new Set();
|
|
181
205
|
this.approvals = new ApprovalGate();
|
|
206
|
+
this.auths = new ApprovalGate();
|
|
207
|
+
this.oauthHub = new McpOauthOnDemandHub();
|
|
182
208
|
/** Serve-time resolved connections (see {@link setResolvedConnections}). */
|
|
183
209
|
this.resolvedConnections = [];
|
|
184
210
|
/** Sessions whose completed turns are fully priced (no further backfill). */
|
|
@@ -203,6 +229,7 @@ export class SessionEngine {
|
|
|
203
229
|
this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
|
|
204
230
|
this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
|
|
205
231
|
this.turnLeases = new TurnLeaseStore(join(options.stateRoot, "turn-leases"));
|
|
232
|
+
this.outbox = new TurnOutbox(join(options.stateRoot, "outbox"));
|
|
206
233
|
this.logs = new EventLogStore((sessionId) => this.sessions.eventFilePath(sessionId), (sessionId, error) => this.logger(`[agent-sdk] failed to persist events for ${sessionId}: ${describeError(error)}`));
|
|
207
234
|
// Framework session state (records, event chunks, continuations) is
|
|
208
235
|
// mirrored to whatever this selects; see framework-storage-selection.ts
|
|
@@ -284,10 +311,20 @@ export class SessionEngine {
|
|
|
284
311
|
setCloudServerToolsConnection(connection) {
|
|
285
312
|
this.cloudServerToolsConnection = connection;
|
|
286
313
|
}
|
|
314
|
+
setMcpOauthCallbackUrl(url) {
|
|
315
|
+
this.oauthHub.callbackUrl = url;
|
|
316
|
+
this.mcpHost.setOAuthRedirectUrl(url);
|
|
317
|
+
}
|
|
318
|
+
setCursorAccountAuthProbe(probe) {
|
|
319
|
+
this.probeCursorAccount = probe;
|
|
320
|
+
}
|
|
321
|
+
get oauthCallbackHub() {
|
|
322
|
+
return this.oauthHub;
|
|
323
|
+
}
|
|
287
324
|
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
|
|
288
325
|
get host() {
|
|
289
326
|
return {
|
|
290
|
-
mcp: this.mcpHost,
|
|
327
|
+
mcp: this.wrapMcp(this.mcpHost),
|
|
291
328
|
github: this.platforms.github,
|
|
292
329
|
slack: this.platforms.slack,
|
|
293
330
|
reminders: this.reminderApi,
|
|
@@ -584,12 +621,36 @@ export class SessionEngine {
|
|
|
584
621
|
const now = new Date().toISOString();
|
|
585
622
|
const channelState = this.initialChannelState(channelId, sendOptions.state);
|
|
586
623
|
const purpose = sendOptions.purpose === "eval" ? "eval" : "live";
|
|
587
|
-
|
|
624
|
+
const frozenAsOf = normalizeAsOf(sendOptions.asOf);
|
|
625
|
+
assertTimeArgs(this.project.agent.tools, frozenAsOf);
|
|
626
|
+
let record = {
|
|
627
|
+
sessionId: (_d = sendOptions.hostedSessionId) !== null && _d !== void 0 ? _d : newSessionId(),
|
|
628
|
+
channelId,
|
|
629
|
+
mode: (_e = sendOptions.mode) !== null && _e !== void 0 ? _e : "chat",
|
|
630
|
+
purpose,
|
|
631
|
+
dryRun: sendOptions.dryRun === true ? true : undefined,
|
|
632
|
+
asOf: frozenAsOf,
|
|
633
|
+
continuationKey: (_f = sendOptions.continuationToken) !== null && _f !== void 0 ? _f : newContinuationKey(),
|
|
634
|
+
auth,
|
|
635
|
+
title: sendOptions.title,
|
|
636
|
+
channelState,
|
|
637
|
+
workspaceOverride: sendOptions.workspaceDir,
|
|
638
|
+
pendingWorkspaceFiles: sendOptions.workspaceFiles,
|
|
639
|
+
pendingImages: sendOptions.images,
|
|
640
|
+
cloudOverride: sendOptions.cloud,
|
|
641
|
+
sdkAgentId: sendOptions.sdkAgentId,
|
|
642
|
+
turnCount: 0,
|
|
643
|
+
createdAt: now,
|
|
644
|
+
updatedAt: now,
|
|
645
|
+
};
|
|
588
646
|
yield this.sessions.save(record);
|
|
589
647
|
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.sessionRecord(record);
|
|
590
648
|
yield this.appendEvent(record.sessionId, {
|
|
591
649
|
type: "session.started",
|
|
592
|
-
data: {
|
|
650
|
+
data: {
|
|
651
|
+
channelId,
|
|
652
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
653
|
+
},
|
|
593
654
|
});
|
|
594
655
|
record = yield this.enrollABs(record);
|
|
595
656
|
const since = (yield this.logs.get(record.sessionId)).count;
|
|
@@ -791,10 +852,28 @@ export class SessionEngine {
|
|
|
791
852
|
const prior = (_a = this.admissionChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
792
853
|
const admitted = prior
|
|
793
854
|
.catch(() => { })
|
|
794
|
-
.then(() => this
|
|
855
|
+
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
856
|
+
// Another server holds the turn: hand the message to it and coalesce.
|
|
857
|
+
if (yield this.turnLeases.runningElsewhere(sessionId)) {
|
|
858
|
+
yield this.outbox.post(sessionId, { message, options });
|
|
859
|
+
return true;
|
|
860
|
+
}
|
|
861
|
+
return this.admitTurn(sessionId, message, options);
|
|
862
|
+
}));
|
|
795
863
|
this.admissionChains.set(sessionId, admitted.catch(() => { }));
|
|
796
864
|
return admitted;
|
|
797
865
|
}
|
|
866
|
+
/** Admit what other servers handed to this turn's holder. Never awaited by the lifecycle. */
|
|
867
|
+
drainOutbox(sessionId) {
|
|
868
|
+
void this.outbox
|
|
869
|
+
.takeAll(sessionId)
|
|
870
|
+
.then((items) => {
|
|
871
|
+
for (const item of items) {
|
|
872
|
+
void this.startTurn(sessionId, item.message, item.options).catch(() => { });
|
|
873
|
+
}
|
|
874
|
+
})
|
|
875
|
+
.catch(() => { });
|
|
876
|
+
}
|
|
798
877
|
enqueueCoalesced(sessionId, item) {
|
|
799
878
|
var _a;
|
|
800
879
|
const queue = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
@@ -1004,6 +1083,8 @@ export class SessionEngine {
|
|
|
1004
1083
|
let lifecycle;
|
|
1005
1084
|
lifecycle = (() => __awaiter(this, void 0, void 0, function* () {
|
|
1006
1085
|
let outcome;
|
|
1086
|
+
const poll = setInterval(() => this.drainOutbox(sessionId), 1000);
|
|
1087
|
+
poll.unref();
|
|
1007
1088
|
try {
|
|
1008
1089
|
outcome = yield this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
|
|
1009
1090
|
}
|
|
@@ -1013,6 +1094,9 @@ export class SessionEngine {
|
|
|
1013
1094
|
errorMessage: describeError(error),
|
|
1014
1095
|
};
|
|
1015
1096
|
}
|
|
1097
|
+
finally {
|
|
1098
|
+
clearInterval(poll);
|
|
1099
|
+
}
|
|
1016
1100
|
this.lastOutcomes.set(sessionId, outcome);
|
|
1017
1101
|
const wasCurrent = this.activeTurns.get(sessionId) === lifecycle;
|
|
1018
1102
|
if (wasCurrent) {
|
|
@@ -1038,6 +1122,10 @@ export class SessionEngine {
|
|
|
1038
1122
|
// The drain may have nested a successor turn, which took its own
|
|
1039
1123
|
// reference; the lease outlives this release until that one ends too.
|
|
1040
1124
|
yield lease.release();
|
|
1125
|
+
// Handed off between the last poll and the release: nobody else will.
|
|
1126
|
+
if (!abort.signal.aborted) {
|
|
1127
|
+
this.drainOutbox(sessionId);
|
|
1128
|
+
}
|
|
1041
1129
|
return outcome;
|
|
1042
1130
|
}))();
|
|
1043
1131
|
this.activeTurns.set(sessionId, lifecycle);
|
|
@@ -1087,6 +1175,7 @@ export class SessionEngine {
|
|
|
1087
1175
|
this.coalesceDrains.has(sessionId) ||
|
|
1088
1176
|
((_b = (_a = this.coalesceQueues.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
1089
1177
|
this.clearCoalesced(sessionId);
|
|
1178
|
+
yield this.outbox.clear(sessionId);
|
|
1090
1179
|
const record = yield this.sessions.get(sessionId);
|
|
1091
1180
|
const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
|
|
1092
1181
|
if (continuationKey !== undefined && continuationKey !== null) {
|
|
@@ -1275,6 +1364,7 @@ export class SessionEngine {
|
|
|
1275
1364
|
seed: isFirstTurn,
|
|
1276
1365
|
stateRoot: this.stateRoot,
|
|
1277
1366
|
});
|
|
1367
|
+
this.materializedWorkspaces.add(sessionId);
|
|
1278
1368
|
// Must precede the carrier: the harness resolves rules, skills,
|
|
1279
1369
|
// `mcp.json` and hooks from its git root, so a workspace nested in an
|
|
1280
1370
|
// outer repo would load that repo's hooks instead of the ones written
|
|
@@ -1337,12 +1427,15 @@ export class SessionEngine {
|
|
|
1337
1427
|
}
|
|
1338
1428
|
catch (error) {
|
|
1339
1429
|
this.approvals.cancelSession(sessionId, "deny");
|
|
1430
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1340
1431
|
outcome = { status: "error", errorMessage: describeError(error) };
|
|
1341
1432
|
}
|
|
1342
1433
|
finally {
|
|
1343
1434
|
permit.release();
|
|
1344
1435
|
this.approvals.cancelSession(sessionId, "deny");
|
|
1436
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1345
1437
|
yield this.clearPendingApprovals(sessionId);
|
|
1438
|
+
yield this.clearPendingAuths(sessionId);
|
|
1346
1439
|
// Anything still queued belongs to the next turn's boundary drain, not
|
|
1347
1440
|
// to this turn's edge detection.
|
|
1348
1441
|
this.deliveredNudges.delete(sessionId);
|
|
@@ -1517,6 +1610,7 @@ export class SessionEngine {
|
|
|
1517
1610
|
channelId: record.channelId,
|
|
1518
1611
|
channelKind: (_b = (_a = this.channelsById.get(record.channelId)) === null || _a === void 0 ? void 0 : _a.meta) === null || _b === void 0 ? void 0 : _b.kind,
|
|
1519
1612
|
environment: isDevMode() ? "local dev server" : "hosted deployment",
|
|
1613
|
+
now: record.asOf === undefined ? undefined : sessionClock(record.asOf)(),
|
|
1520
1614
|
});
|
|
1521
1615
|
turnPrompt = `${contextBlock}\n\n${prompt}`;
|
|
1522
1616
|
}
|
|
@@ -1833,11 +1927,17 @@ export class SessionEngine {
|
|
|
1833
1927
|
// named passthroughs resolved fresh on every turn — reminder fires and
|
|
1834
1928
|
// post-restart follow-ups included — and a listing failure throws,
|
|
1835
1929
|
// failing the turn closed.
|
|
1836
|
-
for (const tool of yield
|
|
1930
|
+
for (const tool of yield mcpAuthAls.run({
|
|
1931
|
+
sessionId: record.sessionId,
|
|
1932
|
+
turnId,
|
|
1933
|
+
callId: newToolCallId("mcp"),
|
|
1934
|
+
toolName: "mcp",
|
|
1935
|
+
emit,
|
|
1936
|
+
}, () => resolveAdvertisedTools({
|
|
1837
1937
|
agent,
|
|
1838
1938
|
session: this.sessionInfo(record),
|
|
1839
|
-
hostMcp: this.
|
|
1840
|
-
})) {
|
|
1939
|
+
hostMcp: this.advertiseMcp(),
|
|
1940
|
+
}))) {
|
|
1841
1941
|
register(tool);
|
|
1842
1942
|
}
|
|
1843
1943
|
return tools;
|
|
@@ -1903,6 +2003,7 @@ export class SessionEngine {
|
|
|
1903
2003
|
send: (channelId, message, sendOptions = {}) => { var _a; return this.send(channelId, message, Object.assign(Object.assign({}, sendOptions), { auth: (_a = sendOptions.auth) !== null && _a !== void 0 ? _a : args.record.auth })); },
|
|
1904
2004
|
getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
|
|
1905
2005
|
emit: args.emit,
|
|
2006
|
+
now: sessionClock(args.record.asOf),
|
|
1906
2007
|
};
|
|
1907
2008
|
try {
|
|
1908
2009
|
// Before the approval gate: a dry run is unattended, so parking a write
|
|
@@ -1914,12 +2015,16 @@ export class SessionEngine {
|
|
|
1914
2015
|
annotations: args.tool.annotations,
|
|
1915
2016
|
},
|
|
1916
2017
|
}
|
|
1917
|
-
: {})), { dryRunResult: args.tool.definition.dryRunResult, input: validation.value }));
|
|
2018
|
+
: {})), { dryRunResult: args.tool.definition.dryRunResult, input: validation.value, asOf: args.record.asOf, timeArgs: args.tool.definition.timeArgs }));
|
|
1918
2019
|
if (decision.kind === "answer") {
|
|
1919
2020
|
(_a = this.stubbedCallIds.get(args.record.sessionId)) === null || _a === void 0 ? void 0 : _a.add(toolCallId);
|
|
1920
2021
|
this.logger(`[session] tool ${args.tool.name} answered by host (${toolCallId})`);
|
|
1921
2022
|
return normalizeToolResult(decision.result);
|
|
1922
2023
|
}
|
|
2024
|
+
if (decision.kind === "unbounded") {
|
|
2025
|
+
this.logger(`[session] tool ${args.tool.name} refused, unbounded_time (${toolCallId})`);
|
|
2026
|
+
return toolErrorResult(decision.message);
|
|
2027
|
+
}
|
|
1923
2028
|
if (decision.kind === "refuse") {
|
|
1924
2029
|
this.logger(`[session] tool ${args.tool.name} refused, effect unclassified (${toolCallId})`);
|
|
1925
2030
|
// The coverage signal: a run reports how much of itself was runnable.
|
|
@@ -1945,7 +2050,13 @@ export class SessionEngine {
|
|
|
1945
2050
|
}
|
|
1946
2051
|
const started = Date.now();
|
|
1947
2052
|
this.logger(`[session] tool ${args.tool.name} start`);
|
|
1948
|
-
const result = yield
|
|
2053
|
+
const result = yield mcpAuthAls.run({
|
|
2054
|
+
sessionId: args.record.sessionId,
|
|
2055
|
+
turnId: args.turnId,
|
|
2056
|
+
callId: toolCallId,
|
|
2057
|
+
toolName: args.tool.name,
|
|
2058
|
+
emit: args.emit,
|
|
2059
|
+
}, () => execute(validation.value, toolCtx));
|
|
1949
2060
|
this.logger(`[session] tool ${args.tool.name} done (${Date.now() - started}ms)`);
|
|
1950
2061
|
return normalizeToolResult(result);
|
|
1951
2062
|
}
|
|
@@ -1970,7 +2081,7 @@ export class SessionEngine {
|
|
|
1970
2081
|
args: args.args,
|
|
1971
2082
|
requestedAt,
|
|
1972
2083
|
};
|
|
1973
|
-
const resolutionPromise = this.approvals.wait(
|
|
2084
|
+
const resolutionPromise = this.approvals.wait(args.sessionId, pendingRecord);
|
|
1974
2085
|
yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: [
|
|
1975
2086
|
...((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
|
|
1976
2087
|
pendingRecord,
|
|
@@ -2056,6 +2167,245 @@ export class SessionEngine {
|
|
|
2056
2167
|
yield this.updateRecord(sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: ((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== callId) })); });
|
|
2057
2168
|
});
|
|
2058
2169
|
}
|
|
2170
|
+
wrapMcp(inner) {
|
|
2171
|
+
return {
|
|
2172
|
+
names: () => inner.names(),
|
|
2173
|
+
listTools: (name) => this.withMcpAuth(name, () => inner.listTools(name)),
|
|
2174
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => inner.callTool(name, tool, args)),
|
|
2175
|
+
};
|
|
2176
|
+
}
|
|
2177
|
+
advertiseMcp() {
|
|
2178
|
+
return {
|
|
2179
|
+
listTools: (name) => this.withMcpListAuth(name, () => this.mcpHost.listTools(name)),
|
|
2180
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => this.mcpHost.callTool(name, tool, args)),
|
|
2181
|
+
oneOff: (connection) => {
|
|
2182
|
+
const inner = this.mcpHost.oneOff(connection);
|
|
2183
|
+
return {
|
|
2184
|
+
listTools: () => this.withMcpListAuth(connection.name, () => inner.listTools()),
|
|
2185
|
+
callTool: (tool, args) => this.withMcpAuth(connection.name, () => inner.callTool(tool, args)),
|
|
2186
|
+
};
|
|
2187
|
+
},
|
|
2188
|
+
};
|
|
2189
|
+
}
|
|
2190
|
+
withMcpListAuth(connection, run) {
|
|
2191
|
+
const ctx = mcpAuthAls.getStore();
|
|
2192
|
+
if (ctx === undefined) {
|
|
2193
|
+
return this.withMcpAuth(connection, run);
|
|
2194
|
+
}
|
|
2195
|
+
const toolName = `mcp:${connection}`;
|
|
2196
|
+
return mcpAuthAls.run(Object.assign(Object.assign({}, ctx), { callId: newToolCallId(toolName), toolName }), () => this.withMcpAuth(connection, run));
|
|
2197
|
+
}
|
|
2198
|
+
withMcpAuth(connection, run) {
|
|
2199
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2200
|
+
try {
|
|
2201
|
+
return yield run();
|
|
2202
|
+
}
|
|
2203
|
+
catch (error) {
|
|
2204
|
+
if (!isMcpNeedsAuthError(error)) {
|
|
2205
|
+
throw error;
|
|
2206
|
+
}
|
|
2207
|
+
return yield this.parkAndRetryMcp(connection, run, error);
|
|
2208
|
+
}
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
parkAndRetryMcp(connection, run, error) {
|
|
2212
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2213
|
+
const ctx = mcpAuthAls.getStore();
|
|
2214
|
+
if (ctx === undefined) {
|
|
2215
|
+
throw error;
|
|
2216
|
+
}
|
|
2217
|
+
const minted = yield this.mintAuthUrl(connection, ctx.sessionId);
|
|
2218
|
+
if (minted === undefined) {
|
|
2219
|
+
throw error;
|
|
2220
|
+
}
|
|
2221
|
+
if ("authorized" in minted) {
|
|
2222
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2223
|
+
yield this.mcpHost.dropClient(connection);
|
|
2224
|
+
return yield this.withMcpAuth(connection, run);
|
|
2225
|
+
}
|
|
2226
|
+
if (minted.state !== "") {
|
|
2227
|
+
const bind = this.oauthHub.bind(minted.state, ctx.sessionId, ctx.callId);
|
|
2228
|
+
if (bind === "already_done") {
|
|
2229
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2230
|
+
yield this.mcpHost.dropClient(connection);
|
|
2231
|
+
return yield this.withMcpAuth(connection, run);
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
const decision = yield this.parkForAuth({
|
|
2235
|
+
sessionId: ctx.sessionId,
|
|
2236
|
+
turnId: ctx.turnId,
|
|
2237
|
+
callId: ctx.callId,
|
|
2238
|
+
toolName: ctx.toolName,
|
|
2239
|
+
connection,
|
|
2240
|
+
authUrl: minted.authUrl,
|
|
2241
|
+
emit: ctx.emit,
|
|
2242
|
+
});
|
|
2243
|
+
if (decision === "deny") {
|
|
2244
|
+
throw new Error(`MCP connection "${connection}" authorization was denied.`);
|
|
2245
|
+
}
|
|
2246
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2247
|
+
yield this.mcpHost.dropClient(connection);
|
|
2248
|
+
return yield this.withMcpAuth(connection, run);
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
persistHostedMcpOauth(connection) {
|
|
2252
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2253
|
+
const authored = this.authoredConnection(connection);
|
|
2254
|
+
if (authored === undefined ||
|
|
2255
|
+
!isHostOauthConnectionTransport(authored.transport)) {
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
yield this.oauthHub.persistFor(connection, authored.transport.url);
|
|
2259
|
+
});
|
|
2260
|
+
}
|
|
2261
|
+
authoredConnection(name) {
|
|
2262
|
+
var _a;
|
|
2263
|
+
var _b;
|
|
2264
|
+
return ((_b = this.project.agent.connections.find((entry) => entry.name === name)) !== null && _b !== void 0 ? _b : (_a = this.project.agent.hostConnections) === null || _a === void 0 ? void 0 : _a.find((entry) => entry.name === name));
|
|
2265
|
+
}
|
|
2266
|
+
mintAuthUrl(connection, sessionId) {
|
|
2267
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2268
|
+
const authored = this.authoredConnection(connection);
|
|
2269
|
+
if (authored === undefined) {
|
|
2270
|
+
return undefined;
|
|
2271
|
+
}
|
|
2272
|
+
if (isCursorAccountTransport(authored.transport)) {
|
|
2273
|
+
const probe = this.probeCursorAccount;
|
|
2274
|
+
if (probe === undefined) {
|
|
2275
|
+
return undefined;
|
|
2276
|
+
}
|
|
2277
|
+
const authUrl = yield probe(connection);
|
|
2278
|
+
if (authUrl === undefined || authUrl === "") {
|
|
2279
|
+
return undefined;
|
|
2280
|
+
}
|
|
2281
|
+
return { authUrl, state: "" };
|
|
2282
|
+
}
|
|
2283
|
+
if (!isHostOauthConnectionTransport(authored.transport)) {
|
|
2284
|
+
return undefined;
|
|
2285
|
+
}
|
|
2286
|
+
return yield this.oauthHub.mint({
|
|
2287
|
+
connection,
|
|
2288
|
+
url: authored.transport.url,
|
|
2289
|
+
type: authored.transport.type === "sse" ? "sse" : "http",
|
|
2290
|
+
headers: authored.transport.headers,
|
|
2291
|
+
sessionId,
|
|
2292
|
+
});
|
|
2293
|
+
});
|
|
2294
|
+
}
|
|
2295
|
+
parkForAuth(args) {
|
|
2296
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2297
|
+
const requestedAt = new Date().toISOString();
|
|
2298
|
+
const pendingRecord = {
|
|
2299
|
+
callId: args.callId,
|
|
2300
|
+
turnId: args.turnId,
|
|
2301
|
+
toolName: args.toolName,
|
|
2302
|
+
connection: args.connection,
|
|
2303
|
+
authUrl: args.authUrl,
|
|
2304
|
+
requestedAt,
|
|
2305
|
+
};
|
|
2306
|
+
const resolutionPromise = this.auths.wait(args.sessionId, pendingRecord);
|
|
2307
|
+
yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: [
|
|
2308
|
+
...((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
|
|
2309
|
+
pendingRecord,
|
|
2310
|
+
] })); });
|
|
2311
|
+
args.emit({
|
|
2312
|
+
type: "action.auth_requested",
|
|
2313
|
+
data: {
|
|
2314
|
+
callId: args.callId,
|
|
2315
|
+
toolName: args.toolName,
|
|
2316
|
+
connection: args.connection,
|
|
2317
|
+
authUrl: args.authUrl,
|
|
2318
|
+
},
|
|
2319
|
+
});
|
|
2320
|
+
this.logger(`[session] tool ${args.toolName} awaiting MCP auth ${args.connection} (${args.callId})`);
|
|
2321
|
+
const { decision, by } = yield resolutionPromise;
|
|
2322
|
+
yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId) })); });
|
|
2323
|
+
args.emit({
|
|
2324
|
+
type: "action.auth_resolved",
|
|
2325
|
+
data: {
|
|
2326
|
+
callId: args.callId,
|
|
2327
|
+
toolName: args.toolName,
|
|
2328
|
+
connection: args.connection,
|
|
2329
|
+
decision,
|
|
2330
|
+
by,
|
|
2331
|
+
},
|
|
2332
|
+
});
|
|
2333
|
+
this.logger(`[session] tool ${args.toolName} MCP auth ${decision} (${args.callId})`);
|
|
2334
|
+
return decision;
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
clearPendingAuths(sessionId) {
|
|
2338
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2339
|
+
yield this.oauthHub.abortSession(sessionId);
|
|
2340
|
+
const current = yield this.sessions.get(sessionId);
|
|
2341
|
+
if (current === undefined ||
|
|
2342
|
+
current.pendingAuths === undefined ||
|
|
2343
|
+
current.pendingAuths.length === 0) {
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
yield this.updateRecord(sessionId, (record) => (Object.assign(Object.assign({}, record), { pendingAuths: [] })));
|
|
2347
|
+
});
|
|
2348
|
+
}
|
|
2349
|
+
listPendingAuths(sessionId) {
|
|
2350
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2351
|
+
var _a;
|
|
2352
|
+
const record = yield this.sessions.get(sessionId);
|
|
2353
|
+
if (record === undefined) {
|
|
2354
|
+
return [];
|
|
2355
|
+
}
|
|
2356
|
+
const live = this.auths.list(sessionId);
|
|
2357
|
+
if (live.length > 0) {
|
|
2358
|
+
return live;
|
|
2359
|
+
}
|
|
2360
|
+
return (_a = record.pendingAuths) !== null && _a !== void 0 ? _a : [];
|
|
2361
|
+
});
|
|
2362
|
+
}
|
|
2363
|
+
resolveAuth(sessionId, callId, decision, auth, options) {
|
|
2364
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2365
|
+
var _a, _b;
|
|
2366
|
+
const record = yield this.sessions.get(sessionId);
|
|
2367
|
+
if (record === undefined) {
|
|
2368
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2369
|
+
}
|
|
2370
|
+
const ownershipAuth = (options === null || options === void 0 ? void 0 : options.asSessionOwner) === true ? record.auth : auth;
|
|
2371
|
+
if (!samePrincipal(record.auth, ownershipAuth)) {
|
|
2372
|
+
throw new SessionAuthError(sessionId);
|
|
2373
|
+
}
|
|
2374
|
+
const pending = (_b = ((_a = record.pendingAuths) !== null && _a !== void 0 ? _a : []).find((item) => item.callId === callId)) !== null && _b !== void 0 ? _b : this.auths.list(sessionId).find((item) => item.callId === callId);
|
|
2375
|
+
if (pending === undefined) {
|
|
2376
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2377
|
+
}
|
|
2378
|
+
const resolvedBy = approvalResolverLabel(auth);
|
|
2379
|
+
if (decision === "deny") {
|
|
2380
|
+
yield this.oauthHub.abortCall(sessionId, callId);
|
|
2381
|
+
}
|
|
2382
|
+
if (!this.auths.resolve(sessionId, callId, decision, resolvedBy)) {
|
|
2383
|
+
yield this.removePendingAuth(sessionId, callId);
|
|
2384
|
+
throw new AuthInterruptedError(sessionId, callId);
|
|
2385
|
+
}
|
|
2386
|
+
return pending;
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
removePendingAuth(sessionId, callId) {
|
|
2390
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2391
|
+
yield this.updateRecord(sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: ((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== callId) })); });
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
runWithMcpPark(sessionId, toolName, fn) {
|
|
2395
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2396
|
+
const active = this.activeToolContexts.get(sessionId);
|
|
2397
|
+
if (active === undefined) {
|
|
2398
|
+
return fn();
|
|
2399
|
+
}
|
|
2400
|
+
return mcpAuthAls.run({
|
|
2401
|
+
sessionId,
|
|
2402
|
+
turnId: active.turnId,
|
|
2403
|
+
callId: newToolCallId(toolName),
|
|
2404
|
+
toolName,
|
|
2405
|
+
emit: active.emit,
|
|
2406
|
+
}, fn);
|
|
2407
|
+
});
|
|
2408
|
+
}
|
|
2059
2409
|
// ==========================================================================
|
|
2060
2410
|
// Direct tool calls (deterministic invocation, no model turn)
|
|
2061
2411
|
// ==========================================================================
|
|
@@ -2070,8 +2420,10 @@ export class SessionEngine {
|
|
|
2070
2420
|
* and materialized workspace) and the call is recorded on the session's
|
|
2071
2421
|
* event stream as `actions.requested` / `action.result` under a
|
|
2072
2422
|
* per-call `turnId` (the call id), so it shows up in trajectories.
|
|
2073
|
-
*
|
|
2074
|
-
*
|
|
2423
|
+
* While a model turn is running, a read-effect call runs alongside it
|
|
2424
|
+
* (the turn's workspace is read as-is) and a write-effect call is
|
|
2425
|
+
* rejected with {@link SessionBusyError}. Without a session, the tool
|
|
2426
|
+
* runs against an **ephemeral** scratch
|
|
2075
2427
|
* workspace under `<stateRoot>/tool-calls/<callId>` (materialized with
|
|
2076
2428
|
* seed files, removed after the call returns).
|
|
2077
2429
|
*
|
|
@@ -2111,26 +2463,22 @@ export class SessionEngine {
|
|
|
2111
2463
|
throw new InvalidContinuationTokenError(`expected "<channelId>:<key>" (as /v1/sessions lists it), got "${options.continuationToken}"`);
|
|
2112
2464
|
}
|
|
2113
2465
|
// A token that maps to a live (or restorable) session takes the
|
|
2114
|
-
// session path: same owner check, same
|
|
2115
|
-
//
|
|
2116
|
-
//
|
|
2466
|
+
// session path: same owner check, same busy semantics, same event
|
|
2467
|
+
// recording as an explicit sessionId. Skipping those just because
|
|
2468
|
+
// the caller addressed the session by token would fail open.
|
|
2117
2469
|
record = (_b = (yield this.sessions.findByContinuation(continuation.channelId, continuation.key))) !== null && _b !== void 0 ? _b : (yield this.restoreSessionByContinuation(continuation.channelId, continuation.key));
|
|
2118
2470
|
if (record !== undefined && !samePrincipal(record.auth, auth)) {
|
|
2119
2471
|
throw new SessionAuthError(record.sessionId);
|
|
2120
2472
|
}
|
|
2121
2473
|
}
|
|
2122
2474
|
if (record !== undefined) {
|
|
2123
|
-
// Direct calls respect the same serialization as follow-up sends: a
|
|
2124
|
-
// running model turn owns the session workspace and event stream.
|
|
2125
|
-
if (this.activeTurns.has(record.sessionId) ||
|
|
2126
|
-
this.pendingTurns.has(record.sessionId)) {
|
|
2127
|
-
throw new SessionBusyError(record.sessionId);
|
|
2128
|
-
}
|
|
2129
2475
|
return {
|
|
2130
2476
|
record,
|
|
2131
2477
|
session: this.sessionInfo(record),
|
|
2132
2478
|
workspaceDir: this.harnessCwd(record),
|
|
2133
2479
|
scratchDir: undefined,
|
|
2480
|
+
busy: this.activeTurns.has(record.sessionId) ||
|
|
2481
|
+
this.pendingTurns.has(record.sessionId),
|
|
2134
2482
|
};
|
|
2135
2483
|
}
|
|
2136
2484
|
// No session behind the token (or no token at all): a synthetic
|
|
@@ -2150,6 +2498,7 @@ export class SessionEngine {
|
|
|
2150
2498
|
},
|
|
2151
2499
|
workspaceDir: scratchDir,
|
|
2152
2500
|
scratchDir,
|
|
2501
|
+
busy: false,
|
|
2153
2502
|
};
|
|
2154
2503
|
});
|
|
2155
2504
|
}
|
|
@@ -2182,8 +2531,32 @@ export class SessionEngine {
|
|
|
2182
2531
|
};
|
|
2183
2532
|
});
|
|
2184
2533
|
}
|
|
2534
|
+
/**
|
|
2535
|
+
* One catalog tool by name. Authored tools are local — their schema does
|
|
2536
|
+
* not depend on session identity or connection listings — so a detail
|
|
2537
|
+
* fetch for `ls` must not wait on every advertised MCP server.
|
|
2538
|
+
*/
|
|
2539
|
+
describeCatalogTool(toolName_1) {
|
|
2540
|
+
return __awaiter(this, arguments, void 0, function* (toolName, options = {}) {
|
|
2541
|
+
const authored = this.project.agent.tools.find((tool) => tool.name === toolName);
|
|
2542
|
+
if (authored !== undefined) {
|
|
2543
|
+
return { tool: authored };
|
|
2544
|
+
}
|
|
2545
|
+
const catalog = yield this.listCatalogTools(options);
|
|
2546
|
+
const tool = catalog.tools.find((entry) => entry.name === toolName);
|
|
2547
|
+
if (tool === undefined) {
|
|
2548
|
+
return {
|
|
2549
|
+
tool: undefined,
|
|
2550
|
+
availableTools: catalog.tools.map((entry) => entry.name),
|
|
2551
|
+
connectionErrors: catalog.connectionErrors,
|
|
2552
|
+
};
|
|
2553
|
+
}
|
|
2554
|
+
return { tool };
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2185
2557
|
callTool(toolName_1) {
|
|
2186
2558
|
return __awaiter(this, arguments, void 0, function* (toolName, input = {}, options = {}) {
|
|
2559
|
+
var _a;
|
|
2187
2560
|
const callId = newToolCallId(toolName);
|
|
2188
2561
|
// Identity resolves before the tool lookup: advertised connection tools
|
|
2189
2562
|
// only exist relative to a session (per-session auth), so finding them
|
|
@@ -2203,7 +2576,7 @@ export class SessionEngine {
|
|
|
2203
2576
|
const advertised = yield resolveAdvertisedTools({
|
|
2204
2577
|
agent: this.project.agent,
|
|
2205
2578
|
session,
|
|
2206
|
-
hostMcp: this.
|
|
2579
|
+
hostMcp: this.advertiseMcp(),
|
|
2207
2580
|
});
|
|
2208
2581
|
tool = advertised.find((t) => t.name === toolName);
|
|
2209
2582
|
}
|
|
@@ -2220,6 +2593,17 @@ export class SessionEngine {
|
|
|
2220
2593
|
if (!validation.ok) {
|
|
2221
2594
|
throw new ToolInputError({ toolName, detail: validation.message });
|
|
2222
2595
|
}
|
|
2596
|
+
// A running model turn owns the session workspace: a direct write would
|
|
2597
|
+
// race the harness's own edits, so it waits (409). A read runs alongside
|
|
2598
|
+
// the turn — the operator inspecting a live session is the point — and
|
|
2599
|
+
// its events stay separable by their per-call turnId. The effect can
|
|
2600
|
+
// depend on the input, hence deciding after validation; an MCP tool
|
|
2601
|
+
// nothing classifies is a write here, fail-closed.
|
|
2602
|
+
if (identity.busy &&
|
|
2603
|
+
record !== undefined &&
|
|
2604
|
+
directCallEffect(tool, validation.value) !== "read") {
|
|
2605
|
+
throw new SessionBusyError(record.sessionId);
|
|
2606
|
+
}
|
|
2223
2607
|
let workspaceUnavailable = false;
|
|
2224
2608
|
try {
|
|
2225
2609
|
yield mkdir(workspaceDir, { recursive: true });
|
|
@@ -2239,28 +2623,50 @@ export class SessionEngine {
|
|
|
2239
2623
|
// to write) authored tool may rely on ctx.workspaceDir being the
|
|
2240
2624
|
// real session workspace, so silently relocating it is not safe —
|
|
2241
2625
|
// fail typed instead.
|
|
2626
|
+
//
|
|
2627
|
+
// A shared `local.workspaceDir` (change-monitor's phantom /repo) is
|
|
2628
|
+
// that path by design. Relocating it to scratch would flag a false
|
|
2629
|
+
// session-state warning; keep the borrowed cwd and skip the flag.
|
|
2242
2630
|
const relocatable = tool.advertised === true ||
|
|
2243
|
-
|
|
2631
|
+
directCallEffect(tool, validation.value) === "read";
|
|
2244
2632
|
if (!relocatable) {
|
|
2245
2633
|
throw new WorkspaceUnavailableError(toolName, error);
|
|
2246
2634
|
}
|
|
2247
|
-
this.
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2635
|
+
if (((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined) {
|
|
2636
|
+
this.logger(`[session] tool ${toolName} harness cwd unavailable (direct ${callId}); keeping borrowed workspace`);
|
|
2637
|
+
}
|
|
2638
|
+
else {
|
|
2639
|
+
this.logger(`[session] tool ${toolName} workspace unavailable (direct ${callId}); using scratch: ${error instanceof Error ? error.message : String(error)}`);
|
|
2640
|
+
scratchDir = join(this.stateRoot, "tool-calls", callId);
|
|
2641
|
+
workspaceDir = scratchDir;
|
|
2642
|
+
workspaceUnavailable = true;
|
|
2643
|
+
yield mkdir(workspaceDir, { recursive: true });
|
|
2644
|
+
}
|
|
2252
2645
|
}
|
|
2253
2646
|
// Match what a model turn would see: session workspaces get the same
|
|
2254
2647
|
// scaffold as executeTurn (seeds only before the first turn); scratch
|
|
2255
|
-
// workspaces are materialized fresh.
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2648
|
+
// workspaces are materialized fresh. A read running alongside a turn
|
|
2649
|
+
// that has materialized its workspace leaves it alone — re-seeding
|
|
2650
|
+
// would restore files the agent deleted mid-turn. A session is busy
|
|
2651
|
+
// from admit, though, so a read that lands while the first turn is
|
|
2652
|
+
// still queued or scaffolding materializes the workspace itself, seeds
|
|
2653
|
+
// included (turnCount is already 1 for that pending first turn).
|
|
2654
|
+
const turnMaterialized = record !== undefined &&
|
|
2655
|
+
identity.busy &&
|
|
2656
|
+
this.materializedWorkspaces.has(record.sessionId);
|
|
2657
|
+
if (record === undefined || workspaceUnavailable || !turnMaterialized) {
|
|
2658
|
+
yield materializeWorkspace({
|
|
2659
|
+
agent: this.project.agent,
|
|
2660
|
+
workspaceDir: record === undefined || workspaceUnavailable
|
|
2661
|
+
? workspaceDir
|
|
2662
|
+
: this.scaffoldDir(record),
|
|
2663
|
+
seed: record === undefined ||
|
|
2664
|
+
workspaceUnavailable ||
|
|
2665
|
+
record.turnCount === 0 ||
|
|
2666
|
+
(identity.busy && record.turnCount === 1),
|
|
2667
|
+
stateRoot: this.stateRoot,
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2264
2670
|
// A per-call turnId keeps these events visible in trajectories (which
|
|
2265
2671
|
// drop turn-less action events) without attributing them to a model turn.
|
|
2266
2672
|
if (record !== undefined) {
|
|
@@ -2275,7 +2681,7 @@ export class SessionEngine {
|
|
|
2275
2681
|
// `handle.callTool` performs a write just as readily.
|
|
2276
2682
|
const decision = decideToolCall(Object.assign(Object.assign({ dryRun: (record === null || record === void 0 ? void 0 : record.dryRun) === true, effect: tool.definition.effect }, (tool.advertised === true
|
|
2277
2683
|
? { mcpTool: { name: tool.name, annotations: tool.annotations } }
|
|
2278
|
-
: {})), { dryRunResult: tool.definition.dryRunResult, input: validation.value }));
|
|
2684
|
+
: {})), { dryRunResult: tool.definition.dryRunResult, input: validation.value, asOf: record === null || record === void 0 ? void 0 : record.asOf, timeArgs: tool.definition.timeArgs }));
|
|
2279
2685
|
const started = Date.now();
|
|
2280
2686
|
this.logger(`[session] tool ${toolName} start (direct ${callId})`);
|
|
2281
2687
|
let result;
|
|
@@ -2288,7 +2694,12 @@ export class SessionEngine {
|
|
|
2288
2694
|
void this.appendEvent(live.sessionId, Object.assign(Object.assign({}, payload), { turnId: callId })).catch(() => { });
|
|
2289
2695
|
};
|
|
2290
2696
|
try {
|
|
2291
|
-
if (decision.kind === "
|
|
2697
|
+
if (decision.kind === "unbounded") {
|
|
2698
|
+
this.logger(`[session] tool ${toolName} refused, unbounded_time (direct ${callId})`);
|
|
2699
|
+
result = normalizeToolResult(toolErrorResult(decision.message));
|
|
2700
|
+
isError = true;
|
|
2701
|
+
}
|
|
2702
|
+
else if (decision.kind === "refuse") {
|
|
2292
2703
|
this.logger(`[session] tool ${toolName} refused, effect unclassified (direct ${callId})`);
|
|
2293
2704
|
emit === null || emit === void 0 ? void 0 : emit({
|
|
2294
2705
|
type: "action.effect_unclassified",
|
|
@@ -2303,7 +2714,7 @@ export class SessionEngine {
|
|
|
2303
2714
|
isError = false;
|
|
2304
2715
|
}
|
|
2305
2716
|
else {
|
|
2306
|
-
|
|
2717
|
+
const run = () => execute(validation.value, {
|
|
2307
2718
|
toolCallId: callId,
|
|
2308
2719
|
session,
|
|
2309
2720
|
workspaceDir,
|
|
@@ -2321,7 +2732,17 @@ export class SessionEngine {
|
|
|
2321
2732
|
: session.auth })),
|
|
2322
2733
|
getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
|
|
2323
2734
|
emit,
|
|
2324
|
-
|
|
2735
|
+
now: sessionClock(record === null || record === void 0 ? void 0 : record.asOf),
|
|
2736
|
+
});
|
|
2737
|
+
result = normalizeToolResult(record !== undefined && emit !== undefined
|
|
2738
|
+
? yield mcpAuthAls.run({
|
|
2739
|
+
sessionId: record.sessionId,
|
|
2740
|
+
turnId: callId,
|
|
2741
|
+
callId,
|
|
2742
|
+
toolName,
|
|
2743
|
+
emit,
|
|
2744
|
+
}, run)
|
|
2745
|
+
: yield run());
|
|
2325
2746
|
isError = isErrorEnvelope(result);
|
|
2326
2747
|
}
|
|
2327
2748
|
}
|
|
@@ -2401,6 +2822,10 @@ export class SessionEngine {
|
|
|
2401
2822
|
if (decision.kind === "run") {
|
|
2402
2823
|
return { answered: false };
|
|
2403
2824
|
}
|
|
2825
|
+
if (decision.kind === "unbounded") {
|
|
2826
|
+
this.logger(`[session] bridged tool ${args.toolName} refused, unbounded_time (${args.sessionId})`);
|
|
2827
|
+
return { answered: true, result: toolErrorResult(decision.message) };
|
|
2828
|
+
}
|
|
2404
2829
|
if (decision.kind === "refuse") {
|
|
2405
2830
|
this.logger(`[session] bridged tool ${args.toolName} refused, effect unclassified (${args.sessionId})`);
|
|
2406
2831
|
context.emit({
|
|
@@ -2688,6 +3113,64 @@ export class SessionEngine {
|
|
|
2688
3113
|
};
|
|
2689
3114
|
});
|
|
2690
3115
|
}
|
|
3116
|
+
/**
|
|
3117
|
+
* The single newest session visible to `caller` (`GET /v1/sessions?latest=1`).
|
|
3118
|
+
*
|
|
3119
|
+
* Short-circuits the full listing: the local store already holds every
|
|
3120
|
+
* session this host created plus the startup bulk restore, so the common
|
|
3121
|
+
* case answers from memory without a sink round trip. Only when the local
|
|
3122
|
+
* store has nothing for the caller (fresh host, `restore: "off"`) does it
|
|
3123
|
+
* ask the sink — for a bounded newest-written window when the backend
|
|
3124
|
+
* can order by write time, else the full list. A session written by
|
|
3125
|
+
* another replica moments ago can lose to a local one; callers that need
|
|
3126
|
+
* the exact newest row should use the full listing.
|
|
3127
|
+
*/
|
|
3128
|
+
latestSessionWithSource(caller, options) {
|
|
3129
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3130
|
+
var _a;
|
|
3131
|
+
var _b;
|
|
3132
|
+
const includeAll = (_b = options === null || options === void 0 ? void 0 : options.includeAll) !== null && _b !== void 0 ? _b : false;
|
|
3133
|
+
const newestVisible = (records) => {
|
|
3134
|
+
let newest;
|
|
3135
|
+
for (const record of records) {
|
|
3136
|
+
if (!(includeAll || samePrincipal(record.auth, caller))) {
|
|
3137
|
+
continue;
|
|
3138
|
+
}
|
|
3139
|
+
if (newest === undefined ||
|
|
3140
|
+
record.updatedAt.localeCompare(newest.updatedAt) > 0) {
|
|
3141
|
+
newest = record;
|
|
3142
|
+
}
|
|
3143
|
+
}
|
|
3144
|
+
return newest;
|
|
3145
|
+
};
|
|
3146
|
+
const local = newestVisible(yield this.sessions.list());
|
|
3147
|
+
if (local !== undefined) {
|
|
3148
|
+
return {
|
|
3149
|
+
sessions: this.summarizeSessionRecords([local], caller, includeAll),
|
|
3150
|
+
source: "local",
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
if (((_a = this.storage) === null || _a === void 0 ? void 0 : _a.isCursorHosted) === true) {
|
|
3154
|
+
try {
|
|
3155
|
+
const window = yield this.storage.listNewestSessionsFromSink(LATEST_SESSION_SINK_WINDOW);
|
|
3156
|
+
if (window !== undefined) {
|
|
3157
|
+
const stored = newestVisible(window);
|
|
3158
|
+
if (stored !== undefined) {
|
|
3159
|
+
return {
|
|
3160
|
+
sessions: this.summarizeSessionRecords([stored], caller, includeAll),
|
|
3161
|
+
source: "cursor-hosted",
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
catch (error) {
|
|
3167
|
+
this.logger(`[agent-sdk] cursor-hosted newest-session lookup failed; falling back to the full list: ${describeError(error)}`);
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
const listed = yield this.listSessionsWithSource(caller, options);
|
|
3171
|
+
return { sessions: listed.sessions.slice(0, 1), source: listed.source };
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
2691
3174
|
/**
|
|
2692
3175
|
* Durable sink rows plus local records (local wins on the same id when
|
|
2693
3176
|
* newer — covers debounce windows that have not flushed yet). Newest
|
|
@@ -2734,6 +3217,7 @@ export class SessionEngine {
|
|
|
2734
3217
|
: undefined,
|
|
2735
3218
|
createdAt: record.createdAt,
|
|
2736
3219
|
updatedAt: record.updatedAt,
|
|
3220
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
2737
3221
|
continuationToken: record.httpContinuationKey !== undefined
|
|
2738
3222
|
? formatContinuationToken(HTTP_CHANNEL_ID, record.httpContinuationKey)
|
|
2739
3223
|
: record.continuationKey === null
|
|
@@ -3006,7 +3490,7 @@ export class SessionEngine {
|
|
|
3006
3490
|
this.coalesceDrains.has(sessionId));
|
|
3007
3491
|
}
|
|
3008
3492
|
sessionInfo(record) {
|
|
3009
|
-
return Object.assign(Object.assign(Object.assign(
|
|
3493
|
+
return Object.assign(Object.assign(Object.assign({ id: record.sessionId, channelId: record.channelId, mode: record.mode, purpose: record.purpose === "eval" ? "eval" : "live", dryRun: record.dryRun === true ? true : undefined, asOf: record.asOf, auth: record.auth, continuationKey: record.continuationKey }, (record.abs === undefined ? {} : { abs: record.abs })), (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined
|
|
3010
3494
|
? {}
|
|
3011
3495
|
: { sdkAgentId: record.sdkAgentId }));
|
|
3012
3496
|
}
|
|
@@ -3291,6 +3775,8 @@ export class SessionEngine {
|
|
|
3291
3775
|
var _a, _b, _c, _d, _e;
|
|
3292
3776
|
(_a = this.stopMemoryTelemetry) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
3293
3777
|
this.approvals.cancelAll("deny");
|
|
3778
|
+
this.auths.cancelAll("deny");
|
|
3779
|
+
yield this.oauthHub.close();
|
|
3294
3780
|
yield Promise.allSettled([...this.activeTurns.values()]);
|
|
3295
3781
|
yield Promise.allSettled([...this.backgroundWork]);
|
|
3296
3782
|
yield Promise.allSettled([...this.dispatchChains.values()]);
|
|
@@ -3307,6 +3793,12 @@ export class SessionEngine {
|
|
|
3307
3793
|
}
|
|
3308
3794
|
/** Parallel event-stream loads during the startup bulk restore. */
|
|
3309
3795
|
const RESTORE_FETCH_CONCURRENCY = 8;
|
|
3796
|
+
/**
|
|
3797
|
+
* Newest-written sink rows fetched for `GET /v1/sessions?latest=1` when the
|
|
3798
|
+
* local store has nothing for the caller. Wide enough that the caller's own
|
|
3799
|
+
* session is normally inside it even when other principals are busier.
|
|
3800
|
+
*/
|
|
3801
|
+
const LATEST_SESSION_SINK_WINDOW = 50;
|
|
3310
3802
|
/** NDJSON payload for a restored event stream; undefined when empty. */
|
|
3311
3803
|
function mergePendingImages(current, incoming) {
|
|
3312
3804
|
if (incoming === undefined) {
|