@cursor/july 0.1.95 → 0.1.97
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 +3 -4
- package/README.md +3 -2
- package/dist/bin/agent-serve.js +14 -3
- package/dist/channels/deployments/types.d.ts +1 -1
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/deployments/types.js +1 -0
- package/dist/channels/origin/api.d.ts +72 -15
- package/dist/channels/origin/api.d.ts.map +1 -1
- package/dist/channels/origin/api.js +185 -28
- package/dist/channels/origin/binding.d.ts +13 -2
- package/dist/channels/origin/binding.d.ts.map +1 -1
- package/dist/channels/origin/binding.js +41 -25
- package/dist/channels/origin/checks.d.ts +6 -6
- package/dist/channels/origin/checks.js +6 -6
- package/dist/channels/origin/cursor-account.d.ts +5 -3
- package/dist/channels/origin/cursor-account.d.ts.map +1 -1
- package/dist/channels/origin/cursor-account.js +5 -3
- 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.map +1 -1
- package/dist/channels/origin/origin-channel.js +3 -0
- 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/api.d.ts +9 -0
- package/dist/channels/slack/api.d.ts.map +1 -1
- package/dist/channels/slack/api.js +36 -5
- 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 +110 -81
- package/dist/channels/slack/dispatch.d.ts +7 -1
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +47 -23
- package/dist/channels/slack/inbound.d.ts +8 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +32 -0
- 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 +26 -29
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +172 -73
- package/dist/channels/slack/manifest.d.ts +1 -1
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +6 -2
- package/dist/channels/slack/nudge-carrier.d.ts +2 -0
- package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
- package/dist/channels/slack/nudge-carrier.js +48 -1
- 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 +3 -2
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
- package/dist/channels/slack/tool-approval-events.js +91 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.BBj0klBO.js → app.B3rWNYE1.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CUEXpL78.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DabmQnia.js → theme.B40_SXuv.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/{index.md.BoHaMdeZ.lean.js → index.md.CVeRUOeZ.lean.js} +1 -1
- 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.BsMOxDvh.js → reference_cli.md.RyZf5OTE.js} +10 -6
- package/dist/docs/assets/{reference_cli.md.BsMOxDvh.lean.js → reference_cli.md.RyZf5OTE.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.5zOAbV86.js} +1 -1
- package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
- package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.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/templates_code-wiki.md.CDYZIBFN.js +2 -0
- package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.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/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 +7 -3
- package/dist/docs/llms-full.txt +398 -653
- 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 +14 -10
- package/dist/docs/reference/cli.md +39 -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 +6 -6
- package/dist/docs/reference/http-api.md +2 -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 +6 -6
- package/dist/docs/reference/tools.md +9 -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 +28 -0
- package/dist/docs/templates/code-wiki.md +91 -0
- package/dist/docs/templates/demo.html +5 -5
- 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 +26 -4
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +32 -4
- package/dist/internal/advertise-tools.d.ts +2 -0
- package/dist/internal/advertise-tools.d.ts.map +1 -1
- package/dist/internal/advertise-tools.js +30 -10
- 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/bounded.d.ts +7 -0
- package/dist/internal/bounded.d.ts.map +1 -0
- package/dist/internal/bounded.js +32 -0
- package/dist/internal/chat-client.js +10 -0
- package/dist/internal/cli-deploy.d.ts +5 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +57 -18
- 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/conversation-mirror.d.ts +11 -8
- package/dist/internal/conversation-mirror.d.ts.map +1 -1
- package/dist/internal/conversation-mirror.js +116 -55
- 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/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +22 -48
- package/dist/internal/cursor/session-token.d.ts +45 -0
- package/dist/internal/cursor/session-token.d.ts.map +1 -0
- package/dist/internal/cursor/session-token.js +99 -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 +2 -0
- 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 +61 -0
- package/dist/internal/framework-file-storage.d.ts.map +1 -0
- package/dist/internal/framework-file-storage.js +210 -0
- package/dist/internal/framework-storage-selection.d.ts +11 -5
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +38 -11
- package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +7 -0
- package/dist/internal/hosted-delivery.d.ts +13 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +64 -19
- package/dist/internal/hosted-execution-diag.d.ts +48 -0
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
- package/dist/internal/hosted-execution-diag.js +150 -0
- package/dist/internal/http-channel.d.ts.map +1 -1
- package/dist/internal/http-channel.js +81 -1
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -0
- package/dist/internal/install-cursor-skills.d.ts.map +1 -1
- package/dist/internal/install-cursor-skills.js +1 -3
- 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/managed-delivery.d.ts +80 -0
- package/dist/internal/managed-delivery.d.ts.map +1 -0
- package/dist/internal/managed-delivery.js +86 -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/otel/collector.d.ts.map +1 -1
- package/dist/internal/otel/collector.js +22 -9
- package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
- package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
- package/dist/internal/runtime-dispatch-runner.js +7 -0
- package/dist/internal/sdk-runner.d.ts +11 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +39 -8
- package/dist/internal/server.d.ts +3 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +49 -21
- package/dist/internal/session-engine.d.ts +101 -3
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +577 -46
- 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/steering.d.ts +15 -0
- package/dist/internal/steering.d.ts.map +1 -0
- package/dist/internal/steering.js +18 -0
- package/dist/internal/storage-coordinator.d.ts +7 -0
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +28 -5
- 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 +9 -1
- package/dist/internal/tool-policy.d.ts.map +1 -1
- package/dist/internal/tool-policy.js +13 -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-CZA1uEWA.js → index-DrkI6y5O.js} +51 -51
- package/dist/playground/assets/index-DzNGwm7q.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +9 -6
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +16 -3
- package/dist/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/types.d.ts +65 -8
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +7 -3
- package/docs/deployment.md +5 -4
- 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 +39 -16
- package/docs/reference/connections.md +6 -3
- package/docs/reference/http-api.md +2 -1
- package/docs/reference/sessions.md +4 -2
- package/docs/reference/tools.md +9 -2
- package/docs/templates/agents-md.md +110 -0
- package/docs/templates/code-wiki.md +96 -0
- package/docs/troubleshooting.md +2 -2
- package/package.json +2 -2
- package/skills/ab/SKILL.md +13 -97
- package/skills/create-agent/SKILL.md +124 -223
- package/skills/debug/SKILL.md +34 -68
- package/skills/evals/SKILL.md +55 -180
- package/skills/framework-map/SKILL.md +83 -145
- package/skills/github/SKILL.md +53 -111
- package/skills/hillclimb/SKILL.md +40 -163
- package/skills/mcp-auth/SKILL.md +51 -107
- package/skills/otel/SKILL.md +22 -68
- package/skills/setup-slack/SKILL.md +91 -212
- package/src/bin/agent-serve.ts +13 -1
- package/src/channels/deployments/types.ts +1 -0
- package/src/channels/origin/api.ts +264 -33
- package/src/channels/origin/binding.ts +57 -28
- package/src/channels/origin/checks.ts +6 -6
- package/src/channels/origin/cursor-account.ts +5 -3
- package/src/channels/origin/index.ts +1 -0
- package/src/channels/origin/origin-channel.ts +4 -0
- package/src/channels/origin/reactions.ts +82 -0
- package/src/channels/origin/types.ts +11 -0
- package/src/channels/slack/api.ts +41 -5
- package/src/channels/slack/approvals.ts +105 -0
- package/src/channels/slack/cli-project.ts +267 -0
- package/src/channels/slack/defaults.ts +122 -93
- package/src/channels/slack/dispatch.ts +56 -22
- package/src/channels/slack/inbound.ts +43 -0
- package/src/channels/slack/init.ts +10 -5
- package/src/channels/slack/interactive.ts +35 -9
- package/src/channels/slack/live-delivery.ts +235 -114
- package/src/channels/slack/manifest.ts +6 -2
- package/src/channels/slack/nudge-carrier.ts +48 -1
- package/src/channels/slack/setup.ts +10 -6
- package/src/channels/slack/slack-channel.ts +3 -0
- package/src/channels/slack/tool-approval-events.ts +93 -0
- package/src/evals/run-facts.ts +32 -0
- package/src/files-backends/cursor-hosted.ts +40 -7
- package/src/internal/advertise-tools.ts +49 -12
- package/src/internal/approval-gate.ts +20 -29
- package/src/internal/bounded.ts +28 -0
- package/src/internal/chat-client.ts +19 -0
- package/src/internal/cli-deploy.ts +91 -9
- 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/conversation-mirror.ts +145 -69
- package/src/internal/cursor/account-mcp.ts +21 -0
- package/src/internal/cursor/backend-client.ts +34 -58
- package/src/internal/cursor/session-token.ts +115 -0
- package/src/internal/cursor-agent-template.ts +2 -0
- package/src/internal/deploy-client.ts +43 -0
- package/src/internal/env-file.ts +84 -0
- package/src/internal/framework-file-storage.ts +228 -0
- package/src/internal/framework-storage-selection.ts +51 -11
- package/src/internal/hosted-delivery-protocol.ts +7 -0
- package/src/internal/hosted-delivery.ts +107 -24
- package/src/internal/hosted-execution-diag.ts +207 -0
- package/src/internal/http-channel.ts +99 -0
- package/src/internal/init-project.ts +4 -0
- package/src/internal/install-cursor-skills.ts +1 -4
- package/src/internal/logs-client.ts +5 -0
- package/src/internal/managed-delivery.ts +125 -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/otel/collector.ts +30 -0
- package/src/internal/runtime-dispatch-runner.ts +10 -0
- package/src/internal/sdk-runner.ts +56 -8
- package/src/internal/server.ts +61 -9
- package/src/internal/session-engine.ts +723 -60
- package/src/internal/sessions-client.ts +17 -8
- package/src/internal/steering.ts +17 -0
- package/src/internal/storage-coordinator.ts +38 -8
- package/src/internal/storage-roles.ts +7 -0
- package/src/internal/stream-progress.ts +19 -0
- package/src/internal/tool-policy.ts +18 -0
- package/src/internal/trajectory.ts +10 -0
- package/src/internal/turn-outbox.ts +85 -0
- package/src/storage-backends/cursor-hosted.ts +22 -8
- package/src/storage.ts +12 -0
- package/src/types.ts +70 -9
- 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/templates/code-wiki/README.md +34 -0
- package/templates/code-wiki/agent/agent.ts +8 -0
- package/templates/code-wiki/agent/channels/github.ts +144 -0
- package/templates/code-wiki/agent/instructions.md +39 -0
- package/templates/code-wiki/agent/lib/repos.ts +5 -0
- package/templates/code-wiki/agent/lib/wiki.ts +22 -0
- package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
- package/templates/code-wiki/evals/evals.config.ts +5 -0
- package/templates/code-wiki/evals/wiki.eval.ts +132 -0
- package/templates/code-wiki/init.json +25 -0
- package/templates/code-wiki/package.json +18 -0
- package/templates/code-wiki/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.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.BoHaMdeZ.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/internal/deployment-scm-event-stream.d.ts +0 -116
- package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
- package/dist/internal/deployment-scm-event-stream.js +0 -474
- package/dist/playground/assets/index-CF7hUDnQ.css +0 -1
- package/docs/design/agsh.md +0 -406
- package/src/internal/deployment-scm-event-stream.ts +0 -593
- /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.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.5zOAbV86.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/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.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,11 +25,11 @@ 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";
|
|
34
35
|
import { SessionAuthError, samePrincipal } from "./auth.js";
|
|
@@ -47,19 +48,22 @@ import { createHostPlatforms, createHostSlackClient, } from "./host-platforms.js
|
|
|
47
48
|
import { HostedAdmissionLocalSendError, isDuringHostedAdmission, } from "./hosted-admission-context.js";
|
|
48
49
|
import { formatContinuationToken, newContinuationKey, newSessionId, newToolCallId, parseContinuationToken, } from "./ids.js";
|
|
49
50
|
import { validateToolInput } from "./json-schema.js";
|
|
50
|
-
import { McpHost } from "./mcp-host.js";
|
|
51
|
+
import { isMcpNeedsAuthError, McpHost } from "./mcp-host.js";
|
|
52
|
+
import { McpOauthOnDemandHub } from "./mcp-oauth-on-demand.js";
|
|
51
53
|
import { shouldStartProcessMemoryTelemetry, startProcessMemoryTelemetry, } from "./process-memory-telemetry.js";
|
|
52
54
|
import { buildSessionContextBlock, SCHEDULE_CHANNEL_PREFIX, } from "./prompt-context.js";
|
|
53
55
|
import { assertCloudCanHonorAccountServersFilters } from "./resolved-connections.js";
|
|
54
56
|
import { sessionExternalLinks } from "./session-links.js";
|
|
55
57
|
import { SessionStore } from "./session-store.js";
|
|
56
58
|
import { syncSkillsToStore } from "./skills-sync.js";
|
|
59
|
+
import { steeringEnabled } from "./steering.js";
|
|
57
60
|
import { StorageCoordinator } from "./storage-coordinator.js";
|
|
58
|
-
import { decideToolCall,
|
|
61
|
+
import { decideToolCall, directCallEffect } from "./tool-policy.js";
|
|
59
62
|
import { normalizeToolResult, toolCallErrorMessage } from "./tool-result.js";
|
|
60
63
|
import { estimateTurnCostFromApi, roundCostUsd, } from "./turn-cost.js";
|
|
61
64
|
import { TurnGovernor } from "./turn-governor.js";
|
|
62
65
|
import { TurnLeaseStore } from "./turn-lease-store.js";
|
|
66
|
+
import { TurnOutbox } from "./turn-outbox.js";
|
|
63
67
|
import { buildAgentsMdContent, buildCloudMemorySection, ensureOwnGitRoot, isNestedInGitRepo, materializeWorkspace, SERVER_TOOLS_MCP_SERVER_NAME, writeWorkspaceFiles, } from "./workspace.js";
|
|
64
68
|
export const HTTP_CHANNEL_ID = "http";
|
|
65
69
|
export { SCHEDULE_CHANNEL_PREFIX } from "./prompt-context.js";
|
|
@@ -133,6 +137,19 @@ export class ApprovalInterruptedError extends Error {
|
|
|
133
137
|
this.name = "ApprovalInterruptedError";
|
|
134
138
|
}
|
|
135
139
|
}
|
|
140
|
+
export class AuthNotFoundError extends Error {
|
|
141
|
+
constructor(sessionId, callId) {
|
|
142
|
+
super(`No pending MCP auth for session ${sessionId} call ${callId}`);
|
|
143
|
+
this.name = "AuthNotFoundError";
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export class AuthInterruptedError extends Error {
|
|
147
|
+
constructor(sessionId, callId) {
|
|
148
|
+
super(`MCP auth for session ${sessionId} call ${callId} is no longer live (host may have restarted while the tool was parked)`);
|
|
149
|
+
this.name = "AuthInterruptedError";
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const mcpAuthAls = new AsyncLocalStorage();
|
|
136
153
|
export class SessionEngine {
|
|
137
154
|
constructor(options) {
|
|
138
155
|
var _a, _b, _c, _d, _e;
|
|
@@ -142,6 +159,13 @@ export class SessionEngine {
|
|
|
142
159
|
/** Sessions with a turn being admitted (before the run promise exists). */
|
|
143
160
|
this.pendingTurns = new Set();
|
|
144
161
|
this.activeTurns = new Map();
|
|
162
|
+
/**
|
|
163
|
+
* Sessions whose workspace a turn in this process has materialized. A
|
|
164
|
+
* session is busy from admit — before the governor slot and the turn's
|
|
165
|
+
* own materialize — so a read running alongside a turn consults this,
|
|
166
|
+
* not busy, to know whether the workspace is ready to be left alone.
|
|
167
|
+
*/
|
|
168
|
+
this.materializedWorkspaces = new Set();
|
|
145
169
|
/** Context used by the cloud turn's HTTP MCP callbacks. */
|
|
146
170
|
this.activeToolContexts = new Map();
|
|
147
171
|
/**
|
|
@@ -178,6 +202,8 @@ export class SessionEngine {
|
|
|
178
202
|
this.dispatchChains = new Map();
|
|
179
203
|
this.backgroundWork = new Set();
|
|
180
204
|
this.approvals = new ApprovalGate();
|
|
205
|
+
this.auths = new ApprovalGate();
|
|
206
|
+
this.oauthHub = new McpOauthOnDemandHub();
|
|
181
207
|
/** Serve-time resolved connections (see {@link setResolvedConnections}). */
|
|
182
208
|
this.resolvedConnections = [];
|
|
183
209
|
/** Sessions whose completed turns are fully priced (no further backfill). */
|
|
@@ -202,6 +228,7 @@ export class SessionEngine {
|
|
|
202
228
|
this.nudgeStore = new SlackNudgeStore(join(options.stateRoot, "slack-nudges"));
|
|
203
229
|
this.askDedupe = new SlackAskDedupeStore(join(options.stateRoot, "slack-asks"));
|
|
204
230
|
this.turnLeases = new TurnLeaseStore(join(options.stateRoot, "turn-leases"));
|
|
231
|
+
this.outbox = new TurnOutbox(join(options.stateRoot, "outbox"));
|
|
205
232
|
this.logs = new EventLogStore((sessionId) => this.sessions.eventFilePath(sessionId), (sessionId, error) => this.logger(`[agent-sdk] failed to persist events for ${sessionId}: ${describeError(error)}`));
|
|
206
233
|
// Framework session state (records, event chunks, continuations) is
|
|
207
234
|
// mirrored to whatever this selects; see framework-storage-selection.ts
|
|
@@ -283,10 +310,20 @@ export class SessionEngine {
|
|
|
283
310
|
setCloudServerToolsConnection(connection) {
|
|
284
311
|
this.cloudServerToolsConnection = connection;
|
|
285
312
|
}
|
|
313
|
+
setMcpOauthCallbackUrl(url) {
|
|
314
|
+
this.oauthHub.callbackUrl = url;
|
|
315
|
+
this.mcpHost.setOAuthRedirectUrl(url);
|
|
316
|
+
}
|
|
317
|
+
setCursorAccountAuthProbe(probe) {
|
|
318
|
+
this.probeCursorAccount = probe;
|
|
319
|
+
}
|
|
320
|
+
get oauthCallbackHub() {
|
|
321
|
+
return this.oauthHub;
|
|
322
|
+
}
|
|
286
323
|
/** Shared host services (MCP / GitHub / Slack / reminders / evals / kv / files / otel / Slack nudges). */
|
|
287
324
|
get host() {
|
|
288
325
|
return {
|
|
289
|
-
mcp: this.mcpHost,
|
|
326
|
+
mcp: this.wrapMcp(this.mcpHost),
|
|
290
327
|
github: this.platforms.github,
|
|
291
328
|
slack: this.platforms.slack,
|
|
292
329
|
reminders: this.reminderApi,
|
|
@@ -396,6 +433,9 @@ export class SessionEngine {
|
|
|
396
433
|
this.hasBorrowedWorkspace(record)) {
|
|
397
434
|
return;
|
|
398
435
|
}
|
|
436
|
+
if (steeringEnabled()) {
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
399
439
|
try {
|
|
400
440
|
yield materializeSlackNudgeCarrier({
|
|
401
441
|
harnessCwd,
|
|
@@ -580,12 +620,33 @@ export class SessionEngine {
|
|
|
580
620
|
const now = new Date().toISOString();
|
|
581
621
|
const channelState = this.initialChannelState(channelId, sendOptions.state);
|
|
582
622
|
const purpose = sendOptions.purpose === "eval" ? "eval" : "live";
|
|
583
|
-
let record =
|
|
623
|
+
let record = {
|
|
624
|
+
sessionId: (_d = sendOptions.hostedSessionId) !== null && _d !== void 0 ? _d : newSessionId(),
|
|
625
|
+
channelId,
|
|
626
|
+
mode: (_e = sendOptions.mode) !== null && _e !== void 0 ? _e : "chat",
|
|
627
|
+
purpose,
|
|
628
|
+
dryRun: sendOptions.dryRun === true ? true : undefined,
|
|
629
|
+
continuationKey: (_f = sendOptions.continuationToken) !== null && _f !== void 0 ? _f : newContinuationKey(),
|
|
630
|
+
auth,
|
|
631
|
+
title: sendOptions.title,
|
|
632
|
+
channelState,
|
|
633
|
+
workspaceOverride: sendOptions.workspaceDir,
|
|
634
|
+
pendingWorkspaceFiles: sendOptions.workspaceFiles,
|
|
635
|
+
pendingImages: sendOptions.images,
|
|
636
|
+
cloudOverride: sendOptions.cloud,
|
|
637
|
+
sdkAgentId: sendOptions.sdkAgentId,
|
|
638
|
+
turnCount: 0,
|
|
639
|
+
createdAt: now,
|
|
640
|
+
updatedAt: now,
|
|
641
|
+
};
|
|
584
642
|
yield this.sessions.save(record);
|
|
585
643
|
(_a = this.storage) === null || _a === void 0 ? void 0 : _a.sessionRecord(record);
|
|
586
644
|
yield this.appendEvent(record.sessionId, {
|
|
587
645
|
type: "session.started",
|
|
588
|
-
data: {
|
|
646
|
+
data: {
|
|
647
|
+
channelId,
|
|
648
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
649
|
+
},
|
|
589
650
|
});
|
|
590
651
|
record = yield this.enrollABs(record);
|
|
591
652
|
const since = (yield this.logs.get(record.sessionId)).count;
|
|
@@ -787,10 +848,28 @@ export class SessionEngine {
|
|
|
787
848
|
const prior = (_a = this.admissionChains.get(sessionId)) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
788
849
|
const admitted = prior
|
|
789
850
|
.catch(() => { })
|
|
790
|
-
.then(() => this
|
|
851
|
+
.then(() => __awaiter(this, void 0, void 0, function* () {
|
|
852
|
+
// Another server holds the turn: hand the message to it and coalesce.
|
|
853
|
+
if (yield this.turnLeases.runningElsewhere(sessionId)) {
|
|
854
|
+
yield this.outbox.post(sessionId, { message, options });
|
|
855
|
+
return true;
|
|
856
|
+
}
|
|
857
|
+
return this.admitTurn(sessionId, message, options);
|
|
858
|
+
}));
|
|
791
859
|
this.admissionChains.set(sessionId, admitted.catch(() => { }));
|
|
792
860
|
return admitted;
|
|
793
861
|
}
|
|
862
|
+
/** Admit what other servers handed to this turn's holder. Never awaited by the lifecycle. */
|
|
863
|
+
drainOutbox(sessionId) {
|
|
864
|
+
void this.outbox
|
|
865
|
+
.takeAll(sessionId)
|
|
866
|
+
.then((items) => {
|
|
867
|
+
for (const item of items) {
|
|
868
|
+
void this.startTurn(sessionId, item.message, item.options).catch(() => { });
|
|
869
|
+
}
|
|
870
|
+
})
|
|
871
|
+
.catch(() => { });
|
|
872
|
+
}
|
|
794
873
|
enqueueCoalesced(sessionId, item) {
|
|
795
874
|
var _a;
|
|
796
875
|
const queue = (_a = this.coalesceQueues.get(sessionId)) !== null && _a !== void 0 ? _a : [];
|
|
@@ -901,14 +980,46 @@ export class SessionEngine {
|
|
|
901
980
|
});
|
|
902
981
|
});
|
|
903
982
|
}
|
|
983
|
+
steerIntoRunningTurn(sessionId, message) {
|
|
984
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
985
|
+
if (this.runner.steer === undefined) {
|
|
986
|
+
return "revert_to_followup";
|
|
987
|
+
}
|
|
988
|
+
try {
|
|
989
|
+
return yield this.runner.steer({ sessionId, text: message });
|
|
990
|
+
}
|
|
991
|
+
catch (error) {
|
|
992
|
+
this.logger(`[session] steer failed for ${sessionId}: ${describeError(error)}`);
|
|
993
|
+
return "revert_to_followup";
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
}
|
|
904
997
|
admitTurn(sessionId_1, message_1) {
|
|
905
998
|
return __awaiter(this, arguments, void 0, function* (sessionId, message, options = {}) {
|
|
906
999
|
var _a;
|
|
907
1000
|
const admission = (_a = options.admission) !== null && _a !== void 0 ? _a : "preempt";
|
|
1001
|
+
if (admission === "coalesce" &&
|
|
1002
|
+
steeringEnabled() &&
|
|
1003
|
+
this.isTurnRunning(sessionId)) {
|
|
1004
|
+
const toolCtx = this.activeToolContexts.get(sessionId);
|
|
1005
|
+
const steerOutcome = yield this.steerIntoRunningTurn(sessionId, message);
|
|
1006
|
+
if (steerOutcome === "complete_delivered") {
|
|
1007
|
+
this.logger(`[session] steered into running turn ${sessionId}`);
|
|
1008
|
+
if (toolCtx !== undefined) {
|
|
1009
|
+
toolCtx.emit({ type: "message.received", data: { text: message } });
|
|
1010
|
+
toolCtx.emit({ type: "nudge.delivered", data: {} });
|
|
1011
|
+
}
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
// Re-checked after the steer: a turn that ended during it falls through
|
|
1016
|
+
// and this message starts the next turn itself. Enqueueing here instead
|
|
1017
|
+
// would orphan it — the completion drain already ran.
|
|
908
1018
|
if (admission === "coalesce" && this.isTurnRunning(sessionId)) {
|
|
909
|
-
this.enqueueCoalesced(sessionId,
|
|
910
|
-
|
|
911
|
-
|
|
1019
|
+
this.enqueueCoalesced(sessionId, {
|
|
1020
|
+
message,
|
|
1021
|
+
sourceTs: options.sourceTs,
|
|
1022
|
+
});
|
|
912
1023
|
this.logger(`[session] coalesce enqueue ${sessionId}`);
|
|
913
1024
|
return true;
|
|
914
1025
|
}
|
|
@@ -968,6 +1079,8 @@ export class SessionEngine {
|
|
|
968
1079
|
let lifecycle;
|
|
969
1080
|
lifecycle = (() => __awaiter(this, void 0, void 0, function* () {
|
|
970
1081
|
let outcome;
|
|
1082
|
+
const poll = setInterval(() => this.drainOutbox(sessionId), 1000);
|
|
1083
|
+
poll.unref();
|
|
971
1084
|
try {
|
|
972
1085
|
outcome = yield this.executeTurn(record, turnId, message, isFirstTurn, abort.signal);
|
|
973
1086
|
}
|
|
@@ -977,6 +1090,9 @@ export class SessionEngine {
|
|
|
977
1090
|
errorMessage: describeError(error),
|
|
978
1091
|
};
|
|
979
1092
|
}
|
|
1093
|
+
finally {
|
|
1094
|
+
clearInterval(poll);
|
|
1095
|
+
}
|
|
980
1096
|
this.lastOutcomes.set(sessionId, outcome);
|
|
981
1097
|
const wasCurrent = this.activeTurns.get(sessionId) === lifecycle;
|
|
982
1098
|
if (wasCurrent) {
|
|
@@ -1002,6 +1118,10 @@ export class SessionEngine {
|
|
|
1002
1118
|
// The drain may have nested a successor turn, which took its own
|
|
1003
1119
|
// reference; the lease outlives this release until that one ends too.
|
|
1004
1120
|
yield lease.release();
|
|
1121
|
+
// Handed off between the last poll and the release: nobody else will.
|
|
1122
|
+
if (!abort.signal.aborted) {
|
|
1123
|
+
this.drainOutbox(sessionId);
|
|
1124
|
+
}
|
|
1005
1125
|
return outcome;
|
|
1006
1126
|
}))();
|
|
1007
1127
|
this.activeTurns.set(sessionId, lifecycle);
|
|
@@ -1051,6 +1171,7 @@ export class SessionEngine {
|
|
|
1051
1171
|
this.coalesceDrains.has(sessionId) ||
|
|
1052
1172
|
((_b = (_a = this.coalesceQueues.get(sessionId)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
|
|
1053
1173
|
this.clearCoalesced(sessionId);
|
|
1174
|
+
yield this.outbox.clear(sessionId);
|
|
1054
1175
|
const record = yield this.sessions.get(sessionId);
|
|
1055
1176
|
const continuationKey = record === null || record === void 0 ? void 0 : record.continuationKey;
|
|
1056
1177
|
if (continuationKey !== undefined && continuationKey !== null) {
|
|
@@ -1239,6 +1360,7 @@ export class SessionEngine {
|
|
|
1239
1360
|
seed: isFirstTurn,
|
|
1240
1361
|
stateRoot: this.stateRoot,
|
|
1241
1362
|
});
|
|
1363
|
+
this.materializedWorkspaces.add(sessionId);
|
|
1242
1364
|
// Must precede the carrier: the harness resolves rules, skills,
|
|
1243
1365
|
// `mcp.json` and hooks from its git root, so a workspace nested in an
|
|
1244
1366
|
// outer repo would load that repo's hooks instead of the ones written
|
|
@@ -1301,12 +1423,15 @@ export class SessionEngine {
|
|
|
1301
1423
|
}
|
|
1302
1424
|
catch (error) {
|
|
1303
1425
|
this.approvals.cancelSession(sessionId, "deny");
|
|
1426
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1304
1427
|
outcome = { status: "error", errorMessage: describeError(error) };
|
|
1305
1428
|
}
|
|
1306
1429
|
finally {
|
|
1307
1430
|
permit.release();
|
|
1308
1431
|
this.approvals.cancelSession(sessionId, "deny");
|
|
1432
|
+
this.auths.cancelSession(sessionId, "deny");
|
|
1309
1433
|
yield this.clearPendingApprovals(sessionId);
|
|
1434
|
+
yield this.clearPendingAuths(sessionId);
|
|
1310
1435
|
// Anything still queued belongs to the next turn's boundary drain, not
|
|
1311
1436
|
// to this turn's edge detection.
|
|
1312
1437
|
this.deliveredNudges.delete(sessionId);
|
|
@@ -1797,11 +1922,17 @@ export class SessionEngine {
|
|
|
1797
1922
|
// named passthroughs resolved fresh on every turn — reminder fires and
|
|
1798
1923
|
// post-restart follow-ups included — and a listing failure throws,
|
|
1799
1924
|
// failing the turn closed.
|
|
1800
|
-
for (const tool of yield
|
|
1925
|
+
for (const tool of yield mcpAuthAls.run({
|
|
1926
|
+
sessionId: record.sessionId,
|
|
1927
|
+
turnId,
|
|
1928
|
+
callId: newToolCallId("mcp"),
|
|
1929
|
+
toolName: "mcp",
|
|
1930
|
+
emit,
|
|
1931
|
+
}, () => resolveAdvertisedTools({
|
|
1801
1932
|
agent,
|
|
1802
1933
|
session: this.sessionInfo(record),
|
|
1803
|
-
hostMcp: this.
|
|
1804
|
-
})) {
|
|
1934
|
+
hostMcp: this.advertiseMcp(),
|
|
1935
|
+
}))) {
|
|
1805
1936
|
register(tool);
|
|
1806
1937
|
}
|
|
1807
1938
|
return tools;
|
|
@@ -1909,7 +2040,13 @@ export class SessionEngine {
|
|
|
1909
2040
|
}
|
|
1910
2041
|
const started = Date.now();
|
|
1911
2042
|
this.logger(`[session] tool ${args.tool.name} start`);
|
|
1912
|
-
const result = yield
|
|
2043
|
+
const result = yield mcpAuthAls.run({
|
|
2044
|
+
sessionId: args.record.sessionId,
|
|
2045
|
+
turnId: args.turnId,
|
|
2046
|
+
callId: toolCallId,
|
|
2047
|
+
toolName: args.tool.name,
|
|
2048
|
+
emit: args.emit,
|
|
2049
|
+
}, () => execute(validation.value, toolCtx));
|
|
1913
2050
|
this.logger(`[session] tool ${args.tool.name} done (${Date.now() - started}ms)`);
|
|
1914
2051
|
return normalizeToolResult(result);
|
|
1915
2052
|
}
|
|
@@ -1934,7 +2071,7 @@ export class SessionEngine {
|
|
|
1934
2071
|
args: args.args,
|
|
1935
2072
|
requestedAt,
|
|
1936
2073
|
};
|
|
1937
|
-
const resolutionPromise = this.approvals.wait(
|
|
2074
|
+
const resolutionPromise = this.approvals.wait(args.sessionId, pendingRecord);
|
|
1938
2075
|
yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingApprovals: [
|
|
1939
2076
|
...((_a = current.pendingApprovals) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
|
|
1940
2077
|
pendingRecord,
|
|
@@ -2020,6 +2157,245 @@ export class SessionEngine {
|
|
|
2020
2157
|
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) })); });
|
|
2021
2158
|
});
|
|
2022
2159
|
}
|
|
2160
|
+
wrapMcp(inner) {
|
|
2161
|
+
return {
|
|
2162
|
+
names: () => inner.names(),
|
|
2163
|
+
listTools: (name) => this.withMcpAuth(name, () => inner.listTools(name)),
|
|
2164
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => inner.callTool(name, tool, args)),
|
|
2165
|
+
};
|
|
2166
|
+
}
|
|
2167
|
+
advertiseMcp() {
|
|
2168
|
+
return {
|
|
2169
|
+
listTools: (name) => this.withMcpListAuth(name, () => this.mcpHost.listTools(name)),
|
|
2170
|
+
callTool: (name, tool, args) => this.withMcpAuth(name, () => this.mcpHost.callTool(name, tool, args)),
|
|
2171
|
+
oneOff: (connection) => {
|
|
2172
|
+
const inner = this.mcpHost.oneOff(connection);
|
|
2173
|
+
return {
|
|
2174
|
+
listTools: () => this.withMcpListAuth(connection.name, () => inner.listTools()),
|
|
2175
|
+
callTool: (tool, args) => this.withMcpAuth(connection.name, () => inner.callTool(tool, args)),
|
|
2176
|
+
};
|
|
2177
|
+
},
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
withMcpListAuth(connection, run) {
|
|
2181
|
+
const ctx = mcpAuthAls.getStore();
|
|
2182
|
+
if (ctx === undefined) {
|
|
2183
|
+
return this.withMcpAuth(connection, run);
|
|
2184
|
+
}
|
|
2185
|
+
const toolName = `mcp:${connection}`;
|
|
2186
|
+
return mcpAuthAls.run(Object.assign(Object.assign({}, ctx), { callId: newToolCallId(toolName), toolName }), () => this.withMcpAuth(connection, run));
|
|
2187
|
+
}
|
|
2188
|
+
withMcpAuth(connection, run) {
|
|
2189
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2190
|
+
try {
|
|
2191
|
+
return yield run();
|
|
2192
|
+
}
|
|
2193
|
+
catch (error) {
|
|
2194
|
+
if (!isMcpNeedsAuthError(error)) {
|
|
2195
|
+
throw error;
|
|
2196
|
+
}
|
|
2197
|
+
return yield this.parkAndRetryMcp(connection, run, error);
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
parkAndRetryMcp(connection, run, error) {
|
|
2202
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2203
|
+
const ctx = mcpAuthAls.getStore();
|
|
2204
|
+
if (ctx === undefined) {
|
|
2205
|
+
throw error;
|
|
2206
|
+
}
|
|
2207
|
+
const minted = yield this.mintAuthUrl(connection, ctx.sessionId);
|
|
2208
|
+
if (minted === undefined) {
|
|
2209
|
+
throw error;
|
|
2210
|
+
}
|
|
2211
|
+
if ("authorized" in minted) {
|
|
2212
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2213
|
+
yield this.mcpHost.dropClient(connection);
|
|
2214
|
+
return yield this.withMcpAuth(connection, run);
|
|
2215
|
+
}
|
|
2216
|
+
if (minted.state !== "") {
|
|
2217
|
+
const bind = this.oauthHub.bind(minted.state, ctx.sessionId, ctx.callId);
|
|
2218
|
+
if (bind === "already_done") {
|
|
2219
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2220
|
+
yield this.mcpHost.dropClient(connection);
|
|
2221
|
+
return yield this.withMcpAuth(connection, run);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
const decision = yield this.parkForAuth({
|
|
2225
|
+
sessionId: ctx.sessionId,
|
|
2226
|
+
turnId: ctx.turnId,
|
|
2227
|
+
callId: ctx.callId,
|
|
2228
|
+
toolName: ctx.toolName,
|
|
2229
|
+
connection,
|
|
2230
|
+
authUrl: minted.authUrl,
|
|
2231
|
+
emit: ctx.emit,
|
|
2232
|
+
});
|
|
2233
|
+
if (decision === "deny") {
|
|
2234
|
+
throw new Error(`MCP connection "${connection}" authorization was denied.`);
|
|
2235
|
+
}
|
|
2236
|
+
yield this.persistHostedMcpOauth(connection);
|
|
2237
|
+
yield this.mcpHost.dropClient(connection);
|
|
2238
|
+
return yield this.withMcpAuth(connection, run);
|
|
2239
|
+
});
|
|
2240
|
+
}
|
|
2241
|
+
persistHostedMcpOauth(connection) {
|
|
2242
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2243
|
+
const authored = this.authoredConnection(connection);
|
|
2244
|
+
if (authored === undefined ||
|
|
2245
|
+
!isHostOauthConnectionTransport(authored.transport)) {
|
|
2246
|
+
return;
|
|
2247
|
+
}
|
|
2248
|
+
yield this.oauthHub.persistFor(connection, authored.transport.url);
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
authoredConnection(name) {
|
|
2252
|
+
var _a;
|
|
2253
|
+
var _b;
|
|
2254
|
+
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));
|
|
2255
|
+
}
|
|
2256
|
+
mintAuthUrl(connection, sessionId) {
|
|
2257
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2258
|
+
const authored = this.authoredConnection(connection);
|
|
2259
|
+
if (authored === undefined) {
|
|
2260
|
+
return undefined;
|
|
2261
|
+
}
|
|
2262
|
+
if (isCursorAccountTransport(authored.transport)) {
|
|
2263
|
+
const probe = this.probeCursorAccount;
|
|
2264
|
+
if (probe === undefined) {
|
|
2265
|
+
return undefined;
|
|
2266
|
+
}
|
|
2267
|
+
const authUrl = yield probe(connection);
|
|
2268
|
+
if (authUrl === undefined || authUrl === "") {
|
|
2269
|
+
return undefined;
|
|
2270
|
+
}
|
|
2271
|
+
return { authUrl, state: "" };
|
|
2272
|
+
}
|
|
2273
|
+
if (!isHostOauthConnectionTransport(authored.transport)) {
|
|
2274
|
+
return undefined;
|
|
2275
|
+
}
|
|
2276
|
+
return yield this.oauthHub.mint({
|
|
2277
|
+
connection,
|
|
2278
|
+
url: authored.transport.url,
|
|
2279
|
+
type: authored.transport.type === "sse" ? "sse" : "http",
|
|
2280
|
+
headers: authored.transport.headers,
|
|
2281
|
+
sessionId,
|
|
2282
|
+
});
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
parkForAuth(args) {
|
|
2286
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2287
|
+
const requestedAt = new Date().toISOString();
|
|
2288
|
+
const pendingRecord = {
|
|
2289
|
+
callId: args.callId,
|
|
2290
|
+
turnId: args.turnId,
|
|
2291
|
+
toolName: args.toolName,
|
|
2292
|
+
connection: args.connection,
|
|
2293
|
+
authUrl: args.authUrl,
|
|
2294
|
+
requestedAt,
|
|
2295
|
+
};
|
|
2296
|
+
const resolutionPromise = this.auths.wait(args.sessionId, pendingRecord);
|
|
2297
|
+
yield this.updateRecord(args.sessionId, (current) => { var _a; return (Object.assign(Object.assign({}, current), { pendingAuths: [
|
|
2298
|
+
...((_a = current.pendingAuths) !== null && _a !== void 0 ? _a : []).filter((item) => item.callId !== args.callId),
|
|
2299
|
+
pendingRecord,
|
|
2300
|
+
] })); });
|
|
2301
|
+
args.emit({
|
|
2302
|
+
type: "action.auth_requested",
|
|
2303
|
+
data: {
|
|
2304
|
+
callId: args.callId,
|
|
2305
|
+
toolName: args.toolName,
|
|
2306
|
+
connection: args.connection,
|
|
2307
|
+
authUrl: args.authUrl,
|
|
2308
|
+
},
|
|
2309
|
+
});
|
|
2310
|
+
this.logger(`[session] tool ${args.toolName} awaiting MCP auth ${args.connection} (${args.callId})`);
|
|
2311
|
+
const { decision, by } = yield resolutionPromise;
|
|
2312
|
+
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) })); });
|
|
2313
|
+
args.emit({
|
|
2314
|
+
type: "action.auth_resolved",
|
|
2315
|
+
data: {
|
|
2316
|
+
callId: args.callId,
|
|
2317
|
+
toolName: args.toolName,
|
|
2318
|
+
connection: args.connection,
|
|
2319
|
+
decision,
|
|
2320
|
+
by,
|
|
2321
|
+
},
|
|
2322
|
+
});
|
|
2323
|
+
this.logger(`[session] tool ${args.toolName} MCP auth ${decision} (${args.callId})`);
|
|
2324
|
+
return decision;
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
clearPendingAuths(sessionId) {
|
|
2328
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2329
|
+
yield this.oauthHub.abortSession(sessionId);
|
|
2330
|
+
const current = yield this.sessions.get(sessionId);
|
|
2331
|
+
if (current === undefined ||
|
|
2332
|
+
current.pendingAuths === undefined ||
|
|
2333
|
+
current.pendingAuths.length === 0) {
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
yield this.updateRecord(sessionId, (record) => (Object.assign(Object.assign({}, record), { pendingAuths: [] })));
|
|
2337
|
+
});
|
|
2338
|
+
}
|
|
2339
|
+
listPendingAuths(sessionId) {
|
|
2340
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2341
|
+
var _a;
|
|
2342
|
+
const record = yield this.sessions.get(sessionId);
|
|
2343
|
+
if (record === undefined) {
|
|
2344
|
+
return [];
|
|
2345
|
+
}
|
|
2346
|
+
const live = this.auths.list(sessionId);
|
|
2347
|
+
if (live.length > 0) {
|
|
2348
|
+
return live;
|
|
2349
|
+
}
|
|
2350
|
+
return (_a = record.pendingAuths) !== null && _a !== void 0 ? _a : [];
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
resolveAuth(sessionId, callId, decision, auth, options) {
|
|
2354
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2355
|
+
var _a, _b;
|
|
2356
|
+
const record = yield this.sessions.get(sessionId);
|
|
2357
|
+
if (record === undefined) {
|
|
2358
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2359
|
+
}
|
|
2360
|
+
const ownershipAuth = (options === null || options === void 0 ? void 0 : options.asSessionOwner) === true ? record.auth : auth;
|
|
2361
|
+
if (!samePrincipal(record.auth, ownershipAuth)) {
|
|
2362
|
+
throw new SessionAuthError(sessionId);
|
|
2363
|
+
}
|
|
2364
|
+
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);
|
|
2365
|
+
if (pending === undefined) {
|
|
2366
|
+
throw new AuthNotFoundError(sessionId, callId);
|
|
2367
|
+
}
|
|
2368
|
+
const resolvedBy = approvalResolverLabel(auth);
|
|
2369
|
+
if (decision === "deny") {
|
|
2370
|
+
yield this.oauthHub.abortCall(sessionId, callId);
|
|
2371
|
+
}
|
|
2372
|
+
if (!this.auths.resolve(sessionId, callId, decision, resolvedBy)) {
|
|
2373
|
+
yield this.removePendingAuth(sessionId, callId);
|
|
2374
|
+
throw new AuthInterruptedError(sessionId, callId);
|
|
2375
|
+
}
|
|
2376
|
+
return pending;
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2379
|
+
removePendingAuth(sessionId, callId) {
|
|
2380
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2381
|
+
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) })); });
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2384
|
+
runWithMcpPark(sessionId, toolName, fn) {
|
|
2385
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2386
|
+
const active = this.activeToolContexts.get(sessionId);
|
|
2387
|
+
if (active === undefined) {
|
|
2388
|
+
return fn();
|
|
2389
|
+
}
|
|
2390
|
+
return mcpAuthAls.run({
|
|
2391
|
+
sessionId,
|
|
2392
|
+
turnId: active.turnId,
|
|
2393
|
+
callId: newToolCallId(toolName),
|
|
2394
|
+
toolName,
|
|
2395
|
+
emit: active.emit,
|
|
2396
|
+
}, fn);
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2023
2399
|
// ==========================================================================
|
|
2024
2400
|
// Direct tool calls (deterministic invocation, no model turn)
|
|
2025
2401
|
// ==========================================================================
|
|
@@ -2034,8 +2410,10 @@ export class SessionEngine {
|
|
|
2034
2410
|
* and materialized workspace) and the call is recorded on the session's
|
|
2035
2411
|
* event stream as `actions.requested` / `action.result` under a
|
|
2036
2412
|
* per-call `turnId` (the call id), so it shows up in trajectories.
|
|
2037
|
-
*
|
|
2038
|
-
*
|
|
2413
|
+
* While a model turn is running, a read-effect call runs alongside it
|
|
2414
|
+
* (the turn's workspace is read as-is) and a write-effect call is
|
|
2415
|
+
* rejected with {@link SessionBusyError}. Without a session, the tool
|
|
2416
|
+
* runs against an **ephemeral** scratch
|
|
2039
2417
|
* workspace under `<stateRoot>/tool-calls/<callId>` (materialized with
|
|
2040
2418
|
* seed files, removed after the call returns).
|
|
2041
2419
|
*
|
|
@@ -2075,26 +2453,22 @@ export class SessionEngine {
|
|
|
2075
2453
|
throw new InvalidContinuationTokenError(`expected "<channelId>:<key>" (as /v1/sessions lists it), got "${options.continuationToken}"`);
|
|
2076
2454
|
}
|
|
2077
2455
|
// A token that maps to a live (or restorable) session takes the
|
|
2078
|
-
// session path: same owner check, same
|
|
2079
|
-
//
|
|
2080
|
-
//
|
|
2456
|
+
// session path: same owner check, same busy semantics, same event
|
|
2457
|
+
// recording as an explicit sessionId. Skipping those just because
|
|
2458
|
+
// the caller addressed the session by token would fail open.
|
|
2081
2459
|
record = (_b = (yield this.sessions.findByContinuation(continuation.channelId, continuation.key))) !== null && _b !== void 0 ? _b : (yield this.restoreSessionByContinuation(continuation.channelId, continuation.key));
|
|
2082
2460
|
if (record !== undefined && !samePrincipal(record.auth, auth)) {
|
|
2083
2461
|
throw new SessionAuthError(record.sessionId);
|
|
2084
2462
|
}
|
|
2085
2463
|
}
|
|
2086
2464
|
if (record !== undefined) {
|
|
2087
|
-
// Direct calls respect the same serialization as follow-up sends: a
|
|
2088
|
-
// running model turn owns the session workspace and event stream.
|
|
2089
|
-
if (this.activeTurns.has(record.sessionId) ||
|
|
2090
|
-
this.pendingTurns.has(record.sessionId)) {
|
|
2091
|
-
throw new SessionBusyError(record.sessionId);
|
|
2092
|
-
}
|
|
2093
2465
|
return {
|
|
2094
2466
|
record,
|
|
2095
2467
|
session: this.sessionInfo(record),
|
|
2096
2468
|
workspaceDir: this.harnessCwd(record),
|
|
2097
2469
|
scratchDir: undefined,
|
|
2470
|
+
busy: this.activeTurns.has(record.sessionId) ||
|
|
2471
|
+
this.pendingTurns.has(record.sessionId),
|
|
2098
2472
|
};
|
|
2099
2473
|
}
|
|
2100
2474
|
// No session behind the token (or no token at all): a synthetic
|
|
@@ -2114,6 +2488,7 @@ export class SessionEngine {
|
|
|
2114
2488
|
},
|
|
2115
2489
|
workspaceDir: scratchDir,
|
|
2116
2490
|
scratchDir,
|
|
2491
|
+
busy: false,
|
|
2117
2492
|
};
|
|
2118
2493
|
});
|
|
2119
2494
|
}
|
|
@@ -2146,8 +2521,32 @@ export class SessionEngine {
|
|
|
2146
2521
|
};
|
|
2147
2522
|
});
|
|
2148
2523
|
}
|
|
2524
|
+
/**
|
|
2525
|
+
* One catalog tool by name. Authored tools are local — their schema does
|
|
2526
|
+
* not depend on session identity or connection listings — so a detail
|
|
2527
|
+
* fetch for `ls` must not wait on every advertised MCP server.
|
|
2528
|
+
*/
|
|
2529
|
+
describeCatalogTool(toolName_1) {
|
|
2530
|
+
return __awaiter(this, arguments, void 0, function* (toolName, options = {}) {
|
|
2531
|
+
const authored = this.project.agent.tools.find((tool) => tool.name === toolName);
|
|
2532
|
+
if (authored !== undefined) {
|
|
2533
|
+
return { tool: authored };
|
|
2534
|
+
}
|
|
2535
|
+
const catalog = yield this.listCatalogTools(options);
|
|
2536
|
+
const tool = catalog.tools.find((entry) => entry.name === toolName);
|
|
2537
|
+
if (tool === undefined) {
|
|
2538
|
+
return {
|
|
2539
|
+
tool: undefined,
|
|
2540
|
+
availableTools: catalog.tools.map((entry) => entry.name),
|
|
2541
|
+
connectionErrors: catalog.connectionErrors,
|
|
2542
|
+
};
|
|
2543
|
+
}
|
|
2544
|
+
return { tool };
|
|
2545
|
+
});
|
|
2546
|
+
}
|
|
2149
2547
|
callTool(toolName_1) {
|
|
2150
2548
|
return __awaiter(this, arguments, void 0, function* (toolName, input = {}, options = {}) {
|
|
2549
|
+
var _a;
|
|
2151
2550
|
const callId = newToolCallId(toolName);
|
|
2152
2551
|
// Identity resolves before the tool lookup: advertised connection tools
|
|
2153
2552
|
// only exist relative to a session (per-session auth), so finding them
|
|
@@ -2167,7 +2566,7 @@ export class SessionEngine {
|
|
|
2167
2566
|
const advertised = yield resolveAdvertisedTools({
|
|
2168
2567
|
agent: this.project.agent,
|
|
2169
2568
|
session,
|
|
2170
|
-
hostMcp: this.
|
|
2569
|
+
hostMcp: this.advertiseMcp(),
|
|
2171
2570
|
});
|
|
2172
2571
|
tool = advertised.find((t) => t.name === toolName);
|
|
2173
2572
|
}
|
|
@@ -2184,6 +2583,17 @@ export class SessionEngine {
|
|
|
2184
2583
|
if (!validation.ok) {
|
|
2185
2584
|
throw new ToolInputError({ toolName, detail: validation.message });
|
|
2186
2585
|
}
|
|
2586
|
+
// A running model turn owns the session workspace: a direct write would
|
|
2587
|
+
// race the harness's own edits, so it waits (409). A read runs alongside
|
|
2588
|
+
// the turn — the operator inspecting a live session is the point — and
|
|
2589
|
+
// its events stay separable by their per-call turnId. The effect can
|
|
2590
|
+
// depend on the input, hence deciding after validation; an MCP tool
|
|
2591
|
+
// nothing classifies is a write here, fail-closed.
|
|
2592
|
+
if (identity.busy &&
|
|
2593
|
+
record !== undefined &&
|
|
2594
|
+
directCallEffect(tool, validation.value) !== "read") {
|
|
2595
|
+
throw new SessionBusyError(record.sessionId);
|
|
2596
|
+
}
|
|
2187
2597
|
let workspaceUnavailable = false;
|
|
2188
2598
|
try {
|
|
2189
2599
|
yield mkdir(workspaceDir, { recursive: true });
|
|
@@ -2203,28 +2613,50 @@ export class SessionEngine {
|
|
|
2203
2613
|
// to write) authored tool may rely on ctx.workspaceDir being the
|
|
2204
2614
|
// real session workspace, so silently relocating it is not safe —
|
|
2205
2615
|
// fail typed instead.
|
|
2616
|
+
//
|
|
2617
|
+
// A shared `local.workspaceDir` (change-monitor's phantom /repo) is
|
|
2618
|
+
// that path by design. Relocating it to scratch would flag a false
|
|
2619
|
+
// session-state warning; keep the borrowed cwd and skip the flag.
|
|
2206
2620
|
const relocatable = tool.advertised === true ||
|
|
2207
|
-
|
|
2621
|
+
directCallEffect(tool, validation.value) === "read";
|
|
2208
2622
|
if (!relocatable) {
|
|
2209
2623
|
throw new WorkspaceUnavailableError(toolName, error);
|
|
2210
2624
|
}
|
|
2211
|
-
this.
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2625
|
+
if (((_a = this.project.agent.local) === null || _a === void 0 ? void 0 : _a.workspaceDir) !== undefined) {
|
|
2626
|
+
this.logger(`[session] tool ${toolName} harness cwd unavailable (direct ${callId}); keeping borrowed workspace`);
|
|
2627
|
+
}
|
|
2628
|
+
else {
|
|
2629
|
+
this.logger(`[session] tool ${toolName} workspace unavailable (direct ${callId}); using scratch: ${error instanceof Error ? error.message : String(error)}`);
|
|
2630
|
+
scratchDir = join(this.stateRoot, "tool-calls", callId);
|
|
2631
|
+
workspaceDir = scratchDir;
|
|
2632
|
+
workspaceUnavailable = true;
|
|
2633
|
+
yield mkdir(workspaceDir, { recursive: true });
|
|
2634
|
+
}
|
|
2216
2635
|
}
|
|
2217
2636
|
// Match what a model turn would see: session workspaces get the same
|
|
2218
2637
|
// scaffold as executeTurn (seeds only before the first turn); scratch
|
|
2219
|
-
// workspaces are materialized fresh.
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2638
|
+
// workspaces are materialized fresh. A read running alongside a turn
|
|
2639
|
+
// that has materialized its workspace leaves it alone — re-seeding
|
|
2640
|
+
// would restore files the agent deleted mid-turn. A session is busy
|
|
2641
|
+
// from admit, though, so a read that lands while the first turn is
|
|
2642
|
+
// still queued or scaffolding materializes the workspace itself, seeds
|
|
2643
|
+
// included (turnCount is already 1 for that pending first turn).
|
|
2644
|
+
const turnMaterialized = record !== undefined &&
|
|
2645
|
+
identity.busy &&
|
|
2646
|
+
this.materializedWorkspaces.has(record.sessionId);
|
|
2647
|
+
if (record === undefined || workspaceUnavailable || !turnMaterialized) {
|
|
2648
|
+
yield materializeWorkspace({
|
|
2649
|
+
agent: this.project.agent,
|
|
2650
|
+
workspaceDir: record === undefined || workspaceUnavailable
|
|
2651
|
+
? workspaceDir
|
|
2652
|
+
: this.scaffoldDir(record),
|
|
2653
|
+
seed: record === undefined ||
|
|
2654
|
+
workspaceUnavailable ||
|
|
2655
|
+
record.turnCount === 0 ||
|
|
2656
|
+
(identity.busy && record.turnCount === 1),
|
|
2657
|
+
stateRoot: this.stateRoot,
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2228
2660
|
// A per-call turnId keeps these events visible in trajectories (which
|
|
2229
2661
|
// drop turn-less action events) without attributing them to a model turn.
|
|
2230
2662
|
if (record !== undefined) {
|
|
@@ -2267,7 +2699,7 @@ export class SessionEngine {
|
|
|
2267
2699
|
isError = false;
|
|
2268
2700
|
}
|
|
2269
2701
|
else {
|
|
2270
|
-
|
|
2702
|
+
const run = () => execute(validation.value, {
|
|
2271
2703
|
toolCallId: callId,
|
|
2272
2704
|
session,
|
|
2273
2705
|
workspaceDir,
|
|
@@ -2285,7 +2717,16 @@ export class SessionEngine {
|
|
|
2285
2717
|
: session.auth })),
|
|
2286
2718
|
getSession: (channelId, sessionId) => this.getChannelSession(channelId, sessionId),
|
|
2287
2719
|
emit,
|
|
2288
|
-
})
|
|
2720
|
+
});
|
|
2721
|
+
result = normalizeToolResult(record !== undefined && emit !== undefined
|
|
2722
|
+
? yield mcpAuthAls.run({
|
|
2723
|
+
sessionId: record.sessionId,
|
|
2724
|
+
turnId: callId,
|
|
2725
|
+
callId,
|
|
2726
|
+
toolName,
|
|
2727
|
+
emit,
|
|
2728
|
+
}, run)
|
|
2729
|
+
: yield run());
|
|
2289
2730
|
isError = isErrorEnvelope(result);
|
|
2290
2731
|
}
|
|
2291
2732
|
}
|
|
@@ -2513,6 +2954,29 @@ export class SessionEngine {
|
|
|
2513
2954
|
// ==========================================================================
|
|
2514
2955
|
// Session handles
|
|
2515
2956
|
// ==========================================================================
|
|
2957
|
+
/**
|
|
2958
|
+
* Wait until everything this session has already emitted is durably
|
|
2959
|
+
* mirrored: its dispatch chain first (the chain's tail runs the turn-end
|
|
2960
|
+
* storage flush, so the record carries the boundary event's handler
|
|
2961
|
+
* mutations), then the storage delivery queue. Hosted v2 awaits this
|
|
2962
|
+
* before the execution reply — the pod can be parked the moment the
|
|
2963
|
+
* reply lands, and an unflushed session record would strand a
|
|
2964
|
+
* conversation whose blobs persisted but whose record (which selects the
|
|
2965
|
+
* conversation to hydrate) never reached the sink. No-op without a
|
|
2966
|
+
* storage mirror.
|
|
2967
|
+
*/
|
|
2968
|
+
drainSessionDurability(sessionId) {
|
|
2969
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2970
|
+
if (this.storage === undefined) {
|
|
2971
|
+
return;
|
|
2972
|
+
}
|
|
2973
|
+
const chain = this.dispatchChains.get(sessionId);
|
|
2974
|
+
if (chain !== undefined) {
|
|
2975
|
+
yield chain.catch(() => { });
|
|
2976
|
+
}
|
|
2977
|
+
yield this.storage.whenIdle();
|
|
2978
|
+
});
|
|
2979
|
+
}
|
|
2516
2980
|
waitForCompletion(sessionId) {
|
|
2517
2981
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2518
2982
|
var _a;
|
|
@@ -2629,6 +3093,64 @@ export class SessionEngine {
|
|
|
2629
3093
|
};
|
|
2630
3094
|
});
|
|
2631
3095
|
}
|
|
3096
|
+
/**
|
|
3097
|
+
* The single newest session visible to `caller` (`GET /v1/sessions?latest=1`).
|
|
3098
|
+
*
|
|
3099
|
+
* Short-circuits the full listing: the local store already holds every
|
|
3100
|
+
* session this host created plus the startup bulk restore, so the common
|
|
3101
|
+
* case answers from memory without a sink round trip. Only when the local
|
|
3102
|
+
* store has nothing for the caller (fresh host, `restore: "off"`) does it
|
|
3103
|
+
* ask the sink — for a bounded newest-written window when the backend
|
|
3104
|
+
* can order by write time, else the full list. A session written by
|
|
3105
|
+
* another replica moments ago can lose to a local one; callers that need
|
|
3106
|
+
* the exact newest row should use the full listing.
|
|
3107
|
+
*/
|
|
3108
|
+
latestSessionWithSource(caller, options) {
|
|
3109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3110
|
+
var _a;
|
|
3111
|
+
var _b;
|
|
3112
|
+
const includeAll = (_b = options === null || options === void 0 ? void 0 : options.includeAll) !== null && _b !== void 0 ? _b : false;
|
|
3113
|
+
const newestVisible = (records) => {
|
|
3114
|
+
let newest;
|
|
3115
|
+
for (const record of records) {
|
|
3116
|
+
if (!(includeAll || samePrincipal(record.auth, caller))) {
|
|
3117
|
+
continue;
|
|
3118
|
+
}
|
|
3119
|
+
if (newest === undefined ||
|
|
3120
|
+
record.updatedAt.localeCompare(newest.updatedAt) > 0) {
|
|
3121
|
+
newest = record;
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
return newest;
|
|
3125
|
+
};
|
|
3126
|
+
const local = newestVisible(yield this.sessions.list());
|
|
3127
|
+
if (local !== undefined) {
|
|
3128
|
+
return {
|
|
3129
|
+
sessions: this.summarizeSessionRecords([local], caller, includeAll),
|
|
3130
|
+
source: "local",
|
|
3131
|
+
};
|
|
3132
|
+
}
|
|
3133
|
+
if (((_a = this.storage) === null || _a === void 0 ? void 0 : _a.isCursorHosted) === true) {
|
|
3134
|
+
try {
|
|
3135
|
+
const window = yield this.storage.listNewestSessionsFromSink(LATEST_SESSION_SINK_WINDOW);
|
|
3136
|
+
if (window !== undefined) {
|
|
3137
|
+
const stored = newestVisible(window);
|
|
3138
|
+
if (stored !== undefined) {
|
|
3139
|
+
return {
|
|
3140
|
+
sessions: this.summarizeSessionRecords([stored], caller, includeAll),
|
|
3141
|
+
source: "cursor-hosted",
|
|
3142
|
+
};
|
|
3143
|
+
}
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
catch (error) {
|
|
3147
|
+
this.logger(`[agent-sdk] cursor-hosted newest-session lookup failed; falling back to the full list: ${describeError(error)}`);
|
|
3148
|
+
}
|
|
3149
|
+
}
|
|
3150
|
+
const listed = yield this.listSessionsWithSource(caller, options);
|
|
3151
|
+
return { sessions: listed.sessions.slice(0, 1), source: listed.source };
|
|
3152
|
+
});
|
|
3153
|
+
}
|
|
2632
3154
|
/**
|
|
2633
3155
|
* Durable sink rows plus local records (local wins on the same id when
|
|
2634
3156
|
* newer — covers debounce windows that have not flushed yet). Newest
|
|
@@ -2675,6 +3197,7 @@ export class SessionEngine {
|
|
|
2675
3197
|
: undefined,
|
|
2676
3198
|
createdAt: record.createdAt,
|
|
2677
3199
|
updatedAt: record.updatedAt,
|
|
3200
|
+
dryRun: record.dryRun === true ? true : undefined,
|
|
2678
3201
|
continuationToken: record.httpContinuationKey !== undefined
|
|
2679
3202
|
? formatContinuationToken(HTTP_CHANNEL_ID, record.httpContinuationKey)
|
|
2680
3203
|
: record.continuationKey === null
|
|
@@ -2947,7 +3470,7 @@ export class SessionEngine {
|
|
|
2947
3470
|
this.coalesceDrains.has(sessionId));
|
|
2948
3471
|
}
|
|
2949
3472
|
sessionInfo(record) {
|
|
2950
|
-
return Object.assign(Object.assign(Object.assign(
|
|
3473
|
+
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, auth: record.auth, continuationKey: record.continuationKey }, (record.abs === undefined ? {} : { abs: record.abs })), (record.title === undefined ? {} : { title: record.title })), (record.sdkAgentId === undefined
|
|
2951
3474
|
? {}
|
|
2952
3475
|
: { sdkAgentId: record.sdkAgentId }));
|
|
2953
3476
|
}
|
|
@@ -3232,6 +3755,8 @@ export class SessionEngine {
|
|
|
3232
3755
|
var _a, _b, _c, _d, _e;
|
|
3233
3756
|
(_a = this.stopMemoryTelemetry) === null || _a === void 0 ? void 0 : _a.call(this);
|
|
3234
3757
|
this.approvals.cancelAll("deny");
|
|
3758
|
+
this.auths.cancelAll("deny");
|
|
3759
|
+
yield this.oauthHub.close();
|
|
3235
3760
|
yield Promise.allSettled([...this.activeTurns.values()]);
|
|
3236
3761
|
yield Promise.allSettled([...this.backgroundWork]);
|
|
3237
3762
|
yield Promise.allSettled([...this.dispatchChains.values()]);
|
|
@@ -3248,6 +3773,12 @@ export class SessionEngine {
|
|
|
3248
3773
|
}
|
|
3249
3774
|
/** Parallel event-stream loads during the startup bulk restore. */
|
|
3250
3775
|
const RESTORE_FETCH_CONCURRENCY = 8;
|
|
3776
|
+
/**
|
|
3777
|
+
* Newest-written sink rows fetched for `GET /v1/sessions?latest=1` when the
|
|
3778
|
+
* local store has nothing for the caller. Wide enough that the caller's own
|
|
3779
|
+
* session is normally inside it even when other principals are busier.
|
|
3780
|
+
*/
|
|
3781
|
+
const LATEST_SESSION_SINK_WINDOW = 50;
|
|
3251
3782
|
/** NDJSON payload for a restored event stream; undefined when empty. */
|
|
3252
3783
|
function mergePendingImages(current, incoming) {
|
|
3253
3784
|
if (incoming === undefined) {
|