@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
package/dist/docs/llms-full.txt
CHANGED
|
@@ -693,9 +693,10 @@ use `UPPER_SNAKE_CASE`. Commit names only; set their values after
|
|
|
693
693
|
creating the deployment. Names beginning with `CURSOR_` are reserved.
|
|
694
694
|
|
|
695
695
|
For `defineConnection({ url, oauth: true })`, declare
|
|
696
|
-
`MCP_OAUTH_<CONNECTION>_*` in `secretNames
|
|
697
|
-
`agent-sdk mcp oauth <connection> --store
|
|
698
|
-
|
|
696
|
+
`MCP_OAUTH_<CONNECTION>_*` in `secretNames`. Finish Connect for the
|
|
697
|
+
current process. Run `agent-sdk mcp oauth <connection> --store` and
|
|
698
|
+
redeploy so the next pod sees the secrets.
|
|
699
|
+
See [Host MCP OAuth](/docs/guides/mcp-oauth.md).
|
|
699
700
|
|
|
700
701
|
Run `agent-sdk validate` before deploying. It reports invalid domains and
|
|
701
702
|
secret names as warnings, so fix them even when validation exits zero.
|
|
@@ -943,7 +944,7 @@ A self-hosted server can read these credentials.
|
|
|
943
944
|
| Slack tokens | Slack channels | `<PREFIX>_SLACK_BOT_TOKEN` + `<PREFIX>_SLACK_APP_TOKEN` per agent |
|
|
944
945
|
| GitHub webhook secret | delivery signature verification | `GITHUB_WEBHOOK_SECRET`, same value on server and signer |
|
|
945
946
|
| GitHub API | outbound API calls | a GitHub App (`GITHUB_APP_ID` + `GITHUB_APP_PRIVATE_KEY` + installation id) or `GITHUB_TOKEN` / `gh auth login` |
|
|
946
|
-
| MCP connection tokens | authored MCP connections | env vars your `mcp-connections/*.ts` read, or host OAuth secrets from `agent-sdk mcp oauth <name> --store` (`MCP_OAUTH_*`; see [Host MCP OAuth](/docs/guides/mcp-oauth.md)) |
|
|
947
|
+
| MCP connection tokens | authored MCP connections | env vars your `mcp-connections/*.ts` read, or host OAuth secrets from hosted Connect / `agent-sdk mcp oauth <name> --store` (`MCP_OAUTH_*`; see [Host MCP OAuth](/docs/guides/mcp-oauth.md)) |
|
|
947
948
|
|
|
948
949
|
Use a dedicated Cursor key per host. `agent-sdk whoami` shows the active
|
|
949
950
|
credential. `logout` removes the stored key from the host; revoke the key
|
|
@@ -1087,414 +1088,1765 @@ Continue with these pages:
|
|
|
1087
1088
|
|
|
1088
1089
|
---
|
|
1089
1090
|
|
|
1090
|
-
Source: /docs/design/
|
|
1091
|
+
Source: /docs/design/runtime-abstraction.md
|
|
1092
|
+
|
|
1093
|
+
# Replacing `runtime: "local" | "cloud"` with a code interface
|
|
1094
|
+
|
|
1095
|
+
> **Status:** design proposal for review. No product code in this PR.
|
|
1096
|
+
> Implementation is split across later PRs (see §8). Do not start those
|
|
1097
|
+
> until this document has been reviewed.
|
|
1098
|
+
> **Audience:** Agent SDK and cursor-sdk maintainers; change-monitor as the
|
|
1099
|
+
> first consumer that is already faking a third runtime.
|
|
1100
|
+
> **Companions:** [cloud-runtime.md](/docs/guides/cloud-runtime.md) (today's
|
|
1101
|
+
> user-facing contract),
|
|
1102
|
+
> [factory/change-monitor/docs/tools.md](https://github.com/cursor/cursor/blob/main/factory/change-monitor/docs/tools.md)
|
|
1103
|
+
> (why change-monitor is pinned to local),
|
|
1104
|
+
> [factory/change-monitor/docs/computer-use.md](https://github.com/cursor/cursor/blob/main/factory/change-monitor/docs/computer-use.md)
|
|
1105
|
+
> (why a cloud VM as the *workspace* is not `runtime: "cloud"`).
|
|
1106
|
+
> **Checked against:** `@cursor/july` (`packages/agent-serve`) and
|
|
1107
|
+
> `@cursor/sdk` (`packages/cursor-sdk`) on `origin/main` as of 2026-08-31.
|
|
1108
|
+
|
|
1109
|
+
## Goals
|
|
1110
|
+
|
|
1111
|
+
1. **Stay compatible with agents that already ship.**
|
|
1112
|
+
The current `defineAgent` / `AgentOptions` shape keeps
|
|
1113
|
+
working: string `runtime`, sibling `local`, sibling
|
|
1114
|
+
`cloud`. Existing files do not need a rewrite.
|
|
1115
|
+
Constructors are additive (`localRuntime(opts)` takes
|
|
1116
|
+
today's `AgentLocalOptions`). The old fields are
|
|
1117
|
+
**deprecated and still supported** — JSDoc `@deprecated`
|
|
1118
|
+
plus published-docs notes — not removed in Phases 1–4.
|
|
1119
|
+
|
|
1120
|
+
2. **Default to `localRuntime()`.**
|
|
1121
|
+
That is today's default and the one developers mean when
|
|
1122
|
+
they omit `runtime`: local loop, host workspace, full
|
|
1123
|
+
harness toolset. `defineAgent({})` and
|
|
1124
|
+
`defineAgent({ runtime: "local" })` both become
|
|
1125
|
+
`localRuntime()`.
|
|
1126
|
+
|
|
1127
|
+
3. **Refuse incoherent pairs at discovery.**
|
|
1128
|
+
The string let authors write `runtime: "cloud"` next to
|
|
1129
|
+
`local: { sandbox, cwd, … }`, or `tools` on a cloud
|
|
1130
|
+
agent, and only some of those failed. `Runtime` is a
|
|
1131
|
+
closed union. `workspace` exists only on the local arm,
|
|
1132
|
+
so `cloudRuntime` cannot take one. `virtualRuntime`
|
|
1133
|
+
refuses `send({ runtime: cloudRuntime(...) })`.
|
|
1134
|
+
Combinations an arm cannot honor are errors, not
|
|
1135
|
+
ignored fields.
|
|
1136
|
+
|
|
1137
|
+
4. **Replace the enum with an extensible interface.**
|
|
1138
|
+
External users of `@cursor/july` / `@cursor/sdk` should
|
|
1139
|
+
be able to bring their own workspace (a `FileSystem`, a test
|
|
1140
|
+
map, a remote machine mount) without waiting for a new
|
|
1141
|
+
string value. `Workspace` is that interface.
|
|
1142
|
+
Do not grow `"local" | "cloud" | "virtual" | …`.
|
|
1143
|
+
|
|
1144
|
+
The Agent SDK exposes execution as a string on `defineAgent`:
|
|
1091
1145
|
|
|
1092
|
-
|
|
1146
|
+
```ts
|
|
1147
|
+
runtime?: "local" | "cloud" | "grokbot";
|
|
1148
|
+
```
|
|
1149
|
+
|
|
1150
|
+
`"local"` and `"cloud"` look like two places the same agent can run.
|
|
1151
|
+
They are not. The string selects a **loop backend**, a **workspace**, and
|
|
1152
|
+
an **artifact-delivery path** at once, and it already has a third value
|
|
1153
|
+
(`"grokbot"`) that fits neither name. Change-monitor then takes
|
|
1154
|
+
`"local"` and hollows it out: native shell off, native `read`/`grep`/
|
|
1155
|
+
`glob`/`ls` replaced by server tools over a virtual filesystem. That is
|
|
1156
|
+
a virtual workspace on the local loop, forced to claim it is local
|
|
1157
|
+
because there is no other legal value.
|
|
1158
|
+
|
|
1159
|
+
This document replaces the string with a closed `Runtime` union.
|
|
1160
|
+
The local arm carries a **workspace**. Cloud and grokbot do not.
|
|
1161
|
+
The workspace is one object whose optional methods (`read`,
|
|
1162
|
+
`ls`, `shell`, …) are the capabilities. Presence enables the
|
|
1163
|
+
matching tool. All methods share one path namespace.
|
|
1164
|
+
`workspace` exists only on the local arm, so `cloudRuntime`
|
|
1165
|
+
cannot take one.
|
|
1166
|
+
|
|
1167
|
+
The first implementation cut is native redirection in Cursor SDK
|
|
1168
|
+
(`AgentOptions.local.workspace` rebound through local-exec). Agent
|
|
1169
|
+
SDK then authors that workspace as `virtualRuntime(fs)`.
|
|
1170
|
+
|
|
1171
|
+
Lookalike server tools are not good enough. Change-monitor's
|
|
1172
|
+
`read` is an Agent SDK server tool, which the local harness
|
|
1173
|
+
exposes as MCP (`custom-user-tools`). The model gets
|
|
1174
|
+
`GetMcpTools` / `CallMcpTool`, not a builtin `read`. That is
|
|
1175
|
+
the bug. Copying native schemas does not fix the tool the
|
|
1176
|
+
model is offered. The SDK change exists so the model calls
|
|
1177
|
+
harness `read` against the injected workspace.
|
|
1178
|
+
|
|
1179
|
+
---
|
|
1180
|
+
|
|
1181
|
+
## 1. Chosen abstraction
|
|
1182
|
+
|
|
1183
|
+
```ts
|
|
1184
|
+
/**
|
|
1185
|
+
* Native tool args, open for fields the harness adds later.
|
|
1186
|
+
* Known keys match the model-facing input schema. Extra keys
|
|
1187
|
+
* are forwarded, not stripped.
|
|
1188
|
+
*/
|
|
1189
|
+
type NativeArgs<T> = T & { [key: string]: unknown };
|
|
1190
|
+
|
|
1191
|
+
/**
|
|
1192
|
+
* Path-namespace verbs. Method names match `@cursor/sdk`
|
|
1193
|
+
* `ToolName`s (`read`, `semSearch`, `readLints`). The model
|
|
1194
|
+
* sees the harness names (`Read`, `SemanticSearch`,
|
|
1195
|
+
* `ReadLints`). `write` is the exception: the model tool is
|
|
1196
|
+
* `Write`, but proto folded it into `edit_tool_call` — there
|
|
1197
|
+
* is no public `tools: ["write"]`. Args and results are those
|
|
1198
|
+
* tools' input/output schemas. `@cursor/sdk` owns this type.
|
|
1199
|
+
*/
|
|
1200
|
+
interface FileSystem {
|
|
1201
|
+
ls?(args: NativeArgs<LsArgs>): Promise<LsResult>;
|
|
1202
|
+
glob?(args: NativeArgs<GlobArgs>): Promise<GlobResult>;
|
|
1203
|
+
read?(args: NativeArgs<ReadArgs>): Promise<ReadResult>;
|
|
1204
|
+
grep?(args: NativeArgs<GrepArgs>): Promise<GrepResult>;
|
|
1205
|
+
/** Native Write tool: create or overwrite a whole file. */
|
|
1206
|
+
write?(args: NativeArgs<WriteArgs>): Promise<WriteResult>;
|
|
1207
|
+
/** Native Edit tool: str-replace / multi-replace / apply-patch. */
|
|
1208
|
+
edit?(args: NativeArgs<EditArgs>): Promise<EditResult>;
|
|
1209
|
+
delete?(args: NativeArgs<DeleteArgs>): Promise<DeleteResult>;
|
|
1210
|
+
semSearch?(args: NativeArgs<SemSearchArgs>): Promise<SemSearchResult>;
|
|
1211
|
+
readLints?(args: NativeArgs<ReadLintsArgs>): Promise<ReadLintsResult>;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* One namespace. File verbs and shell are optional methods —
|
|
1216
|
+
* presence is the capability. They must share `root()`.
|
|
1217
|
+
* Do not implement `read` against Origin and `shell` against
|
|
1218
|
+
* host bash.
|
|
1219
|
+
*/
|
|
1220
|
+
interface Workspace extends FileSystem {
|
|
1221
|
+
shell?(args: NativeArgs<ShellArgs>): Promise<ShellResult>;
|
|
1222
|
+
root(): string;
|
|
1223
|
+
brief?(ctx: WorkspaceBriefContext): string;
|
|
1224
|
+
}
|
|
1093
1225
|
|
|
1094
|
-
|
|
1226
|
+
/**
|
|
1227
|
+
* Closed union. `kind` is internal dispatch. Authors call
|
|
1228
|
+
* constructors. Each constructor returns its arm, not
|
|
1229
|
+
* `Runtime`, so `send({ runtime })` can require
|
|
1230
|
+
* `CloudRuntime | LocalRuntime` and exclude grokbot.
|
|
1231
|
+
* `workspace` exists only on the local arm.
|
|
1232
|
+
*/
|
|
1233
|
+
type LocalRuntime = { kind: "local"; workspace: Workspace };
|
|
1234
|
+
type CloudRuntime = { kind: "cloud"; cloud: AgentCloudOptions };
|
|
1235
|
+
type GrokbotRuntime = { kind: "grokbot" };
|
|
1236
|
+
type Runtime = LocalRuntime | CloudRuntime | GrokbotRuntime;
|
|
1237
|
+
|
|
1238
|
+
function localRuntime(opts?: AgentLocalOptions): LocalRuntime {
|
|
1239
|
+
return { kind: "local", workspace: localWorkspace(opts) };
|
|
1240
|
+
}
|
|
1095
1241
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1242
|
+
/**
|
|
1243
|
+
* Host-local with Cursor's local sandbox on. Same
|
|
1244
|
+
* `kind: "local"` family as `localRuntime` / `virtualRuntime`.
|
|
1245
|
+
* `cwd` / `workspaceDir` still configure the host tree.
|
|
1246
|
+
* The workspace omits `shell`: this object cannot honor
|
|
1247
|
+
* `sandboxOptions`. Host+sandbox turns keep the SDK's
|
|
1248
|
+
* `LocalShellExecutor` and do not inject the workspace.
|
|
1249
|
+
*/
|
|
1250
|
+
function sandboxRuntime(
|
|
1251
|
+
opts?: Omit<AgentLocalOptions, "sandbox">
|
|
1252
|
+
): LocalRuntime {
|
|
1253
|
+
return localRuntime({ ...opts, sandbox: true });
|
|
1254
|
+
}
|
|
1101
1255
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1256
|
+
/**
|
|
1257
|
+
* Local arm + a virtual workspace. Default is empty: `root()`
|
|
1258
|
+
* is `/`, no file/shell methods, no materialization, no
|
|
1259
|
+
* skills mount. Pass a `FileSystem` (or an async per-session
|
|
1260
|
+
* factory) to compose mounts. Authors pass a `FileSystem`,
|
|
1261
|
+
* not a `Workspace` — the lift is not a knob.
|
|
1262
|
+
*/
|
|
1263
|
+
function virtualRuntime(
|
|
1264
|
+
fs?: FileSystem | ((ctx: SessionWorkspaceContext) => Promise<FileSystem>)
|
|
1265
|
+
): LocalRuntime {
|
|
1266
|
+
return { kind: "local", workspace: virtualWorkspace(fs) };
|
|
1267
|
+
}
|
|
1108
1268
|
|
|
1109
|
-
|
|
1269
|
+
function cloudRuntime(cloud: AgentCloudOptions): CloudRuntime {
|
|
1270
|
+
return { kind: "cloud", cloud };
|
|
1271
|
+
}
|
|
1110
1272
|
|
|
1273
|
+
function grokbotRuntime(): GrokbotRuntime {
|
|
1274
|
+
return { kind: "grokbot" };
|
|
1275
|
+
}
|
|
1111
1276
|
```
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1277
|
+
|
|
1278
|
+
`cwd` / `workspaceDir` are arguments to `localRuntime` and
|
|
1279
|
+
`sandboxRuntime`, same as `repos` on `cloudRuntime`. They
|
|
1280
|
+
configure the host tree `localWorkspace(opts)` wraps. They
|
|
1281
|
+
are not a `Workspace` and they are not on `virtualRuntime`.
|
|
1282
|
+
Prefer `sandboxRuntime()` when the harness should run
|
|
1283
|
+
sandboxed. `localRuntime({ sandbox: true })` and sibling
|
|
1284
|
+
`local.sandbox` stay as equivalent deprecated sugar.
|
|
1285
|
+
The injected object on the Cursor SDK is
|
|
1286
|
+
`AgentOptions.local.workspace`. Cursor SDK's existing
|
|
1287
|
+
`WorkspaceRuntime` is the session factory, not `Workspace`.
|
|
1288
|
+
Host+sandbox turns do not inject: a Workspace that claimed
|
|
1289
|
+
`shell` would replace `LocalShellExecutor`.
|
|
1290
|
+
|
|
1291
|
+
Authoring:
|
|
1292
|
+
|
|
1293
|
+
```ts
|
|
1294
|
+
export default defineAgent({
|
|
1295
|
+
runtime: localRuntime(),
|
|
1296
|
+
});
|
|
1297
|
+
|
|
1298
|
+
export default defineAgent({
|
|
1299
|
+
runtime: sandboxRuntime({ workspaceDir: "/repo" }),
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
export default defineAgent({
|
|
1303
|
+
runtime: virtualRuntime(async (ctx) =>
|
|
1304
|
+
unionFs({
|
|
1305
|
+
"/repo": originRepoFs(bindingFor(ctx)),
|
|
1306
|
+
"/host": hostFilesFs(),
|
|
1307
|
+
"/agent/skills": skillsFs(),
|
|
1308
|
+
})
|
|
1309
|
+
),
|
|
1310
|
+
});
|
|
1311
|
+
|
|
1312
|
+
export default defineAgent({
|
|
1313
|
+
runtime: cloudRuntime({
|
|
1314
|
+
repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
|
|
1315
|
+
}),
|
|
1316
|
+
});
|
|
1119
1317
|
```
|
|
1120
1318
|
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1319
|
+
`localRuntime` / `sandboxRuntime` / `virtualRuntime` /
|
|
1320
|
+
`cloudRuntime` / `grokbotRuntime` are constructors over that
|
|
1321
|
+
union. They are not five implementations of one `fs()` +
|
|
1322
|
+
`shell()` pair. `sandboxRuntime` stays on the local arm.
|
|
1124
1323
|
|
|
1125
|
-
|
|
1324
|
+
### Backward compatibility
|
|
1126
1325
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1326
|
+
Today's fields stay on `defineAgent` and on Cursor
|
|
1327
|
+
`AgentOptions`. Constructors sit beside them. Nothing that
|
|
1328
|
+
compiles today is broken by Phase 2.
|
|
1130
1329
|
|
|
1131
|
-
|
|
1132
|
-
|
|
1330
|
+
```ts
|
|
1331
|
+
// Still valid. Deprecated. Same agent as localRuntime(opts).
|
|
1332
|
+
defineAgent({
|
|
1333
|
+
runtime: "local",
|
|
1334
|
+
local: { workspaceDir: "/repo", sandbox: true },
|
|
1335
|
+
});
|
|
1133
1336
|
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
`
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
(`
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
(
|
|
1272
|
-
|
|
1273
|
-
tool
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
not
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1337
|
+
// Preferred.
|
|
1338
|
+
defineAgent({
|
|
1339
|
+
runtime: sandboxRuntime({ workspaceDir: "/repo" }),
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
// Still valid. Deprecated. Same agent as cloudRuntime(opts).
|
|
1343
|
+
defineAgent({
|
|
1344
|
+
runtime: "cloud",
|
|
1345
|
+
cloud: { repos: [{ url: "https://github.com/org/repo", startingRef: "main" }] },
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
// Preferred.
|
|
1349
|
+
defineAgent({
|
|
1350
|
+
runtime: cloudRuntime({
|
|
1351
|
+
repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
|
|
1352
|
+
}),
|
|
1353
|
+
});
|
|
1354
|
+
```
|
|
1355
|
+
|
|
1356
|
+
| Today's field | Still does | Preferred |
|
|
1357
|
+
|---|---|---|
|
|
1358
|
+
| omit `runtime` / `runtime: "local"` | `localRuntime(agent.local)` | `runtime: localRuntime(opts)` |
|
|
1359
|
+
| `runtime: "cloud"` | `cloudRuntime(agent.cloud)` | `runtime: cloudRuntime(opts)` |
|
|
1360
|
+
| `runtime: "grokbot"` | `grokbotRuntime()` | `runtime: grokbotRuntime()` |
|
|
1361
|
+
| `local: { cwd, workspaceDir, sandbox }` | Host options when the string / omitted runtime is local | `localRuntime(opts)` or `sandboxRuntime(opts)`. Same `AgentLocalOptions`. |
|
|
1362
|
+
| `cloud: { repos, … }` | Payload for `runtime: "cloud"` | `cloudRuntime(opts)` |
|
|
1363
|
+
| `send({ cloud })` | This session becomes cloud; options merge over agent `cloud` | `send({ runtime: cloudRuntime(opts) })`. No merge. |
|
|
1364
|
+
| `send({ workspaceDir })` | Harness cwd for this local session | `send({ runtime: localRuntime({ workspaceDir }) })` |
|
|
1365
|
+
|
|
1366
|
+
Runtime payloads live on constructors: `AgentLocalOptions`
|
|
1367
|
+
on `localRuntime`, `AgentCloudOptions` on `cloudRuntime`.
|
|
1368
|
+
These stay on `defineAgent` / `send` / `Agent.create`, not
|
|
1369
|
+
on `Runtime`: `model`, `tools`, `builtinTools`, `hosting`,
|
|
1370
|
+
`architecture`, `workspaceFiles`.
|
|
1371
|
+
|
|
1372
|
+
`virtualRuntime` does not take `AgentLocalOptions`. There is
|
|
1373
|
+
no host cwd to point. `local.workspaceDir` / `sandbox` +
|
|
1374
|
+
`virtualRuntime` is still a discovery error.
|
|
1375
|
+
|
|
1376
|
+
Cursor SDK stays on `AgentOptions.local` / `.cloud` for
|
|
1377
|
+
Phases 1–4. Presence of `cloud` still dispatches. Phase 1
|
|
1378
|
+
**adds** `local.workspace`. july `localRuntime(opts)` writes
|
|
1379
|
+
cwd / sandbox / workspaceDir into that bag. Constructors on
|
|
1380
|
+
`Agent.create` are a later SDK deprecation, after july has
|
|
1381
|
+
shipped the same types. Do not leave “`cloud` means cloud”
|
|
1382
|
+
as the forever SDK contract — just do not do that cut in
|
|
1383
|
+
Phase 1.
|
|
1384
|
+
|
|
1385
|
+
Deprecation is documentation and JSDoc (`@deprecated` with
|
|
1386
|
+
the constructor equivalent). Do not delete the string or
|
|
1387
|
+
the sibling fields in Phases 1–4. Removal is a later
|
|
1388
|
+
window after Phase 4 has taught constructors. Subagents
|
|
1389
|
+
keep today's behavior, stored internally as `Runtime`
|
|
1390
|
+
(`G12`).
|
|
1391
|
+
|
|
1392
|
+
If a constructor and a sibling disagree (`localRuntime(a)`
|
|
1393
|
+
plus `local: b`; `cloudRuntime(a)` plus `cloud: b`;
|
|
1394
|
+
`virtualRuntime` plus `local.workspaceDir`), fail closed.
|
|
1395
|
+
If only the deprecated form is present, accept it.
|
|
1396
|
+
|
|
1397
|
+
`kind` is internal dispatch. Authors never write it; they
|
|
1398
|
+
call a constructor. `AgentRunner` stays on
|
|
1399
|
+
`StartServerInternalOptions` for replay and eval fakes. It
|
|
1400
|
+
is not a `Runtime` arm and not a `defineAgent` field.
|
|
1401
|
+
|
|
1402
|
+
`virtualRuntime` is an empty local+virtual workspace until the
|
|
1403
|
+
author unions mounts in. `root()` is always `/`. Nothing is
|
|
1404
|
+
materialized onto a host scratch dir; every path is served
|
|
1405
|
+
lazily from the `FileSystem`. The framework does not inject
|
|
1406
|
+
skills, `/repo`, or a fake cwd. `skillsFs()` is a july
|
|
1407
|
+
helper that returns a `FileSystem` the application mounts if
|
|
1408
|
+
it wants skills on the workspace.
|
|
1409
|
+
|
|
1410
|
+
Change-monitor's `search` (regex = `grep`, tags on
|
|
1411
|
+
`/issues`, reserved keyword / semantic) stays a consumer
|
|
1412
|
+
authored tool if they still want it. The framework does not
|
|
1413
|
+
grow a `search` method. `grep` and optional `semSearch`
|
|
1414
|
+
cover retrieval.
|
|
1415
|
+
|
|
1416
|
+
`virtualWorkspace` is an internal lift: take the `FileSystem`,
|
|
1417
|
+
set `root()` to `/`, derive `brief()` from the mount table
|
|
1418
|
+
when there is one, omit `shell` unless a mount is
|
|
1419
|
+
shell-backed. Authors do not compose that lift, and they
|
|
1420
|
+
cannot pass `localWorkspace()` in.
|
|
1421
|
+
|
|
1422
|
+
**Session factory.** `defineAgent` has no PR/SHA. A function
|
|
1423
|
+
argument runs **once per session**, not at discovery and not
|
|
1424
|
+
per turn. It is always async (`Promise<FileSystem>`). The
|
|
1425
|
+
returned `FileSystem` is reused for every turn and disposed
|
|
1426
|
+
with the session. Per-wake data (tenant plugin skills) is a
|
|
1427
|
+
lazy layer *inside* that filesystem, not a new Workspace. A
|
|
1428
|
+
stable tree (`memoryFs({ "/a.txt": "hi" })`) is passed
|
|
1429
|
+
directly and needs no factory.
|
|
1430
|
+
|
|
1431
|
+
```ts
|
|
1432
|
+
interface SessionWorkspaceContext {
|
|
1433
|
+
sessionId: string;
|
|
1434
|
+
agentName?: string;
|
|
1435
|
+
/** Channel continuation key when the session is addressable. */
|
|
1436
|
+
continuationKey?: string;
|
|
1437
|
+
/** Host services already on tool context. */
|
|
1438
|
+
host?: { files: HostFilesApi; reminders?: ReminderHostApi };
|
|
1439
|
+
}
|
|
1440
|
+
```
|
|
1441
|
+
|
|
1442
|
+
`sessionId` is the session when one exists. Unscoped
|
|
1443
|
+
`callTool` / `agent-sdk call` has no session row; the
|
|
1444
|
+
framework must not invent a shared default id that would
|
|
1445
|
+
collapse distinct callers onto one factory-backed store.
|
|
1446
|
+
|
|
1447
|
+
### One workspace, one namespace
|
|
1448
|
+
|
|
1449
|
+
File verbs and `shell` live on **`Workspace`**, not on `Runtime`.
|
|
1450
|
+
The local arm holds that object. Cloud and grokbot have no
|
|
1451
|
+
`workspace` field. Putting `read()` on `Runtime` re-bundles
|
|
1452
|
+
the string this document takes apart.
|
|
1453
|
+
|
|
1454
|
+
`FileSystem` is the shared file-verb type (`@cursor/sdk`).
|
|
1455
|
+
`Workspace` adds `shell` / `root` / `brief`. July authors pass
|
|
1456
|
+
a `FileSystem`; helpers (`memoryFs`, `unionFs`, `skillsFs`)
|
|
1457
|
+
return one. That is not an attachable `Shell`: `shell` stays
|
|
1458
|
+
on `Workspace` so it cannot be pointed at a different tree
|
|
1459
|
+
than `read`. Change-monitor avoids the split today by having
|
|
1460
|
+
no shell. computer-use.md avoids it by deriving every
|
|
1461
|
+
`/workspace` verb from one `RemoteMachine.exec`.
|
|
1462
|
+
|
|
1463
|
+
`unionFs(mounts)` composes FileSystems under absolute
|
|
1464
|
+
prefixes. Paths resolve by **longest prefix**. Method
|
|
1465
|
+
presence is the **union**: `write` is offered if any mount
|
|
1466
|
+
implements it. `shell` is not a FileSystem method and is
|
|
1467
|
+
not synthesized.
|
|
1468
|
+
|
|
1469
|
+
Unifying the type does **not** enforce the coupling. Two methods
|
|
1470
|
+
on one object can still have two backends. The rule is semantic:
|
|
1471
|
+
|
|
1472
|
+
**All Workspace methods share one path namespace, rooted at
|
|
1473
|
+
`root()`.** For `virtualRuntime`, that root is `/`.
|
|
1474
|
+
`ls({ path: "/repo" })` and `shell({ command: "ls /repo" })`
|
|
1475
|
+
see the same entries. Same tree, not the same formatter: the
|
|
1476
|
+
`ls` tool's ignore/dotfile rules and `shell({ command: "ls -la" })`
|
|
1477
|
+
may still render differently, as they do on a real machine.
|
|
1478
|
+
|
|
1479
|
+
The framework cannot typecheck that. The public constructors keep
|
|
1480
|
+
the invariant by not taking a raw `Workspace`:
|
|
1481
|
+
|
|
1482
|
+
```ts
|
|
1483
|
+
// Host. File verbs and shell wrap the same cwd.
|
|
1484
|
+
localRuntime(opts) → localWorkspace(opts)
|
|
1485
|
+
|
|
1486
|
+
// Host + Cursor sandbox. Workspace omits shell.
|
|
1487
|
+
sandboxRuntime(opts) → localWorkspace({ ...opts, sandbox: true })
|
|
1488
|
+
|
|
1489
|
+
// Empty virtual. `/`, no methods, no mounts, no skills.
|
|
1490
|
+
virtualRuntime()
|
|
1491
|
+
|
|
1492
|
+
// Composed virtual. Application unions what it needs.
|
|
1493
|
+
virtualRuntime(unionFs({
|
|
1494
|
+
"/repo": originRepoFs(binding),
|
|
1495
|
+
"/host": hostFilesFs(),
|
|
1496
|
+
"/agent/skills": skillsFs(),
|
|
1497
|
+
"/workspace": cloudFs(remoteMachine),
|
|
1498
|
+
}))
|
|
1499
|
+
|
|
1500
|
+
// Tests.
|
|
1501
|
+
virtualRuntime(memoryFs({ "/a.txt": "hi" }))
|
|
1502
|
+
```
|
|
1503
|
+
|
|
1504
|
+
A shell-primary mount (`cloudFs`) is a `FileSystem`
|
|
1505
|
+
implementation, not a second runtime argument. You union it
|
|
1506
|
+
in. `shell` appears on the lifted Workspace only when that
|
|
1507
|
+
mount (or the union) is shell-backed — same tree as `ls` /
|
|
1508
|
+
`read` under `/workspace`. A TypeScript isolate (tools.md §8)
|
|
1509
|
+
is an authored `script` tool, not a `{ script: true }` flag
|
|
1510
|
+
on `virtualRuntime`.
|
|
1511
|
+
|
|
1512
|
+
Do not put `shell` on a separate type the caller attaches later.
|
|
1513
|
+
Do not take `Workspace` as a public argument to `virtualRuntime`:
|
|
1514
|
+
that is how `virtualRuntime(localWorkspace())` happens.
|
|
1515
|
+
|
|
1516
|
+
### Capability is method presence
|
|
1517
|
+
|
|
1518
|
+
Every Workspace method above except `root` is optional.
|
|
1519
|
+
The framework exposes the matching tool iff the method is
|
|
1520
|
+
implemented. A missing method is a missing tool. This is
|
|
1521
|
+
change-monitor's optional-write rule ("optionality is earned")
|
|
1522
|
+
applied to the whole workspace.
|
|
1523
|
+
|
|
1524
|
+
| Method present | Model-facing tool | Method absent |
|
|
1525
|
+
|---|---|---|
|
|
1526
|
+
| `read` | `Read` | not offered |
|
|
1527
|
+
| `ls` | `LS` | not offered |
|
|
1528
|
+
| `glob` | `Glob` | not offered |
|
|
1529
|
+
| `grep` | `Grep` | not offered |
|
|
1530
|
+
| `write` | `Write` | not offered |
|
|
1531
|
+
| `edit` | `StrReplace` | not offered |
|
|
1532
|
+
| `delete` | `Delete` | not offered |
|
|
1533
|
+
| `semSearch` | `SemanticSearch` | not offered |
|
|
1534
|
+
| `readLints` | `ReadLints` | not offered |
|
|
1535
|
+
| `shell` | `Shell` | not offered |
|
|
1536
|
+
|
|
1537
|
+
Those are the latest/default harness names. Some prompt
|
|
1538
|
+
versions rename them (`read_file`, `search_replace`,
|
|
1539
|
+
`list_dir`, `codebase_search`, `run_terminal_cmd`). The
|
|
1540
|
+
binding does not invent a fourth name.
|
|
1541
|
+
|
|
1542
|
+
`@cursor/sdk` `AgentOptions.tools` and stream `tool_call`
|
|
1543
|
+
events stay camelCase (`read`, `edit`, `semSearch`,
|
|
1544
|
+
`readLints`, `shell`, …). There is no public
|
|
1545
|
+
`tools: ["write"]`. `Write` and `StrReplace` both travel as
|
|
1546
|
+
proto `edit_tool_call`.
|
|
1547
|
+
|
|
1548
|
+
`tools: ["edit"]` means the **envelope**, not
|
|
1549
|
+
`workspace.edit`. It is satisfied if `write` **or** `edit`
|
|
1550
|
+
is present. A write-only workspace with `tools: ["edit"]`
|
|
1551
|
+
is valid and offers `Write` only. `tools: ["edit"]` on a
|
|
1552
|
+
workspace with neither method is a discovery error. Method
|
|
1553
|
+
presence still decides which of `Write` / `StrReplace` are
|
|
1554
|
+
registered. A harness that already offers `MultiStrReplace`
|
|
1555
|
+
or `ApplyPatch` for that model keeps doing so when `edit`
|
|
1556
|
+
is present, and omits them when it is not.
|
|
1557
|
+
|
|
1558
|
+
Cursor SDK binding: derive `AgentOptions.tools` from the
|
|
1559
|
+
presence set (`write` or `edit` contributes `edit` on the
|
|
1560
|
+
allowlist), then re-point those natives at the workspace.
|
|
1561
|
+
The model calls harness `Read`, not MCP `read` on
|
|
1562
|
+
`custom-user-tools`.
|
|
1563
|
+
|
|
1564
|
+
**Presence means a function, not a stub.**
|
|
1565
|
+
`read() { throw new Error("unsupported") }` still enables `read`.
|
|
1566
|
+
Omit the method.
|
|
1567
|
+
|
|
1568
|
+
**This gates the file/shell family only.**
|
|
1569
|
+
Do not grow Workspace to enumerate every harness builtin.
|
|
1570
|
+
`updateTodos` and `webSearch` do not share `root()`. Putting
|
|
1571
|
+
them on Workspace breaks the namespace rule and still does not
|
|
1572
|
+
make them work on `cloudRuntime` (that arm has no workspace
|
|
1573
|
+
field). Availability is not injectability.
|
|
1574
|
+
|
|
1575
|
+
**`tools` may only narrow, fail-closed.**
|
|
1576
|
+
|
|
1577
|
+
```
|
|
1578
|
+
workspaceCaps = methodsPresent(workspace) // { read, ls, grep, … }
|
|
1579
|
+
fileRequested = agent.tools?.filter(isFileOrShell)
|
|
1580
|
+
harnessRequested = agent.tools?.filter(isHarnessBuiltin)
|
|
1581
|
+
|
|
1582
|
+
fileExposed =
|
|
1583
|
+
fileRequested === undefined ? workspaceCaps : fileRequested
|
|
1584
|
+
|
|
1585
|
+
harnessExposed =
|
|
1586
|
+
agent.tools === undefined
|
|
1587
|
+
? (virtualRuntime ? ∅ : runtime.defaultHarnessTools)
|
|
1588
|
+
: harnessRequested ∩ runtime.harnessTools
|
|
1589
|
+
```
|
|
1590
|
+
|
|
1591
|
+
If `agent.tools` names `grep` and `workspace.grep` is missing,
|
|
1592
|
+
refuse at discovery — same posture as today's "restriction that
|
|
1593
|
+
silently does not apply." `tools: ["edit"]` is the exception:
|
|
1594
|
+
it names the envelope, so refuse only when both `write` and
|
|
1595
|
+
`edit` are missing. If it names `webSearch` and the loop
|
|
1596
|
+
does not have that builtin (grokbot), refuse the same way.
|
|
1597
|
+
If `agent.tools` is unset, the workspace *is* the file/shell
|
|
1598
|
+
allowlist. `"mcp"` is still granted when server tools or
|
|
1599
|
+
connections exist; it is transport, not a file verb and not a
|
|
1600
|
+
harness builtin.
|
|
1601
|
+
|
|
1602
|
+
Host `localRuntime()` implements every file/shell method and
|
|
1603
|
+
keeps today's default harness set, so the toolset does not
|
|
1604
|
+
shrink. `virtualRuntime` with `read` + `ls` + `grep` + `glob`
|
|
1605
|
+
and no `shell` is change-monitor without a redundant
|
|
1606
|
+
`tools: ["read", …]` line. Harness builtins stay off that
|
|
1607
|
+
default; opt in:
|
|
1608
|
+
|
|
1609
|
+
```ts
|
|
1610
|
+
defineAgent({
|
|
1611
|
+
runtime: virtualRuntime(fs),
|
|
1612
|
+
tools: ["read", "ls", "grep", "glob", "webSearch", "updateTodos"],
|
|
1613
|
+
});
|
|
1614
|
+
```
|
|
1615
|
+
|
|
1616
|
+
`tools` is one allowlist. Listing only `["webSearch"]` turns
|
|
1617
|
+
file/shell natives off, even if the workspace implements them.
|
|
1618
|
+
|
|
1619
|
+
`tools: []` is an empty allowlist, not "legacy suppress
|
|
1620
|
+
natives." `[] ∩ workspaceCaps = ∅`. Phase 2 must omit
|
|
1621
|
+
`tools` on `virtualRuntime` agents so workspace methods
|
|
1622
|
+
become the file/shell allowlist. List harness builtins
|
|
1623
|
+
only to opt those in. Change-monitor drops today's
|
|
1624
|
+
`tools: []` when it cuts over (Phase 3).
|
|
1625
|
+
|
|
1626
|
+
### Three tool families
|
|
1627
|
+
|
|
1628
|
+
| Family | Examples | Owner | How the model gets the native tool |
|
|
1629
|
+
|---|---|---|---|
|
|
1630
|
+
| **Workspace** | `read`, `ls`, `grep`, `glob`, `write`, `edit`, `delete`, `semSearch`, `readLints`, `shell` | Injected `Workspace` (`FileSystem` + `shell`) | Method presence → native tool. Args are that tool's schema. Omit the method if this workspace cannot honor it. |
|
|
1631
|
+
| **Harness** | `webSearch`, `webFetch`, `updateTodos`, `readTodos`, `askQuestion`, `await`, `generateImage` | The **Runtime arm's** Cursor harness | `defineAgent({ tools })`. Same builtin name. Do not put these on Workspace. |
|
|
1632
|
+
| **Transport / spawn** | `mcp`, `task` | Runtime arm | `mcp` when server tools or connections exist. `task` is subagents (`G12`). |
|
|
1633
|
+
|
|
1634
|
+
**Framework server tools.** Agent SDK `builtinTools`
|
|
1635
|
+
(`reminders`, …) are `execution: "server"`. Local
|
|
1636
|
+
in-process; cloud via HTTP MCP; grokbot blocked. They are
|
|
1637
|
+
not Cursor `ToolName`s and not Workspace methods.
|
|
1638
|
+
|
|
1639
|
+
No `Harness` interface on `Runtime`. `webSearch` is the
|
|
1640
|
+
backend; there is nothing to inject. `updateTodos` is
|
|
1641
|
+
conversation state the local harness already keeps. A third
|
|
1642
|
+
object would re-bundle the string (Runtime arm + workspace +
|
|
1643
|
+
"the rest") and still could not be honored on cloud.
|
|
1644
|
+
|
|
1645
|
+
If a later consumer needs a custom todo store or a stub
|
|
1646
|
+
`webSearch` in evals, add executor overrides on
|
|
1647
|
+
`AgentOptions.local` the same way Workspace landed — a rebind
|
|
1648
|
+
seam, not a constructor argument.
|
|
1649
|
+
|
|
1650
|
+
### What each constructor actually offers
|
|
1651
|
+
|
|
1652
|
+
`virtual` and `local` share the **local loop**. Harness
|
|
1653
|
+
builtins are the same implementations: backend `webSearch`,
|
|
1654
|
+
in-process todos. The workspace is what changes. Cloud is a
|
|
1655
|
+
different harness that already includes those builtins and
|
|
1656
|
+
cannot select them.
|
|
1657
|
+
|
|
1658
|
+
| | `localRuntime()` | `virtualRuntime(fs)` | `cloudRuntime(opts)` | `grokbotRuntime()` |
|
|
1659
|
+
|---|---|---|---|---|
|
|
1660
|
+
| File/shell natives | Host workspace; `tools` narrows | Workspace methods only | VM workspace; cannot restrict | Box workspace; cannot restrict |
|
|
1661
|
+
| `webSearch` / `webFetch` | Default on; `tools` narrows | Off until listed in `tools` | On (VM default); `tools` refused | Fail closed if listed |
|
|
1662
|
+
| `updateTodos` / `readTodos` | Same | Off until listed | On (VM default); `tools` refused | Fail closed if listed |
|
|
1663
|
+
| `task` | Default on | Off until listed; children inherit parent workspace (`G12`) | On; cannot restrict | Blocked |
|
|
1664
|
+
| `mcp` | Granted for server tools / connections | Same (local loop) | Bridged HTTP MCP when configured | Blocked |
|
|
1665
|
+
| `edit` / `delete` / `semSearch` / `readLints` | Default on (`localWorkspace`) | Off unless the `FileSystem` implements the method | On (VM tree) | Box default |
|
|
1666
|
+
|
|
1667
|
+
`sandboxRuntime()` is the same local loop as `localRuntime()`,
|
|
1668
|
+
with Cursor sandbox on. The workspace omits `shell`. Host+sandbox
|
|
1669
|
+
turns keep `LocalShellExecutor` and do not inject the workspace.
|
|
1670
|
+
|
|
1671
|
+
One agent file cannot express the same *selected* toolset on
|
|
1672
|
+
all three constructors until the cloud API honors `tools`
|
|
1673
|
+
(non-goal, §10). `tools: ["webSearch", "updateTodos"]` on
|
|
1674
|
+
`cloudRuntime` is still a discovery error: we cannot prove the
|
|
1675
|
+
VM will offer only those, and we must not run unrestricted
|
|
1676
|
+
and pretend we selected.
|
|
1677
|
+
|
|
1678
|
+
What *is* true without that API:
|
|
1679
|
+
|
|
1680
|
+
- Local (including today's `tools: []` agents): list the
|
|
1681
|
+
harness names. That already works. `virtualRuntime` does
|
|
1682
|
+
not unlock `updateTodos` / `webSearch`; the allowlist does.
|
|
1683
|
+
- Cloud: those names are already on the VM. Authors who need
|
|
1684
|
+
them on a cloud agent omit `tools` and accept the rest of
|
|
1685
|
+
the VM workspace.
|
|
1686
|
+
- Do not special-case "harness-only `tools` + cloud" as
|
|
1687
|
+
silently honored. That is the hybrid footgun with extra
|
|
1688
|
+
steps.
|
|
1689
|
+
|
|
1690
|
+
**Do not synthesize omitted verbs.**
|
|
1691
|
+
Change-monitor's current mounts require `grep` on every *mount* because
|
|
1692
|
+
it is derivable from `glob` + `read`. That is a mount-quality
|
|
1693
|
+
rule, not a Workspace-to-tool rule. If the workspace omits `grep`,
|
|
1694
|
+
the model does not get a generated grep that secretly scans via
|
|
1695
|
+
`read`. An implementation that has `glob` and `read` should
|
|
1696
|
+
usually implement `grep` (delegate to `scanGrep`); the framework
|
|
1697
|
+
does not invent it.
|
|
1698
|
+
|
|
1699
|
+
**`write` and `edit` are two native tools, not one.**
|
|
1700
|
+
|
|
1701
|
+
Str-replace `edit` cannot create a file (`oldText: ""`
|
|
1702
|
+
errors). The harness still offers a **Write** tool
|
|
1703
|
+
(`{ path, fileText }`) for create-or-overwrite. Apply-patch
|
|
1704
|
+
`edit` can create via `*** Add File:`. Proto `WriteToolCall`
|
|
1705
|
+
was folded into the `EditToolCall` envelope; the model-facing
|
|
1706
|
+
Write tool remains.
|
|
1707
|
+
|
|
1708
|
+
Workspace keeps both methods, matching those two tools:
|
|
1709
|
+
|
|
1710
|
+
- `write` — native Write schema (`path`, `fileText`, open for
|
|
1711
|
+
later fields). Create or replace a whole file.
|
|
1712
|
+
- `edit` — native Edit schema (`path`, plus `oldText` /
|
|
1713
|
+
`newText`, `edits[]`, or `patchContent`). Change an
|
|
1714
|
+
existing file; apply-patch may add.
|
|
1715
|
+
- `grep` — native result kinds are `"content"` | `"files"` |
|
|
1716
|
+
`"count"` (not `"files_with_matches"`).
|
|
1717
|
+
|
|
1718
|
+
Presence of one does not synthesize the other. A
|
|
1719
|
+
`FileSystem` that implements `write` and omits `edit` offers
|
|
1720
|
+
Write only. That workspace may still list `tools: ["edit"]`
|
|
1721
|
+
(envelope). It must not register `StrReplace`.
|
|
1722
|
+
Change-monitor's CAS `write` / authored str-replace stay
|
|
1723
|
+
authored tools until the mount implements these native
|
|
1724
|
+
schemas. Do not keep a third write contract.
|
|
1725
|
+
|
|
1726
|
+
**`edit`, `delete`, `semSearch`, `readLints` are Workspace
|
|
1727
|
+
methods.** They see `root()`. Optional: `localWorkspace()`
|
|
1728
|
+
implements what the host harness already has;
|
|
1729
|
+
`virtualWorkspace(fs)` omits a method until that
|
|
1730
|
+
`FileSystem` can honor it. The type includes them now. A
|
|
1731
|
+
tree that cannot index or lint simply leaves the method off.
|
|
1732
|
+
|
|
1733
|
+
- `delete` — path verb. `DeleteExecutor` exists; Phase 1 adds
|
|
1734
|
+
the override and fail-closed omission.
|
|
1735
|
+
- `edit` — same tree as `write`. Native schema stays native.
|
|
1736
|
+
Phase 1 fail-closed-omits host `edit` when the method is
|
|
1737
|
+
missing so a virtual tree cannot be patched on disk.
|
|
1738
|
+
Implementing the virtual body can wait for a writable
|
|
1739
|
+
mount (change-monitor Phase 3).
|
|
1740
|
+
- `semSearch` — native schema. Host walks a **codebase index**,
|
|
1741
|
+
then `read`s snippets. A virtual `FileSystem` omits it
|
|
1742
|
+
until that tree has an index. Do not leave host
|
|
1743
|
+
`semSearch` registered against a virtual workspace. Do not
|
|
1744
|
+
alias change-monitor's authored `search` (tags / regex)
|
|
1745
|
+
onto this method.
|
|
1746
|
+
- `readLints` — `diagnosticsExecutor` (LSP against the tree).
|
|
1747
|
+
Same presence rule. A virtual tree has no language service
|
|
1748
|
+
unless a mount provides one.
|
|
1749
|
+
|
|
1750
|
+
### Effect and approvals stay outside Workspace
|
|
1751
|
+
|
|
1752
|
+
Defaults do not change. `localRuntime()` / no `workspace` is
|
|
1753
|
+
today's `LocalWriteExecutor` + `permissionsService` +
|
|
1754
|
+
pending-decision cards.
|
|
1755
|
+
|
|
1756
|
+
Two planes already exist. Workspace implements neither.
|
|
1757
|
+
|
|
1758
|
+
| Plane | What it is | Who configures it | Virtual |
|
|
1759
|
+
|---|---|---|---|
|
|
1760
|
+
| **Effect** (`read` / `write`) | Dry-run and trace: reads run, writes stub. Native file/shell verbs keep today's effects (`read`/`ls`/`grep`/`glob`/`semSearch`/`readLints` = read; `write`/`edit`/`delete`/`shell` = write). | Existing tool `effect` for authored tools. Natives keep harness defaults. | Same defaults. A Workspace method cannot relabel `edit` as read. |
|
|
1761
|
+
| **Approval policy** | Human-in-the-loop before an effectful call runs. | Today's Agent SDK / harness approval config. | Same policy. The adapter runs **after** the approval gate, not instead of `LocalWriteExecutor`. |
|
|
1762
|
+
|
|
1763
|
+
```
|
|
1764
|
+
tool call → effect / dry-run → approval policy → Workspace method
|
|
1765
|
+
```
|
|
1766
|
+
|
|
1767
|
+
Host path allowlists, `.cursorignore`, and worktree guards
|
|
1768
|
+
live on **`localWorkspace` only**. They key on host paths.
|
|
1769
|
+
Virtual paths (`/repo/…`) are a different namespace; wrapping
|
|
1770
|
+
them in `permissionsService.shouldBlockWrite` would block or
|
|
1771
|
+
prompt every virtual call. Mount policy (CAS, publish ladder)
|
|
1772
|
+
stays inside the `FileSystem`.
|
|
1773
|
+
|
|
1774
|
+
Eval `memoryFs` uses the same gates. If the agent's approval
|
|
1775
|
+
policy is off (today's default for many local agents), no new
|
|
1776
|
+
cards appear.
|
|
1777
|
+
|
|
1778
|
+
`@cursor/sdk` exports `FileSystem` and `Workspace`. July
|
|
1779
|
+
authors pass a `FileSystem` into `virtualRuntime`. Helpers
|
|
1780
|
+
return `FileSystem`. Do not add a `search` method: the regex
|
|
1781
|
+
arm is `grep`, `semSearch` is already on `FileSystem`,
|
|
1782
|
+
keyword has no implementation, and tags is an unproven
|
|
1783
|
+
change-monitor issues-mount feature. If they still want tag
|
|
1784
|
+
lookup, it stays an authored tool on their mounts.
|
|
1785
|
+
|
|
1786
|
+
Phase 1 does not need virtual implementations of these four.
|
|
1787
|
+
It does need the omit path: when a workspace is set and the
|
|
1788
|
+
method is absent, the host executor is not registered.
|
|
1789
|
+
|
|
1790
|
+
### Environment brief is not `Runtime.instructions()`
|
|
1791
|
+
|
|
1792
|
+
The workspace owns facts the model must know — `root()`,
|
|
1793
|
+
mount table, "there is no checkout," grep dialect. Change-monitor
|
|
1794
|
+
currently writes those into `agent/instructions.md` because there
|
|
1795
|
+
is nowhere else to put them. Cloud already injects loop-owned
|
|
1796
|
+
prose the same way (`buildAgentsMdContent` prepends a server-tool
|
|
1797
|
+
MCP catalog and a memory-mount path).
|
|
1798
|
+
|
|
1799
|
+
That is a real injection point. It is not
|
|
1800
|
+
`Runtime.instructions() -> string`.
|
|
1801
|
+
|
|
1802
|
+
`defineAgent({ instructions })` / `instructions.md` is the
|
|
1803
|
+
agent's job: role, steering, what to publish. A method with the
|
|
1804
|
+
same name implies it replaces that file. Today's prompt is
|
|
1805
|
+
already layered, and the layers have different owners:
|
|
1806
|
+
|
|
1807
|
+
| Layer | Owner | When / where |
|
|
1808
|
+
|---|---|---|
|
|
1809
|
+
| `instructions.md` | Agent author | Always; identity of the agent |
|
|
1810
|
+
| `## About you` | Framework | Folded into AGENTS.md / preamble |
|
|
1811
|
+
| Server-tool MCP catalog, "write the script if missing" | **Runtime arm / delivery** | Cloud first prompt; grokbot `createSession({ instructions })` |
|
|
1812
|
+
| `<agentkit_context>` (session, channel, time) | Framework | First user prompt only |
|
|
1813
|
+
| Harness `<user_info>` `Workspace Path` | **Workspace** (today: cwd) | Local harness, every turn |
|
|
1814
|
+
| Mount table, dialects, "no shell" | **Workspace** | Missing as an API; stuffed into instructions.md |
|
|
1815
|
+
|
|
1816
|
+
A string on `Runtime` re-bundles those layers and cannot say
|
|
1817
|
+
which channel to use. Returning prose does not update harness
|
|
1818
|
+
`Workspace Path`. `virtualRuntime` sets `root()` to `/` and
|
|
1819
|
+
does not materialize a scratch cwd. `local.workspaceDir` +
|
|
1820
|
+
virtual is a discovery error. `root()` is the contract; if it
|
|
1821
|
+
disagrees with `brief()`, the model gets two maps.
|
|
1822
|
+
|
|
1823
|
+
Ownership rules:
|
|
1824
|
+
|
|
1825
|
+
1. **Agent instructions stay authored.** The framework never asks
|
|
1826
|
+
a runtime to supply the agent's job description.
|
|
1827
|
+
2. **Workspace brief is an addendum.** Mount-table paragraphs move
|
|
1828
|
+
here; "You write a monitoring plan for one PR" does not.
|
|
1829
|
+
3. **Arm-owned catalogs stay on the arm.** Cloud's
|
|
1830
|
+
`agentsdk-tools` preamble and grokbot's composed blob are
|
|
1831
|
+
delivery, not `Workspace.brief()`.
|
|
1832
|
+
4. **Brief is session-scoped.** Planner mounts depend on the
|
|
1833
|
+
dispatcher's binding (repo, SHA, which mounts exist).
|
|
1834
|
+
`brief(ctx)` runs when the session workspace is built.
|
|
1835
|
+
5. **Prefer structure over a blob.** `root()` is what
|
|
1836
|
+
the harness honors. Do not grow `brief()` into a second
|
|
1837
|
+
instructions.md.
|
|
1838
|
+
|
|
1839
|
+
### What `shell` is allowed to be
|
|
1840
|
+
|
|
1841
|
+
`shell` is "run a program, return stdout/stderr/exit." Language
|
|
1842
|
+
and isolation are implementation details. The namespace is not:
|
|
1843
|
+
`shell` must see the same tree as `ls` / `read`.
|
|
1844
|
+
|
|
1845
|
+
| Implementation | Language | Isolation | How the tree stays one |
|
|
1846
|
+
|---|---|---|---|
|
|
1847
|
+
| Host | bash / zsh / cmd | Serve-host process, optional sandbox | Same cwd as file verbs |
|
|
1848
|
+
| Script isolate (tools.md §8) | TypeScript | Host-side isolate; only bound Workspace methods | `shell` *is* those methods |
|
|
1849
|
+
| Remote machine (computer-use.md) | bash on the pod | anyrun microVM, no host secrets | File verbs are commands over the same `shell` |
|
|
1850
|
+
|
|
1851
|
+
A virtual workspace may omit `shell` (change-monitor today). The
|
|
1852
|
+
loop does not change.
|
|
1853
|
+
|
|
1854
|
+
A TypeScript isolate and host bash are different trust classes.
|
|
1855
|
+
Do not overload one `shell` method for both. Omit `shell` and
|
|
1856
|
+
keep `script` as an authored tool (tools.md §8). A shell-backed
|
|
1857
|
+
mount (`cloudFs`) is the other `shell`. If those two ever
|
|
1858
|
+
need one method, add a `kind` then — not as a staged "v2."
|
|
1859
|
+
|
|
1860
|
+
### What stays off `Runtime`
|
|
1861
|
+
|
|
1862
|
+
- **Inference.** Local Connect client stays framework-owned.
|
|
1863
|
+
- **`AgentRunner`.** Internal `startServer` test/host seam.
|
|
1864
|
+
Not a `Runtime` arm.
|
|
1865
|
+
- **MCP / OAuth / peer URLs.** Host capabilities, already on
|
|
1866
|
+
`ctx.host` and connection files.
|
|
1867
|
+
- **Server tools / `builtinTools`.** Host-side,
|
|
1868
|
+
`execution: "server"`. They are not workspace verbs.
|
|
1869
|
+
- **`ctx.host.files`.** Durable store. A workspace *may* mount it
|
|
1870
|
+
(change-monitor `/host`); the store API itself is not FS.
|
|
1871
|
+
|
|
1872
|
+
### Composition rules
|
|
1873
|
+
|
|
1874
|
+
1. **`localRuntime(opts?)`** → `{ kind: "local", workspace: localWorkspace(opts) }`
|
|
1875
|
+
(today). Sibling `local` is deprecated sugar for `opts`.
|
|
1876
|
+
**`sandboxRuntime(opts?)`** is the named constructor for
|
|
1877
|
+
host-local + Cursor sandbox: `localRuntime({ ...opts, sandbox: true })`.
|
|
1878
|
+
The workspace omits `shell`. Host+sandbox turns do not
|
|
1879
|
+
inject that workspace into the SDK.
|
|
1880
|
+
2. **`virtualRuntime(fs?)`** → local arm + that workspace.
|
|
1881
|
+
Each implemented method exposes that tool. Authored
|
|
1882
|
+
`execution: "server"` tools stay on the host. Effect and
|
|
1883
|
+
approval policy wrap the tool call, then the Workspace
|
|
1884
|
+
runs. `tools` may only narrow, fail-closed.
|
|
1885
|
+
`local.workspaceDir` / `sandbox` + virtual is a discovery
|
|
1886
|
+
error.
|
|
1887
|
+
3. **`cloudRuntime` / `grokbotRuntime` have no `workspace`
|
|
1888
|
+
field.** Those arms own their tree. A machine API would
|
|
1889
|
+
add a field on that arm later, not a sibling we then
|
|
1890
|
+
refuse.
|
|
1891
|
+
4. **`tools` allowlist + cloud/grokbot** → same fail-closed
|
|
1892
|
+
errors as today.
|
|
1893
|
+
5. **Per-send `runtime?: CloudRuntime | LocalRuntime`.**
|
|
1894
|
+
Host-local + `cloudRuntime` → attach. Host-local +
|
|
1895
|
+
`localRuntime(opts)` / `sandboxRuntime(opts)` → overlay
|
|
1896
|
+
cwd / sandbox / workspaceDir. Cloud + `cloudRuntime` → replace the
|
|
1897
|
+
session cloud payload (no merge). Virtual + either →
|
|
1898
|
+
refuse. Cloud / grokbot + `localRuntime` → refuse.
|
|
1899
|
+
Grokbot + `cloudRuntime` → refuse (today's throw).
|
|
1900
|
+
`grokbotRuntime` is not in the field type. Deprecated
|
|
1901
|
+
`send({ cloud })` / `send({ workspaceDir })` stay as
|
|
1902
|
+
shims. `send({ runtime, cloud })` and
|
|
1903
|
+
`send({ runtime, workspaceDir })` fail closed.
|
|
1904
|
+
6. **Delivery** is derived from the arm and, on local, which
|
|
1905
|
+
workspace: host → materialize; virtual → no host
|
|
1906
|
+
workspace, `Workspace Path` is `/`, skills only if the
|
|
1907
|
+
author mounted `skillsFs()`; cloud → preamble + store;
|
|
1908
|
+
grokbot → session instructions.
|
|
1909
|
+
|
|
1910
|
+
---
|
|
1911
|
+
|
|
1912
|
+
## 2. What the string actually selects
|
|
1913
|
+
|
|
1914
|
+
`AgentRuntime` lives on the Agent SDK config
|
|
1915
|
+
(`packages/agent-serve/src/types.ts`). The Cursor SDK has **no**
|
|
1916
|
+
`runtime` field on `AgentOptions`. It routes on `options.cloud`
|
|
1917
|
+
(or a `bc-` agent id) in `createDefaultAgent` /
|
|
1918
|
+
`resumeDefaultAgent` (`packages/cursor-sdk/src/agent/platform.ts`).
|
|
1919
|
+
The Agent SDK string is a product-level selector that then builds
|
|
1920
|
+
different SDK option bags.
|
|
1921
|
+
|
|
1922
|
+
There are already three values, plus a hybrid, plus a consumer
|
|
1923
|
+
that forges a fourth:
|
|
1924
|
+
|
|
1925
|
+
| | Local loop | Cloud loop | Grok Bot loop | Change-monitor today |
|
|
1926
|
+
|---|---|---|---|---|
|
|
1927
|
+
| **Turn runner** | Cursor SDK local harness on the serve host (`sdk-runner.ts`) | Cursor cloud API (`POST /v1/agents`, SSE) | Hosted `/v0/grokbot` client (`grokbot/runner.ts`) | Local harness |
|
|
1928
|
+
| **Inference** | Backend `AgentService` via Connect; model from `defineAgent` | Coupled inside the cloud agent service; model optional | Hosted harness picks the model; `defineAgent.model` ignored | Same as local |
|
|
1929
|
+
| **Workspace (builtins)** | Host OS via `LocalResourceProvider` | Cloud VM; SDK is an event client only | The account's Sand box | Authored server tools over a consumer filesystem |
|
|
1930
|
+
| **`tools` allowlist** | Enforced (`x-cursor-agent-allowed-tools`) | `ConfigurationError` if set; discovery / hybrid turn fail closed | Fail closed at discovery | `tools: []` so natives never fire |
|
|
1931
|
+
| **Server tools** | SDK `customTools` in-process | HTTP MCP `agentsdk-tools` back to the host | Blocked at discovery | In-process (this is why they stay local) |
|
|
1932
|
+
| **Approvals** | Supported | Not supported | N/A | Supported |
|
|
1933
|
+
| **Instructions** | Host `AGENTS.md` (or inline if cwd is borrowed) | First-prompt preamble | `createSession({ instructions })` | Instructions + skills index; cwd is a fake `/repo` |
|
|
1934
|
+
| **Skills** | Materialized into `.cursor/skills/` | Synced to Agent Store | Warned: not on the box | Advertised in instructions, not harness files |
|
|
1935
|
+
| **Sandbox seeds** | Written into the session workspace | Ignored | Ignored | Unused (`tools: []`) |
|
|
1936
|
+
| **MCP** | Loopback / per-send auth | Bridged or forwarded; needs `--public-url` | Blocked | Used (sandbox stays off so MCP does not fail closed) |
|
|
1937
|
+
| **Subagents** | SDK `agents` | SDK `agents` | Blocked | Off (`tools: []` strands `task`) |
|
|
1938
|
+
| **`send({ runtime })`** | Host-local → attach or overlay; cloud → replace payload | Replace payload | Refused | Not used |
|
|
1939
|
+
|
|
1940
|
+
The string is checked in roughly fifteen Agent SDK sites. The
|
|
1941
|
+
concentrated ones:
|
|
1942
|
+
|
|
1943
|
+
- `discovery.ts` — compile-time capability errors/warnings per value.
|
|
1944
|
+
- `cloud-merge.ts` `resolveSessionRuntime` — per-session cloud attach
|
|
1945
|
+
forces `"cloud"`; grokbot throws.
|
|
1946
|
+
- `session-engine.ts` `buildTurnRequest` — one function assembles
|
|
1947
|
+
tools, customTools, MCP, instructions, skills sync, and the
|
|
1948
|
+
fail-closed hybrid guards.
|
|
1949
|
+
- `sdk-runner.ts` `openAgent` — `cloud:` block vs local `cwd` /
|
|
1950
|
+
`sandbox` / `tools`.
|
|
1951
|
+
- `runtime-dispatch-runner.ts` — grokbot vs everything else.
|
|
1952
|
+
|
|
1953
|
+
There is no plugin registry. `StartServerInternalOptions.runner`
|
|
1954
|
+
can replace the whole `AgentRunner`, which is too coarse to inject
|
|
1955
|
+
a filesystem.
|
|
1956
|
+
|
|
1957
|
+
---
|
|
1958
|
+
|
|
1959
|
+
## 3. Change-monitor is already a virtual workspace
|
|
1960
|
+
|
|
1961
|
+
Planner, executor, and curator all do the same thing
|
|
1962
|
+
(`factory/change-monitor/{planner,executor,curator}/agent/agent.ts`):
|
|
1963
|
+
|
|
1964
|
+
```ts
|
|
1965
|
+
export default defineAgent({
|
|
1966
|
+
runtime: "local",
|
|
1967
|
+
tools: [],
|
|
1968
|
+
local: {
|
|
1969
|
+
workspaceDir: "/repo", // does not need to exist
|
|
1970
|
+
sandbox: false, // sandbox makes MCP fail closed
|
|
1971
|
+
},
|
|
1972
|
+
});
|
|
1973
|
+
```
|
|
1974
|
+
|
|
1975
|
+
Then `src/vfs/tools.ts` reimplements `ls` / `glob` / `read` /
|
|
1976
|
+
`grep` as server tools whose descriptions and parameter schemas
|
|
1977
|
+
are copied **verbatim** from the native harness
|
|
1978
|
+
(`tools.contract.test.ts` pins the equality). Bodies call
|
|
1979
|
+
`mountVfsForSession` and today's consumer filesystem type
|
|
1980
|
+
(they named it `VFS`; Phase 3 replaces it with `FileSystem`):
|
|
1981
|
+
|
|
1982
|
+
```ts
|
|
1983
|
+
interface VFS {
|
|
1984
|
+
list(dir: string): Promise<VFSEntry[]>;
|
|
1985
|
+
glob(pattern: string, dir?: string): Promise<string[]>;
|
|
1986
|
+
read(path: string): Promise<string>;
|
|
1987
|
+
grep(query: GrepQuery): Promise<GrepResult>;
|
|
1988
|
+
search(query: SearchQuery): Promise<GrepResult>;
|
|
1989
|
+
write?(path: string, contents: string): Promise<void>;
|
|
1990
|
+
}
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
That type is **not** a july type. `search` (regex = `grep`,
|
|
1994
|
+
tags on issues, reserved keyword / semantic) stays theirs if
|
|
1995
|
+
they still want the authored tool. The framework cutover is
|
|
1996
|
+
`FileSystem` + native tools.
|
|
1997
|
+
|
|
1998
|
+
Mounts (`src/vfs/session.ts`) compose into one namespace: `/repo`
|
|
1999
|
+
(Origin at a pinned SHA), `/host` (`ctx.host.files`), plus plans /
|
|
2000
|
+
issues / checkpoints / skills. The model thinks it has a
|
|
2001
|
+
filesystem. The serve host has no checkout and no shell.
|
|
2002
|
+
|
|
2003
|
+
Two follow-on designs stay on this local-loop + virtual-workspace
|
|
2004
|
+
posture rather than flipping the string:
|
|
2005
|
+
|
|
2006
|
+
- **Tool scripting** (tools.md §8, not implemented): a `script`
|
|
2007
|
+
server tool that runs TypeScript against a capability-bounded
|
|
2008
|
+
tool bridge. Authored tool, still under
|
|
2009
|
+
`virtualRuntime(fs)`. Not a new loop.
|
|
2010
|
+
- **Computer use** (computer-use.md, not implemented): a
|
|
2011
|
+
writable `/workspace` mount (`cloudFs`) whose file verbs are
|
|
2012
|
+
commands on a lazily-provisioned anyrun microVM. Union it into
|
|
2013
|
+
the same `FileSystem`. Explicitly **not** `runtime: "cloud"`: cloud
|
|
2014
|
+
turns cannot restrict tools, cannot run server tools
|
|
2015
|
+
in-process, and cannot park approvals.
|
|
2016
|
+
|
|
2017
|
+
So "virtual" is one constructor with many filesystems. A third
|
|
2018
|
+
string value would repeat the original mistake.
|
|
2019
|
+
|
|
2020
|
+
---
|
|
2021
|
+
|
|
2022
|
+
## 4. Constraints the string hid
|
|
2023
|
+
|
|
2024
|
+
### 4.1 Cloud is not a different workspace on the same loop
|
|
2025
|
+
|
|
2026
|
+
A local turn: the serve host runs the agent loop, the backend
|
|
2027
|
+
runs inference, tools run against a workspace (host or injected).
|
|
2028
|
+
|
|
2029
|
+
A cloud turn: the SDK is a REST + SSE client (`CloudApiClient`).
|
|
2030
|
+
`createAgent` / `createRun` take a prompt. Tool execution happens
|
|
2031
|
+
on the VM; the SDK surfaces `tool_call` events. There is no
|
|
2032
|
+
in-process `read` or `shell` to swap. The public cloud surface has
|
|
2033
|
+
no `Exec` RPC (computer-use.md Gap 1). Driving the VM by
|
|
2034
|
+
prompting it is "execution by persuasion" — rejected as an exec
|
|
2035
|
+
backend.
|
|
2036
|
+
|
|
2037
|
+
`cloudRuntime` growing a `workspace` field would be either:
|
|
2038
|
+
|
|
2039
|
+
1. **Lies** — methods that do not run, because that arm owns
|
|
2040
|
+
the tree and the SDK cannot reach it, or
|
|
2041
|
+
2. **A new product** — a machine primitive (create / exec / read /
|
|
2042
|
+
hibernate, no model attached) that does not exist on the
|
|
2043
|
+
public API.
|
|
2044
|
+
|
|
2045
|
+
Until (2) exists, `cloudRuntime` is a **Runtime arm that
|
|
2046
|
+
owns its tree**. It has no `workspace` field.
|
|
2047
|
+
|
|
2048
|
+
### 4.2 Inference does not belong on `Runtime`
|
|
2049
|
+
|
|
2050
|
+
Local already separates inference from tools:
|
|
2051
|
+
`AgentConnectClient` streams the model, `resources`
|
|
2052
|
+
(`ResourceAccessor`) execute side effects
|
|
2053
|
+
(`packages/cursor-sdk/src/agent/local-executor.ts`). Cloud and
|
|
2054
|
+
Grok Bot couple them inside a remote service.
|
|
2055
|
+
|
|
2056
|
+
Putting `infer()` on `Runtime` would be true for local, false
|
|
2057
|
+
for cloud/grokbot, and unused by change-monitor. Keep inference
|
|
2058
|
+
off the workspace. Replay / eval fakes keep using
|
|
2059
|
+
`StartServerInternalOptions.runner`.
|
|
2060
|
+
|
|
2061
|
+
### 4.3 Delivery is a third axis
|
|
2062
|
+
|
|
2063
|
+
| Artifact | Local | Cloud | Grok Bot | Virtual |
|
|
2064
|
+
|---|---|---|---|---|
|
|
2065
|
+
| Instructions | `AGENTS.md` in cwd | First-prompt preamble | Session `instructions` | Authored instructions + optional `brief()` |
|
|
2066
|
+
| Skills | `.cursor/skills/` | Agent Store sync | Not on the box | Nothing unless the author mounts `skillsFs()` |
|
|
2067
|
+
| `execution: "agent"` scripts | Files in workspace | Prompt bodies | Prompt bodies | Banned without `shell` |
|
|
2068
|
+
| Sandbox seeds | Copied into cwd | Dropped | Dropped | Skip; put data in the `FileSystem` if the model should see it |
|
|
2069
|
+
| Workspace Path | Host cwd | VM | Box | `/` |
|
|
2070
|
+
|
|
2071
|
+
`virtualRuntime` does not materialize a scratch dir and does
|
|
2072
|
+
not fake `/repo`. Everything is lazy from the `FileSystem`.
|
|
2073
|
+
Empty `virtualRuntime()` is a workspace at `/` with no methods.
|
|
2074
|
+
|
|
2075
|
+
Skills are the sharp case. Authoring is one tree
|
|
2076
|
+
(`agent/skills/`). Discovery loads it the same way on every
|
|
2077
|
+
constructor. After that the pipes diverge. The model does
|
|
2078
|
+
not get the same catalog, the same load path, or the same
|
|
2079
|
+
ambient extras.
|
|
2080
|
+
|
|
2081
|
+
| | How the catalog is advertised | Where `SKILL.md` bytes live | How the model loads the body | Ambient extras the author did not write |
|
|
2082
|
+
|---|---|---|---|---|
|
|
2083
|
+
| **`localRuntime()`** | Harness native catalog from `.cursor/skills/<name>/SKILL.md` in the session cwd (`materializeWorkspace`, rewritten every turn) | Those files on the host workspace | Harness skill loader; "Using {name}" | Project / user (`~/.cursor/skills`) / team / plugin / Cursor-managed layers if the cwd is a real checkout |
|
|
2084
|
+
| **`cloudRuntime()`** | Native store discovery (`--agent-store-skills-dir`) after `syncSkillsToStore` | Hosted: deployment store `skills/<name>/`. Local serve + personal key: USER store `agent-serve/<agent>/skills/`. Re-copied on every first cloud turn (the mount is writable; a once-per-process copy would let one session edit the definition) | Harness skill loader on the VM | Skills already in the cloud repo. If the store is unreachable or sync fails, **only** those — the turn still runs |
|
|
2085
|
+
| **`grokbotRuntime()`** | Discovery **warning** only: the box does not see authored skills | Nowhere on the box | It cannot | Whatever the hosted Grok Bot box already has. `GET /v1/info` still lists the agent's skills |
|
|
2086
|
+
| **`virtualRuntime`** | None by default. If the author mounts `skillsFs()`, they advertise (instructions index, `brief`, or a later catalog-path override) | Only on that mount, e.g. `/agent/skills/…` | Workspace `read` of the mount path | None. No host `.cursor/skills/` materialization |
|
|
2087
|
+
|
|
2088
|
+
This is why delivery is derived from the **Runtime arm** and,
|
|
2089
|
+
on local, which workspace — not from `Workspace` alone:
|
|
2090
|
+
|
|
2091
|
+
- local + host workspace → materialize `.cursor/skills/` (today).
|
|
2092
|
+
- local + virtual workspace → no host workspace. Skills exist
|
|
2093
|
+
only if the author unions `skillsFs()` (or their own
|
|
2094
|
+
mount) and advertises it. `read` hits `/`.
|
|
2095
|
+
- cloud → store sync. No cwd to write.
|
|
2096
|
+
- grokbot → warn. Do not pretend the box has them.
|
|
2097
|
+
|
|
2098
|
+
Do not put `skills()` on `Workspace`. `skillsFs()` is a
|
|
2099
|
+
`FileSystem` the application may mount. The harness catalog
|
|
2100
|
+
is not pointed at a fake `/repo`.
|
|
2101
|
+
|
|
2102
|
+
### 4.4 Capability gates stay fail-closed
|
|
2103
|
+
|
|
2104
|
+
`tools` + cloud throws in the Cursor SDK
|
|
2105
|
+
(`assertNoCloudToolsRestriction`). The Agent SDK repeats the
|
|
2106
|
+
guard at discovery and again on hybrid turns
|
|
2107
|
+
(`session-engine.ts` ~2020). `advertiseTools` / per-session MCP
|
|
2108
|
+
auth are local-only for the same reason.
|
|
2109
|
+
|
|
2110
|
+
If a workspace cannot honor an allowlist, the turn is refused.
|
|
2111
|
+
Do not invent a "virtual cloud" that claims `tools: ["read"]`
|
|
2112
|
+
and then runs a VM with a full shell.
|
|
2113
|
+
|
|
2114
|
+
### 4.5 `send({ runtime })` overlays or attaches
|
|
2115
|
+
|
|
2116
|
+
Two jobs, one field:
|
|
2117
|
+
|
|
2118
|
+
```ts
|
|
2119
|
+
SendMessageOptions.runtime?: CloudRuntime | LocalRuntime;
|
|
2120
|
+
```
|
|
2121
|
+
|
|
2122
|
+
**Attach (hybrid).** Host-local agent, this session on a
|
|
2123
|
+
VM. Same continuation token, different turn backend.
|
|
2124
|
+
|
|
2125
|
+
```ts
|
|
2126
|
+
await send("fix the CI failure", {
|
|
2127
|
+
runtime: cloudRuntime({
|
|
2128
|
+
repos: [{ url, startingRef }],
|
|
2129
|
+
}),
|
|
2130
|
+
});
|
|
2131
|
+
```
|
|
2132
|
+
|
|
2133
|
+
**Overlay.** Host-local agent, this session's host options
|
|
2134
|
+
(PR worktree, etc.). Today's `send({ workspaceDir })`.
|
|
2135
|
+
|
|
2136
|
+
```ts
|
|
2137
|
+
await send("review this PR", {
|
|
2138
|
+
runtime: localRuntime({ workspaceDir: worktree }),
|
|
2139
|
+
});
|
|
2140
|
+
```
|
|
2141
|
+
|
|
2142
|
+
`virtualRuntime` is not a send argument (`virtualRuntime`
|
|
2143
|
+
returns `LocalRuntime`, but a new tree mid-session is
|
|
2144
|
+
refused). Types cannot tell host `localRuntime` from
|
|
2145
|
+
`virtualRuntime`. Virtual + `cloudRuntime` or
|
|
2146
|
+
`localRuntime({ workspaceDir })` fails at session start.
|
|
2147
|
+
|
|
2148
|
+
| Agent default | `send({ runtime })` | |
|
|
2149
|
+
|---|---|---|
|
|
2150
|
+
| host `localRuntime` | `cloudRuntime(opts)` | Attach. Today's hybrid. |
|
|
2151
|
+
| host `localRuntime` | `localRuntime(opts)` | Overlay cwd / sandbox / workspaceDir. |
|
|
2152
|
+
| `virtualRuntime` | `cloudRuntime` / `localRuntime({ workspaceDir })` | Type-ok, refuse at start. |
|
|
2153
|
+
| `cloudRuntime` | `cloudRuntime(opts)` | Replace session cloud payload. No merge. |
|
|
2154
|
+
| `cloudRuntime` | `localRuntime` | Refuse. |
|
|
2155
|
+
| grokbot | `cloudRuntime` / `localRuntime` | Refuse. Today's throw. |
|
|
2156
|
+
| anything | `grokbotRuntime` | Type error. |
|
|
2157
|
+
|
|
2158
|
+
Overlay uses today's cwd resolution: send overlay →
|
|
2159
|
+
agent `localRuntime` opts → `cwd/<sessionId>` → durable
|
|
2160
|
+
session dir. Cloud ignores a `workspaceDir` overlay.
|
|
2161
|
+
|
|
2162
|
+
Preferred `cloudRuntime(opts)` on send is the full session
|
|
2163
|
+
payload. It does not merge over `agent.cloud`. A cloud-default
|
|
2164
|
+
agent that wants today's per-session merge keeps the
|
|
2165
|
+
deprecated shim.
|
|
2166
|
+
|
|
2167
|
+
Deprecated shims: `send({ cloud: opts })` →
|
|
2168
|
+
`cloudRuntime(merge(agent.cloud, opts))`, then the table
|
|
2169
|
+
(so a cloud-default agent still merges; grokbot still
|
|
2170
|
+
refuses). `send({ workspaceDir })` →
|
|
2171
|
+
`localRuntime({ workspaceDir })` when the agent is
|
|
2172
|
+
host-local; ignored on cloud; refused on virtual / grokbot.
|
|
2173
|
+
`send({ runtime, cloud })` and
|
|
2174
|
+
`send({ runtime, workspaceDir })` fail closed.
|
|
2175
|
+
|
|
2176
|
+
This is a session override, not a subagent (`task` /
|
|
2177
|
+
`G12`). A process-wide singleton cannot express this.
|
|
2178
|
+
|
|
2179
|
+
### 4.6 Workspace is a product surface, not `ResourceAccessor`
|
|
2180
|
+
|
|
2181
|
+
The harness workspace is `ResourceAccessor`
|
|
2182
|
+
(`packages/agent-exec`): read, write/edit, grep, ls, glob (via
|
|
2183
|
+
grep), shell, shell-stdin, MCP, computer-use, and more.
|
|
2184
|
+
`LocalResourceProviderOptions` exposes overrides for **three**
|
|
2185
|
+
of ~fifteen tools. Grep, ls, and glob have no public override.
|
|
2186
|
+
|
|
2187
|
+
`FileSystem` is the public file-verb type: native tool names,
|
|
2188
|
+
native arg schemas (`NativeArgs<T>`). `Workspace` adds `shell`
|
|
2189
|
+
/ `root` / `brief`. It is not a drop-in for
|
|
2190
|
+
`ResourceAccessor`. Streaming stdin, MCP, and computer-use
|
|
2191
|
+
stay off the type.
|
|
2192
|
+
|
|
2193
|
+
### 4.7 The model must call native `read`
|
|
2194
|
+
|
|
2195
|
+
Models are trained on first-class `read` / `ls` / `grep` /
|
|
2196
|
+
`glob`. Change-monitor copies those texts into server tools
|
|
2197
|
+
because there is no supported way to keep the native tool
|
|
2198
|
+
*name* and swap the *implementation* (computer-use.md Gap 2).
|
|
2199
|
+
|
|
2200
|
+
That copy is not the real miss. Agent SDK server tools become
|
|
2201
|
+
Cursor `customTools`. Those are a synthetic MCP server
|
|
2202
|
+
(`custom-user-tools`). Local SDK defaults
|
|
2203
|
+
`mcpMetaToolEnabled` to true, so the model's tool list is
|
|
2204
|
+
`GetMcpTools` and `CallMcpTool`. After discovery the tool on
|
|
2205
|
+
that server is `read`. The call is MCP, not `ReadToolCall`.
|
|
2206
|
+
The stream event is `name: "mcp"`, not `name: "read"`.
|
|
2207
|
+
`custom-user-tools-read` is the internal wire id, not a
|
|
2208
|
+
top-level tool.
|
|
2209
|
+
|
|
2210
|
+
Cursor SDK executor overrides keep the native tool, the
|
|
2211
|
+
native schema, and the native `tool_call`. Only the
|
|
2212
|
+
implementation is swapped. Callers of
|
|
2213
|
+
`virtualRuntime(unionFs(…))` never see the binding. If
|
|
2214
|
+
Workspace only registered the same per-agent server tools
|
|
2215
|
+
change-monitor already ships, the model would still be on
|
|
2216
|
+
MCP. That is not a fix.
|
|
2217
|
+
|
|
2218
|
+
---
|
|
2219
|
+
|
|
2220
|
+
## 5. Existing seams (use them; do not invent a fourth)
|
|
2221
|
+
|
|
2222
|
+
```
|
|
2223
|
+
Agent SDK defineAgent({ runtime }) product selector
|
|
2224
|
+
↓
|
|
2225
|
+
Cursor SDK AgentOptions { local?, cloud? } dispatch
|
|
2226
|
+
↓
|
|
2227
|
+
local-runtime WorkspaceRuntime / SessionRuntime session factory
|
|
2228
|
+
↓
|
|
2229
|
+
local-exec LocalResourceProvider workspace
|
|
2230
|
+
↓
|
|
2231
|
+
agent-exec ResourceAccessor / ExecResource verb registry
|
|
2232
|
+
```
|
|
2233
|
+
|
|
2234
|
+
Change-monitor sits *above* all of this, as
|
|
2235
|
+
`execution: "server"` tools, because the bottom two layers are
|
|
2236
|
+
not plumbed to `AgentOptions`.
|
|
2237
|
+
|
|
2238
|
+
| Seam | What it is | Why it is not enough today |
|
|
2239
|
+
|---|---|---|
|
|
2240
|
+
| `AgentRunner` | Internal `startServer` turn replacement (`runTurn` / `prewarm`) | Replaces the whole turn; no workspace. Not a `Runtime` arm. |
|
|
2241
|
+
| `RuntimeDispatchingRunner` | grokbot vs SDK | Hard-coded third case |
|
|
2242
|
+
| `AgentOptions.local` / `.cloud` | Parallel config blocks | Presence of `cloud` *is* the runtime until later `Agent.create({ runtime })` |
|
|
2243
|
+
| `LocalResourceProvider` overrides | Swap read/write/shell | Private; 3 of ~15 tools; not on `AgentOptions` |
|
|
2244
|
+
| `ResourceAccessor` | The agent loop's workspace | Correct internal interface; not a public SDK type |
|
|
2245
|
+
| Change-monitor filesystem | Product FS | Consumer-side; duplicated native schemas |
|
|
2246
|
+
| `ctx.host.files` | Durable KV, file-shaped | Explicitly not a filesystem; natives cannot see the hosted sink |
|
|
2247
|
+
|
|
2248
|
+
The first code PR promotes a public `Workspace` on
|
|
2249
|
+
`AgentOptions.local`, bound as executor overrides. Agent SDK
|
|
2250
|
+
then passes that workspace through `virtualRuntime`. Do not add
|
|
2251
|
+
`runtime: "virtual"`. Do not monkey-patch
|
|
2252
|
+
`sessionRuntime.resources` from `@cursor/july`.
|
|
2253
|
+
|
|
2254
|
+
---
|
|
2255
|
+
|
|
2256
|
+
## 6. Gaps and risks
|
|
2257
|
+
|
|
2258
|
+
**G1. Cloud cannot honor an injected workspace.**
|
|
2259
|
+
Public `CloudApiClient` is prompt-only. Exec-daemon
|
|
2260
|
+
`ControlService.Exec` exists but needs per-pod credentials a
|
|
2261
|
+
public caller cannot mint (computer-use.md Gap 1). Treat cloud
|
|
2262
|
+
as an opaque loop. Do not ship Workspace methods on
|
|
2263
|
+
`cloudRuntime`.
|
|
2264
|
+
|
|
2265
|
+
**G2. Builtin redirection is not a public SDK feature.**
|
|
2266
|
+
Overrides exist on `LocalResourceProviderOptions` and are not
|
|
2267
|
+
passed through `createDefaultLocalWorkspaceRuntime` or
|
|
2268
|
+
`AgentOptions` (computer-use.md Gap 2). Grep/ls/glob have no
|
|
2269
|
+
override. This is the first code PR: add the missing
|
|
2270
|
+
overrides, put `Workspace` on `AgentOptions.local`, and derive
|
|
2271
|
+
the tools allowlist from method presence. The authoring API
|
|
2272
|
+
in `@cursor/july` waits on that seam. Do not ship
|
|
2273
|
+
`virtualRuntime` on lookalike server tools first.
|
|
2274
|
+
|
|
2275
|
+
**G3. Native tool schemas vs consumer extras.**
|
|
2276
|
+
A redirected `read` must keep the native schema. Verbs that
|
|
2277
|
+
are not Cursor tools (`diff`, change-monitor `search`, CAS
|
|
2278
|
+
`write`) stay authored tools. Do not widen native schemas to
|
|
2279
|
+
carry mount-table prose; that belongs in `brief()` /
|
|
2280
|
+
instructions.md (tools.md §6). Do not add `search` to
|
|
2281
|
+
`FileSystem` for tags or keyword.
|
|
2282
|
+
|
|
2283
|
+
**G4. Hybrid + virtual is easy to get wrong.**
|
|
2284
|
+
A planner with a virtual workspace that also
|
|
2285
|
+
`send({ runtime: cloudRuntime(...) })` would run
|
|
2286
|
+
unrestricted on the VM. Refuse the attach, or require an
|
|
2287
|
+
explicit second agent as the delegation target
|
|
2288
|
+
(tools.md §3). `send({ runtime })` is not an open override.
|
|
2289
|
+
|
|
2290
|
+
**G5. Grokbot is a third Runtime arm, not a workspace.**
|
|
2291
|
+
It has no MCP, no server tools, no model override, no v2
|
|
2292
|
+
architecture. Keep `grokbotRuntime()` as an arm with no
|
|
2293
|
+
`workspace` field.
|
|
2294
|
+
|
|
2295
|
+
**G6. Delivery / `Workspace Path`.**
|
|
2296
|
+
`virtualRuntime` prints `/`. No scratch dir, no
|
|
2297
|
+
`workspaceDir: "/repo"`. A `brief()` that names mounts the
|
|
2298
|
+
`FileSystem` does not have is two maps; `root()` wins.
|
|
2299
|
+
|
|
2300
|
+
**G7. Security: workspace isolation ≠ host isolation.**
|
|
2301
|
+
Injecting a `FileSystem` does not sandbox the serve host. Server tools
|
|
2302
|
+
still run in-process with host credentials. Host `shell` is
|
|
2303
|
+
still host bash. Document the blast radius on each
|
|
2304
|
+
implementation. Prompt-injected writes hit `/host` (or a
|
|
2305
|
+
disposable VM), never the serve host's disk or secrets.
|
|
2306
|
+
|
|
2307
|
+
**G8. `script` is not host `shell`.**
|
|
2308
|
+
A TypeScript isolate that can only call bound Workspace methods
|
|
2309
|
+
is a different trust class from `/bin/bash`. Do not overload
|
|
2310
|
+
one `shell` method for both until two in-tree implementations
|
|
2311
|
+
need a shared `shell` with a `kind`.
|
|
2312
|
+
|
|
2313
|
+
**G9. Two public surfaces.**
|
|
2314
|
+
`@cursor/sdk` owns `FileSystem` / `Workspace` (native schemas).
|
|
2315
|
+
`@cursor/july` owns `virtualRuntime` and helpers that return
|
|
2316
|
+
`FileSystem`. Phase 1 does not put constructors on
|
|
2317
|
+
`Agent.create`. A later SDK deprecation uses the same
|
|
2318
|
+
`Runtime` types. A july-only filesystem cannot re-point
|
|
2319
|
+
natives without the SDK seam. Do not ship the authoring API
|
|
2320
|
+
against lookalikes and flip the binding later.
|
|
2321
|
+
|
|
2322
|
+
**G10. Test and eval story.**
|
|
2323
|
+
A virtual workspace must work in `run` / `eval` without Origin.
|
|
2324
|
+
Change-monitor already has in-memory filesystem fakes; those become
|
|
2325
|
+
`virtualRuntime(memoryFs(…))` in tests.
|
|
2326
|
+
|
|
2327
|
+
**G11. Versioning and published docs.**
|
|
2328
|
+
String `runtime`, sibling `local` / `cloud`, and
|
|
2329
|
+
`send({ cloud })` / `send({ workspaceDir })` are a shipped
|
|
2330
|
+
user contract. Replacement is additive:
|
|
2331
|
+
`localRuntime(opts)` / `cloudRuntime(opts)` plus `@deprecated`
|
|
2332
|
+
on the old selector, sibling bags, and send shims. Do not
|
|
2333
|
+
break existing `defineAgent({ runtime: "cloud", cloud })` or
|
|
2334
|
+
`defineAgent({ local: { workspaceDir } })`. Do not document
|
|
2335
|
+
constructors until they ship (Phase 4 / §9).
|
|
2336
|
+
|
|
2337
|
+
**G12. Subagents keep today's behavior, stored as `Runtime`.**
|
|
2338
|
+
Discovery still loads a child as a Cursor SDK custom
|
|
2339
|
+
subagent (`task`: prompt + description + model). Authored
|
|
2340
|
+
`runtime`, `tools/`, skills, MCP under `agent/subagents/`
|
|
2341
|
+
are warned and ignored. The child gets the harness default
|
|
2342
|
+
toolset on the parent's workspace. Empty july `tools: []`
|
|
2343
|
+
does not mean the child is empty-tooled — those slots are
|
|
2344
|
+
inert.
|
|
2345
|
+
|
|
2346
|
+
Do not change that product. Internally, stop storing
|
|
2347
|
+
`runtime: "local"` + `tools: []` as the child's july
|
|
2348
|
+
config. Express the same forced host-local inherit as
|
|
2349
|
+
`localRuntime()` whose workspace is the parent's (host
|
|
2350
|
+
`localWorkspace` or the virtual tree). Authors do not get
|
|
2351
|
+
a `Runtime` field on subagents in this design.
|
|
2352
|
+
|
|
2353
|
+
A child with its own cwd, own cloud VM, or real july tools
|
|
2354
|
+
is a later product. If that product ever honors a child
|
|
2355
|
+
`Runtime`, default to inheriting the parent workspace so a
|
|
2356
|
+
virtual parent does not spawn host `read`.
|
|
2357
|
+
|
|
2358
|
+
**G13. Independently implemented `read` + `shell`.**
|
|
2359
|
+
Flattening onto Workspace removes the attach-a-different-shell
|
|
2360
|
+
footgun; it does not prove the backends share a namespace.
|
|
2361
|
+
Prefer `virtualRuntime(unionFs(…))` / `localRuntime()`.
|
|
2362
|
+
`virtualWorkspace` stays private.
|
|
2363
|
+
|
|
2364
|
+
---
|
|
2365
|
+
|
|
2366
|
+
## 7. Why Cursor SDK first
|
|
2367
|
+
|
|
2368
|
+
A july-only cut can generate server tools from Workspace
|
|
2369
|
+
methods and pin one schema pack. That is change-monitor
|
|
2370
|
+
today, moved into the framework. The model would still call
|
|
2371
|
+
`CallMcpTool` on `custom-user-tools` / `read`.
|
|
2372
|
+
`tools: ["read"]` would still enable host `read`, not virtual
|
|
2373
|
+
`read`. Stream consumers that key on `name === "read"`
|
|
2374
|
+
would still miss the calls.
|
|
2375
|
+
|
|
2376
|
+
That is the problem this work exists to fix. Do not ship
|
|
2377
|
+
`virtualRuntime` on lookalikes and flip the binding later.
|
|
2378
|
+
The first code PR opens `AgentOptions.local.workspace` and
|
|
2379
|
+
rebounds the native executors.
|
|
2380
|
+
|
|
2381
|
+
---
|
|
2382
|
+
|
|
2383
|
+
## 8. Plan
|
|
2384
|
+
|
|
2385
|
+
Each phase is its own PR. Today's `runtime` string keeps
|
|
2386
|
+
working until a later deprecation window. Do not start Phase
|
|
2387
|
+
1 until this document (Phase 0) has been reviewed.
|
|
2388
|
+
|
|
2389
|
+
### Phase 0 — this document (this PR)
|
|
2390
|
+
|
|
2391
|
+
Publish the design. No product code.
|
|
2392
|
+
|
|
2393
|
+
Reviewers: Agent SDK and cursor-sdk maintainers; change-monitor
|
|
2394
|
+
owners as the first consumer.
|
|
2395
|
+
|
|
2396
|
+
Agree before any code PR:
|
|
2397
|
+
|
|
2398
|
+
- The four goals at the top (compat — old fields deprecated
|
|
2399
|
+
but kept, `localRuntime` default, fail-closed pairs,
|
|
2400
|
+
`Workspace` instead of a longer enum)
|
|
2401
|
+
- `Runtime` is a closed union; `workspace` only on the local
|
|
2402
|
+
arm
|
|
2403
|
+
- **Runtime arm** vs **workspace** vs **delivery**
|
|
2404
|
+
- Workspace is one object, optional methods, one namespace
|
|
2405
|
+
- Cloud/grokbot do not grow a `workspace` field until a
|
|
2406
|
+
machine API exists
|
|
2407
|
+
- Native redirection in Cursor SDK is the first code cut
|
|
2408
|
+
- Change-monitor is the first workspace, in a later PR
|
|
2409
|
+
|
|
2410
|
+
**Review asks** (comment on these):
|
|
2411
|
+
|
|
2412
|
+
1. Phase 1 adds `Workspace` on **Cursor SDK**
|
|
2413
|
+
`Agent.create({ local: { workspace } })`
|
|
2414
|
+
(`LocalAgentOptions`, next to `cwd` / `dirs`). That is
|
|
2415
|
+
not `defineAgent({ local })`. july constructors stay in
|
|
2416
|
+
Phase 2. Same-shape `Agent.create({ runtime })` is a
|
|
2417
|
+
later SDK deprecation, not Phase 1. Agree?
|
|
2418
|
+
2. Method presence as the file/shell allowlist: fail-closed
|
|
2419
|
+
when `tools` names a missing method?
|
|
2420
|
+
3. `virtualRuntime(fs?)` only; no public
|
|
2421
|
+
`virtualRuntime(workspace)`? Empty default, `root()` is `/`.
|
|
2422
|
+
No public `search` method. `FileSystem` is the file-verb
|
|
2423
|
+
type. Agree?
|
|
2424
|
+
4. Session factory runs once per session, always async
|
|
2425
|
+
(`Promise<FileSystem>`), with `SessionWorkspaceContext`
|
|
2426
|
+
(`sessionId`, `agentName?`, `continuationKey?`, `host?`).
|
|
2427
|
+
Per-wake data stays inside the `FileSystem`. Agree?
|
|
2428
|
+
5. Rejecting host executors for omitted methods when a
|
|
2429
|
+
workspace is set (defense in depth), or tools-header only?
|
|
2430
|
+
6. Harness builtins (`webSearch`, `updateTodos`, …) stay off
|
|
2431
|
+
Workspace and off `virtualRuntime`'s default. Opt in via
|
|
2432
|
+
`tools`. Cloud still refuses `tools`. Agree?
|
|
2433
|
+
7. `edit` / `delete` / `semSearch` / `readLints` on Workspace
|
|
2434
|
+
now (optional). Phase 1 only fail-closed-omits host
|
|
2435
|
+
executors. Virtual bodies wait for a consumer. Agree?
|
|
2436
|
+
8. `Runtime` is a closed union. `workspace` only on
|
|
2437
|
+
`{ kind: "local" }`. Agree?
|
|
2438
|
+
9. String `runtime` + sibling `local` / `cloud` stay
|
|
2439
|
+
supported and are marked deprecated. Host options move
|
|
2440
|
+
to `localRuntime(opts)` (`AgentLocalOptions`). Published
|
|
2441
|
+
docs wait for Phase 4 (§9). Agree?
|
|
2442
|
+
10. `send({ runtime?: CloudRuntime | LocalRuntime })`.
|
|
2443
|
+
Host-local + `cloudRuntime` = attach. Host-local +
|
|
2444
|
+
`localRuntime(opts)` = overlay. Cloud + `cloudRuntime`
|
|
2445
|
+
= replace payload (no merge). Grokbot + cloud refused.
|
|
2446
|
+
`send({ cloud })` / `send({ workspaceDir })` deprecated
|
|
2447
|
+
shims (`send({ cloud })` still merges).
|
|
2448
|
+
`send({ runtime, cloud })` fail-closed. Agree?
|
|
2449
|
+
|
|
2450
|
+
**Out of this PR:** types, executors, discovery, change-monitor
|
|
2451
|
+
code, published user docs.
|
|
2452
|
+
|
|
2453
|
+
### Phase 1 — Cursor SDK: inject a workspace
|
|
2454
|
+
|
|
2455
|
+
Packages: `cursor-sdk`, `cursor-sdk-local-runtime`,
|
|
2456
|
+
`local-exec`. No `@cursor/july` authoring change.
|
|
2457
|
+
|
|
2458
|
+
`AgentOptions` in this phase is `@cursor/sdk`'s
|
|
2459
|
+
`Agent.create` bag (`packages/cursor-sdk/src/agent/options.ts`).
|
|
2460
|
+
It is not `defineAgent`'s `AgentLocalOptions`. Agent SDK
|
|
2461
|
+
does not grow a `local.workspace` field here. Phase 2's
|
|
2462
|
+
`virtualRuntime` writes the `FileSystem` into this SDK
|
|
2463
|
+
field when july builds `Agent.create({ local })`.
|
|
2464
|
+
|
|
2465
|
+
- Add `FileSystem` / `Workspace` on Cursor SDK
|
|
2466
|
+
`LocalAgentOptions.workspace` (next to `cwd` / `dirs`).
|
|
2467
|
+
Method args are `NativeArgs<T>` matching each tool schema.
|
|
2468
|
+
- Derive `tools` from method presence when the caller omitted
|
|
2469
|
+
`tools`. Intersect fail-closed when they passed one.
|
|
2470
|
+
- Add `overrideGrepExecutor` / `overrideLsExecutor` /
|
|
2471
|
+
`overrideDeleteExecutor` on `LocalResourceProvider`. Glob
|
|
2472
|
+
rides the grep executor. `edit` rides the write executor.
|
|
2473
|
+
`readLints` rides `diagnosticsExecutor`. `semSearch` has
|
|
2474
|
+
no local-exec override today; when a workspace is set and
|
|
2475
|
+
`semSearch` is omitted, do not register the host index
|
|
2476
|
+
tool.
|
|
2477
|
+
- Plumb overrides through
|
|
2478
|
+
`createDefaultLocalWorkspaceRuntime` → the resource
|
|
2479
|
+
provider factory → `createLocalExecutor`.
|
|
2480
|
+
- When a workspace is set, bind present methods and reject
|
|
2481
|
+
omitted path-namespace natives (do not leave host `read` /
|
|
2482
|
+
`edit` / `delete` / `semSearch` / `readLints` / `shell`
|
|
2483
|
+
registered behind a virtual tree). Virtual implementations
|
|
2484
|
+
of `edit` / `delete` / `semSearch` / `readLints` are not
|
|
2485
|
+
required in this PR.
|
|
2486
|
+
- Cloud + `local.workspace` throws, same posture as
|
|
2487
|
+
`tools` + cloud.
|
|
2488
|
+
- Unit tests: a map-backed workspace serves native `read` /
|
|
2489
|
+
`ls` / `grep` / `glob` through the executor adapters. No
|
|
2490
|
+
model turn.
|
|
2491
|
+
|
|
2492
|
+
**Not in this PR:** `defineAgent({ runtime })` object form,
|
|
2493
|
+
constructors on `Agent.create`, `virtualRuntime`,
|
|
2494
|
+
`memoryFs`, change-monitor, published docs. Presence of
|
|
2495
|
+
`cloud` still dispatches.
|
|
2496
|
+
|
|
2497
|
+
Acceptance:
|
|
2498
|
+
|
|
2499
|
+
- `Agent.create({ local: { workspace }, tools })` rebinds
|
|
2500
|
+
natives; a missing method is absent from the toolset and
|
|
2501
|
+
errors if invoked.
|
|
2502
|
+
- Existing local agents (no `workspace`) are unchanged.
|
|
2503
|
+
- `workspace` + `cloud` is a `ConfigurationError`.
|
|
2504
|
+
|
|
2505
|
+
### Phase 2 — Agent SDK: `runtime` accepts a `Runtime` object
|
|
2506
|
+
|
|
2507
|
+
Packages: `@cursor/july` (`packages/agent-serve`). Depends on
|
|
2508
|
+
Phase 1.
|
|
2509
|
+
|
|
2510
|
+
```ts
|
|
2511
|
+
runtime?: AgentRuntime | Runtime;
|
|
2512
|
+
local?: AgentLocalOptions; // still supported
|
|
2513
|
+
cloud?: AgentCloudOptions; // still supported
|
|
2514
|
+
```
|
|
2515
|
+
|
|
2516
|
+
`"local"` / omitted → `localRuntime(agent.local)` (`{ kind:
|
|
2517
|
+
"local", workspace: localWorkspace(opts) }`; today's
|
|
2518
|
+
default). `"cloud"` → `cloudRuntime(agent.cloud)`.
|
|
2519
|
+
`"grokbot"` → `grokbotRuntime()`. Those constructors return
|
|
2520
|
+
the `Runtime` union in §1. JSDoc marks the string and
|
|
2521
|
+
sibling `local` / `cloud` `@deprecated` in favor of
|
|
2522
|
+
`localRuntime(opts)` / `cloudRuntime(opts)`. The fields
|
|
2523
|
+
stay.
|
|
2524
|
+
|
|
2525
|
+
`virtualRuntime(fs?)` lifts a `FileSystem` (or an async
|
|
2526
|
+
per-session factory) to `AgentOptions.local.workspace`. Empty
|
|
2527
|
+
`virtualRuntime()` is `/` and no file methods. Omit `tools`
|
|
2528
|
+
so workspace methods are the file/shell allowlist; do not
|
|
2529
|
+
keep `tools: []`. Discovery validates the composition rules
|
|
2530
|
+
in §1. `brief(ctx)` folds into the prompt. `<user_info>`
|
|
2531
|
+
Workspace Path is `/`. No `materializeWorkspace`.
|
|
2532
|
+
`local.workspaceDir` + virtual fails closed.
|
|
2533
|
+
`SendMessageOptions.runtime?: CloudRuntime | LocalRuntime`.
|
|
2534
|
+
Host-local + `cloudRuntime` attaches. Host-local +
|
|
2535
|
+
`localRuntime(opts)` overlays cwd / sandbox / workspaceDir.
|
|
2536
|
+
Virtual + either fails closed. Deprecated `send({ cloud })`
|
|
2537
|
+
still merges over `agent.cloud`. Deprecated
|
|
2538
|
+
`send({ workspaceDir })` still overlays a host-local agent.
|
|
2539
|
+
|
|
2540
|
+
`memoryFs` and `skillsFs` ship here as `FileSystem`
|
|
2541
|
+
helpers. The latter is opt-in.
|
|
2542
|
+
|
|
2543
|
+
**Not in this PR:** change-monitor cutover, `unionFs` unless
|
|
2544
|
+
a test needs it, published user-guide rewrite.
|
|
2545
|
+
|
|
2546
|
+
Acceptance:
|
|
2547
|
+
|
|
2548
|
+
- Existing string agents unchanged.
|
|
2549
|
+
- A fixture agent with
|
|
2550
|
+
`virtualRuntime(memoryFs({ "/a.txt": "hi" }))` can
|
|
2551
|
+
`agent-sdk call read` and get `hi` from the native `read`
|
|
2552
|
+
tool.
|
|
2553
|
+
- `virtualRuntime(...)` + `send({ runtime: cloudRuntime(...) })`
|
|
2554
|
+
still fails closed.
|
|
2555
|
+
- `send({ cloud })` still attaches a local agent to cloud
|
|
2556
|
+
(merge). On a cloud-default agent it still merges.
|
|
2557
|
+
`send({ runtime: cloudRuntime(opts) })` replaces, no merge.
|
|
2558
|
+
- Grokbot + `send({ cloud })` / `send({ runtime: cloudRuntime })`
|
|
2559
|
+
still refuses.
|
|
2560
|
+
- `send({ runtime: localRuntime({ workspaceDir }) })` overlays
|
|
2561
|
+
a host-local agent. `send({ workspaceDir })` still works.
|
|
2562
|
+
- `send({ runtime, cloud })` fails closed.
|
|
2563
|
+
- Discovery matrix: object-form runtimes produce the same
|
|
2564
|
+
fail-closed errors as today's string (tools + cloud,
|
|
2565
|
+
advertiseTools + cloud, grokbot + server tools).
|
|
2566
|
+
|
|
2567
|
+
### Phase 3 — change-monitor adopts `virtualRuntime`
|
|
2568
|
+
|
|
2569
|
+
Depends on Phase 2. Session binding is the hard part:
|
|
2570
|
+
planner/executor filesystem is per-session (PR, SHA, which mounts
|
|
2571
|
+
exist), and `defineAgent` runs before that is known.
|
|
2572
|
+
|
|
2573
|
+
Replace `tools: []` + copied `src/vfs/tools.ts` schemas for
|
|
2574
|
+
`ls` / `glob` / `read` / `grep` with
|
|
2575
|
+
`virtualRuntime(async (ctx) => unionFs({ … }))`. Mount
|
|
2576
|
+
`skillsFs()` if they still want `/agent/skills`. Writable
|
|
2577
|
+
mounts implement native `write` / `edit` and omit
|
|
2578
|
+
`delete` / `semSearch` / `readLints` unless a mount earns
|
|
2579
|
+
them. Keep authored tools that have no FileSystem method
|
|
2580
|
+
(`diff`, CAS extras, plugin telemetry, tag `search` if they
|
|
2581
|
+
still want it). Drop `workspaceDir: "/repo"`. `root()` is
|
|
2582
|
+
`/`.
|
|
2583
|
+
|
|
2584
|
+
Acceptance:
|
|
2585
|
+
|
|
2586
|
+
- Change-monitor does not ship a second copy of native
|
|
2587
|
+
schemas.
|
|
2588
|
+
- Planner/executor still have no host shell and no checkout.
|
|
2589
|
+
- Native `read` / `ls` / `grep` / `glob` hit the mounted `FileSystem`.
|
|
2590
|
+
|
|
2591
|
+
### Phase 4 — docs and templates
|
|
2592
|
+
|
|
2593
|
+
Ship the published-docs plan in §9. Constructors become the
|
|
2594
|
+
authoring vocabulary. The string and sibling `local` /
|
|
2595
|
+
`cloud` stay in the pages as deprecated-but-supported.
|
|
2596
|
+
Do this after Phase 2 is real. Do not document constructors
|
|
2597
|
+
that have not shipped.
|
|
2598
|
+
|
|
2599
|
+
### After Phase 4 — not a "v2" of this type
|
|
2600
|
+
|
|
2601
|
+
This design is the end state of the authoring type.
|
|
2602
|
+
`Workspace` already lists every path-namespace verb. Phases
|
|
2603
|
+
1–4 ship that type and the first workspaces. There is no
|
|
2604
|
+
second abstraction waiting behind "v1."
|
|
2605
|
+
|
|
2606
|
+
What remains is implementation of methods a given workspace
|
|
2607
|
+
omits, or **other products** that plug into the same
|
|
2608
|
+
constructors.
|
|
2609
|
+
|
|
2610
|
+
**Already in this type; a workspace implements when it can**
|
|
2611
|
+
|
|
2612
|
+
| Gap | Who fills it | Not |
|
|
2613
|
+
|---|---|---|
|
|
2614
|
+
| Virtual `edit` / `write` | Change-monitor writable mounts (Phase 3) | A new Workspace field |
|
|
2615
|
+
| Virtual `delete` | A mount that can delete | Host `delete` behind a virtual tree |
|
|
2616
|
+
| Virtual `semSearch` | A `FileSystem` that owns an index of `root()` | Host index of the serve disk |
|
|
2617
|
+
| Virtual `readLints` | A mount that owns diagnostics | Host LSP against a fake cwd |
|
|
2618
|
+
| `unionFs` as a public helper | Phase 3 if the factory needs it; otherwise when a second consumer unions mounts | A new runtime |
|
|
2619
|
+
|
|
2620
|
+
**Other designs, same constructors**
|
|
2621
|
+
|
|
2622
|
+
| Work | Home | How it uses this type |
|
|
2623
|
+
|---|---|---|
|
|
2624
|
+
| anyrun `/workspace` (`cloudFs`) | factory/change-monitor `computer-use.md` | A shell-backed mount inside `virtualRuntime(unionFs(…))`. Not `cloudRuntime`. |
|
|
2625
|
+
| TypeScript `script` isolate | factory/change-monitor `tools.md` §8 | Authored server tool. Not `Workspace.shell`. |
|
|
2626
|
+
| Cloud `tools` allowlist | Cursor cloud agent API | Then `defineAgent({ tools })` means the same on `cloudRuntime`. Fail closed until that API exists. |
|
|
2627
|
+
| Machine without a model (create / exec / read / hibernate) | New backend surface | The only way `cloudRuntime` can accept a `workspace` for inspection. Do not prompt the VM. |
|
|
2628
|
+
| Child with its own `Runtime` | Later product | Today's inherit stays (`G12`). Internally already `localRuntime()` on the parent workspace. No authoring `runtime` on `agent/subagents/` in this design. |
|
|
2629
|
+
| `Agent.create({ runtime })` | `@cursor/sdk` after Phase 4 | Same constructors as july. Sibling `local` / `cloud` deprecated as the dispatcher. Not Phase 1. |
|
|
2630
|
+
| Deprecate `runtime: "local" \| "cloud"` | After Phase 4 has shipped | Additive constructors first. |
|
|
2631
|
+
|
|
2632
|
+
**Not a follow-on. Do not build.**
|
|
2633
|
+
|
|
2634
|
+
- `{ kind: "custom"; runner: AgentRunner }` on `Runtime`
|
|
2635
|
+
- A `Harness` type / `Runtime.webSearch()`
|
|
2636
|
+
- `infer()` on `Runtime`
|
|
2637
|
+
- `virtualRuntime(workspace: Workspace)`
|
|
2638
|
+
- `cloudRuntime` Workspace methods that prompt the VM
|
|
2639
|
+
- Lookalike server tools as a stepping stone
|
|
2640
|
+
|
|
2641
|
+
---
|
|
2642
|
+
|
|
2643
|
+
## 9. Documentation
|
|
2644
|
+
|
|
2645
|
+
Phase 4 work. Published Agent SDK docs live under
|
|
2646
|
+
`packages/agent-serve/docs/` (VitePress). They never say
|
|
2647
|
+
`agent-serve`. Product name is Agent SDK; package is
|
|
2648
|
+
`@cursor/july`; CLI is `agent-sdk`. This design file stays
|
|
2649
|
+
internal (`srcExclude: ["design/**"]`).
|
|
2650
|
+
|
|
2651
|
+
Do not rewrite user docs in Phases 0–3. Constructors that
|
|
2652
|
+
have not shipped must not appear on cursor.com / the
|
|
2653
|
+
VitePress site.
|
|
2654
|
+
|
|
2655
|
+
### How we talk about runtime
|
|
2656
|
+
|
|
2657
|
+
Lead with what the reader is choosing: where the turn runs,
|
|
2658
|
+
and (for local) which files the model sees. Constructors
|
|
2659
|
+
are the vocabulary.
|
|
2660
|
+
|
|
2661
|
+
| Say | Do not say |
|
|
2662
|
+
|---|---|
|
|
2663
|
+
| `localRuntime()` runs turns on this machine | "the local loop" / `kind: "local"` |
|
|
2664
|
+
| `sandboxRuntime()` runs those turns inside Cursor's local sandbox | `localRuntime({ sandbox: true })` as the preferred form |
|
|
2665
|
+
| `cloudRuntime({ repos })` runs turns on a Cursor cloud agent | "cloud is the other filesystem" / `runtime: "cloud"` as the only form |
|
|
2666
|
+
| `virtualRuntime(fs)` runs locally against a filesystem you pass | `runtime: "virtual"` / `Workspace` / `Computer` |
|
|
2667
|
+
| String `runtime` and sibling `local` / `cloud` still work | That they are gone, or that they are the preferred form |
|
|
2668
|
+
|
|
2669
|
+
Omit internals: `Runtime` union arms, `kind`, `FileSystem` vs
|
|
2670
|
+
`Workspace` lift, `AgentRunner`, `virtualWorkspace`,
|
|
2671
|
+
`AgentOptions.local.workspace`, MCP lookalikes, executor
|
|
2672
|
+
overrides. Those belong in this design file and in JSDoc for
|
|
2673
|
+
SDK maintainers, not in the user guide.
|
|
2674
|
+
|
|
2675
|
+
`virtualRuntime` is a real user feature (tests, hosted
|
|
2676
|
+
trees, change-monitor-style agents). Document it as "pass
|
|
2677
|
+
the files the agent may see." Show `memoryFs` for tests.
|
|
2678
|
+
Show a mount table only when `unionFs` has shipped. Do not
|
|
2679
|
+
teach readers to implement `FileSystem` from scratch on the
|
|
2680
|
+
first page.
|
|
2681
|
+
|
|
2682
|
+
Deprecated fields get one short note and a link to the
|
|
2683
|
+
constructor, not a second tutorial:
|
|
2684
|
+
|
|
2685
|
+
> `runtime: "cloud"` is still accepted. Prefer
|
|
2686
|
+
> `cloudRuntime({ repos })`.
|
|
2687
|
+
|
|
2688
|
+
Document host options on `localRuntime({ cwd, workspaceDir,
|
|
2689
|
+
sandbox })`, next to `cloudRuntime({ repos })`. Sibling
|
|
2690
|
+
`local` / `cloud` get a deprecated note and a link.
|
|
2691
|
+
|
|
2692
|
+
### Pages to update (Phase 4)
|
|
2693
|
+
|
|
2694
|
+
**Own the contract (rewrite the runtime sections)**
|
|
2695
|
+
|
|
2696
|
+
| Page | Change |
|
|
2697
|
+
|---|---|
|
|
2698
|
+
| [reference/agent-config.md](/docs/reference/agent-config.md) | `runtime` type becomes constructors **or** the deprecated string. Table + "Choose a runtime" show `localRuntime(opts)` / `cloudRuntime(opts)` / `virtualRuntime(fs)` first. Move cwd / sandbox / workspaceDir onto the `localRuntime` section. Mark sibling `local` / `cloud` and string `runtime` deprecated. |
|
|
2699
|
+
| [guides/cloud-runtime.md](/docs/guides/cloud-runtime.md) | Open with `cloudRuntime({ repos })`. Keep `runtime: "cloud"` as the still-supported form. Cloud is a turn host, not a filesystem. Hybrid is `send({ runtime: cloudRuntime(opts) })`; local overlay is `send({ runtime: localRuntime(opts) })`. `send({ cloud })` / `send({ workspaceDir })` deprecated. |
|
|
2700
|
+
| [concepts.md](/docs/concepts.md) | Local vs cloud table: same facts, constructor names. Add one line that `virtualRuntime` is local-with-your-files, not a third host. |
|
|
2701
|
+
|
|
2702
|
+
**New page**
|
|
2703
|
+
|
|
2704
|
+
| Page | Change |
|
|
2705
|
+
|---|---|
|
|
2706
|
+
| `guides/virtual-runtime.md` (new) | When to use `virtualRuntime`: no host checkout, tests (`memoryFs`), composed mounts. Empty default (`/`, no methods). Skills only if you mount them. No `workspaceDir`. Fail-closed hybrid cloud attach. Link from agent-config and concepts. |
|
|
2707
|
+
|
|
2708
|
+
**Fix examples and cross-links only**
|
|
2709
|
+
|
|
2710
|
+
| Page | Change |
|
|
2711
|
+
|---|---|
|
|
2712
|
+
| [quickstart.md](/docs/quickstart.md) | Default scaffold stays `localRuntime()` or omit `runtime` (same default). Do not mention virtual. |
|
|
2713
|
+
| [scaffolding-agents.md](/docs/scaffolding-agents.md) | Same. |
|
|
2714
|
+
| [reference/instructions.md](/docs/reference/instructions.md) / [reference/skills.md](/docs/reference/skills.md) | "On the cloud runtime" → "On `cloudRuntime`" where it is a code choice; keep the delivery facts. |
|
|
2715
|
+
| [reference/tools.md](/docs/reference/tools.md) | `tools` + cloud still fail closed. Mention it works the same on `cloudRuntime`. |
|
|
2716
|
+
| [reference/cli.md](/docs/reference/cli.md) | `agent-sdk call read` against a virtual fixture if we add one in Phase 2. |
|
|
2717
|
+
| [troubleshooting.md](/docs/troubleshooting.md) | Discovery errors: string+incoherent pair, `virtualRuntime` + `workspaceDir`, hybrid attach or local overlay on virtual. |
|
|
2718
|
+
| `templates/*.md` (e.g. [code-wiki.md](/docs/templates/code-wiki.md)) | Examples that set `runtime: "cloud"` get a constructor form; leave a one-line deprecated equivalent if the template is copy-paste for existing agents. |
|
|
2719
|
+
| [building-with-agents.md](/docs/building-with-agents.md) / [deployment.md](/docs/deployment.md) | Wording only if they name the string as the way to choose a host. |
|
|
2720
|
+
|
|
2721
|
+
**Do not document here**
|
|
2722
|
+
|
|
2723
|
+
- Cursor SDK `AgentOptions.local.workspace` (SDK maintainer JSDoc).
|
|
2724
|
+
- Change-monitor mount tables (their repo docs).
|
|
2725
|
+
- Grok Bot unless a public template already ships `grokbot`.
|
|
2726
|
+
- `kind`, `Workspace`, `FileSystem` method lists (JSDoc on
|
|
2727
|
+
`@cursor/sdk` is enough for authors who implement a mount).
|
|
2728
|
+
|
|
2729
|
+
### JSDoc (Phase 2, with the types)
|
|
2730
|
+
|
|
2731
|
+
On `@cursor/july`:
|
|
2732
|
+
|
|
2733
|
+
- `runtime?: AgentRuntime | Runtime` — constructors first.
|
|
2734
|
+
String values `@deprecated Use localRuntime(opts) / cloudRuntime(opts) / grokbotRuntime()`.
|
|
2735
|
+
- `local?` — `@deprecated Use localRuntime(opts)`. Still
|
|
2736
|
+
read when `runtime` is omitted or `"local"`. Error with
|
|
2737
|
+
`virtualRuntime` or `cloudRuntime`.
|
|
2738
|
+
- `cloud?` — `@deprecated Use cloudRuntime(opts)`.
|
|
2739
|
+
- `SendMessageOptions.cloud` — `@deprecated Use
|
|
2740
|
+
send({ runtime: cloudRuntime(opts) })`. Compat shim still
|
|
2741
|
+
merges over `agent.cloud`.
|
|
2742
|
+
- `SendMessageOptions.workspaceDir` — `@deprecated Use
|
|
2743
|
+
send({ runtime: localRuntime({ workspaceDir }) })`.
|
|
2744
|
+
- `SendMessageOptions.runtime?: CloudRuntime | LocalRuntime`
|
|
2745
|
+
— attach, overlay, or replace cloud payload. Virtual +
|
|
2746
|
+
either, and grokbot + cloud, fail at start. Together with
|
|
2747
|
+
sibling `cloud` / `workspaceDir` fails closed.
|
|
2748
|
+
|
|
2749
|
+
On `@cursor/sdk` (Phase 1):
|
|
2750
|
+
|
|
2751
|
+
- New `local.workspace` documented as the injection seam.
|
|
2752
|
+
- Existing `local` / `cloud` blocks stay unmarked. Same-shape
|
|
2753
|
+
`Agent.create({ runtime })` is after Phase 4.
|
|
2754
|
+
|
|
2755
|
+
### Deprecation window
|
|
2756
|
+
|
|
2757
|
+
Phase 4 publishes constructors and marks the string
|
|
2758
|
+
deprecated. A later PR, after that has shipped, may set a
|
|
2759
|
+
removal date. This design does not pick one.
|
|
2760
|
+
|
|
2761
|
+
---
|
|
2762
|
+
|
|
2763
|
+
## 10. Non-goals
|
|
2764
|
+
|
|
2765
|
+
- Rewriting the cloud agent API or making `tools` work on `bc-`
|
|
2766
|
+
agents. Fail closed stays until the API can honor it.
|
|
2767
|
+
- Unifying Grok Bot into Workspace methods.
|
|
2768
|
+
- Promoting `AgentRunner` onto `defineAgent` / `Runtime`.
|
|
2769
|
+
Replay and eval fakes keep `StartServerInternalOptions.runner`.
|
|
2770
|
+
- A user-authored `infer()` on `Runtime`.
|
|
2771
|
+
- `Runtime.instructions()` or `Runtime.read()` — those belong
|
|
2772
|
+
on Workspace, or they re-bundle the string.
|
|
2773
|
+
- A `Shell` type the caller attaches next to `FileSystem`.
|
|
2774
|
+
`shell` stays on `Workspace`.
|
|
2775
|
+
- `virtualRuntime(workspace: Workspace)`. The lift is
|
|
2776
|
+
internal; authors pass a `FileSystem`. Change-monitor
|
|
2777
|
+
`search` / tags stay authored if needed.
|
|
2778
|
+
- Moving server tools, MCP, or `ctx.host.files` behind
|
|
2779
|
+
Workspace.
|
|
2780
|
+
- A `runtime: "virtual"` string.
|
|
2781
|
+
- `SendMessageOptions.runtime?: Runtime` (the full union,
|
|
2782
|
+
including grokbot). The field is `CloudRuntime |
|
|
2783
|
+
LocalRuntime`. Honoring every pair is still a non-goal.
|
|
2784
|
+
- Removing string `runtime` or sibling `local` / `cloud` in
|
|
2785
|
+
Phases 1–4. Deprecate in docs and JSDoc; delete later.
|
|
2786
|
+
- Letting `cloudRuntime` pretend to implement Workspace methods
|
|
2787
|
+
by prompting the VM.
|
|
2788
|
+
- Shipping lookalike server tools as the first binding.
|
|
2789
|
+
- One PR that lands SDK plumbing, july constructors, and
|
|
2790
|
+
change-monitor together.
|
|
2791
|
+
- A `Harness` / `Runtime.webSearch()` surface so todos and
|
|
2792
|
+
search look like Workspace methods. Availability is
|
|
2793
|
+
`defineAgent({ tools })`. Rebind later via local executor
|
|
2794
|
+
overrides if a consumer needs a stub.
|
|
2795
|
+
|
|
2796
|
+
---
|
|
2797
|
+
|
|
2798
|
+
## 11. Proof each code PR owes
|
|
2799
|
+
|
|
2800
|
+
Phase 0 has no experiment. Later PRs prove the seam they
|
|
2801
|
+
open, not the whole stack.
|
|
2802
|
+
|
|
2803
|
+
1. **Phase 1.** Cursor SDK unit test: `overrideReadExecutor`
|
|
2804
|
+
(and ls/grep) serve a map. `Agent.create` with an injected
|
|
2805
|
+
workspace advertises only the present methods.
|
|
2806
|
+
2. **Phase 2.** Fixture agent with
|
|
2807
|
+
`virtualRuntime(memoryFs({ "/a.txt": "hi" }))`.
|
|
2808
|
+
`agent-sdk call read` returns `hi` from native `read`.
|
|
2809
|
+
Discovery matrix matches today's fail-closed errors.
|
|
2810
|
+
3. **Phase 3.** Change-monitor planner/executor typecheck
|
|
2811
|
+
against `virtualRuntime(unionFs(…))`. No second schema copy.
|
|
2812
|
+
No host checkout.
|
|
2813
|
+
|
|
2814
|
+
---
|
|
2815
|
+
|
|
2816
|
+
## 12. Decision summary
|
|
2817
|
+
|
|
2818
|
+
| Question | Answer |
|
|
2819
|
+
|---|---|
|
|
2820
|
+
| Design goals? | Compat with today's `defineAgent` / `AgentOptions` shape. Default `localRuntime()`. Fail-closed incoherent pairs. Extensibility via `Workspace`, not a longer enum. |
|
|
2821
|
+
| What replaces the string? | Closed `Runtime` union. `workspace` only on `{ kind: "local" }`. `localRuntime(opts)` / `cloudRuntime(opts)` take today's option bags. String + sibling `local` / `cloud` stay, deprecated. |
|
|
2822
|
+
| Where do file/shell verbs live? | `FileSystem` in `@cursor/sdk` (native tool names + `NativeArgs<T>`). `Workspace` adds `shell` / `root` / `brief`. No public `search` method. |
|
|
2823
|
+
| How do new files get created? | Native **Write** tool (`write` on FileSystem). Str-replace `edit` cannot create. Apply-patch `edit` can via `Add File`. |
|
|
2824
|
+
| Empty `virtualRuntime()`? | `/`, no methods, no mounts, no skills, no materialization. Application unions what it needs (`skillsFs()` is opt-in). |
|
|
2825
|
+
| Session filesystem? | `virtualRuntime(async (ctx) => fs)` once per session. Always `Promise<FileSystem>`. `SessionWorkspaceContext`: `sessionId`, `agentName?`, `continuationKey?`, `host?`. Per-wake data is a lazy layer. |
|
|
2826
|
+
| Effect / approvals? | Unchanged defaults. Gate wraps the tool call, then Workspace runs. Host path allowlists stay on `localWorkspace` only. |
|
|
2827
|
+
| Does method presence enable tools? | Yes, for Workspace methods. `tools` may only narrow, fail-closed. |
|
|
2828
|
+
| Where do `webSearch` / `updateTodos` live? | Off Workspace. Loop-owned harness builtins. `tools` opts them in on local/virtual. Cloud already has them and cannot select. No `Harness` type. |
|
|
2829
|
+
| Should those methods live on `Runtime`? | No. That re-bundles the arm and the workspace. |
|
|
2830
|
+
| Should `Runtime.instructions()` exist? | No. Workspace gets `root()` + optional session-scoped `brief()`. |
|
|
2831
|
+
| Should inference live on `Runtime`? | No. |
|
|
2832
|
+
| What about Grok Bot? | `grokbotRuntime()` — a Runtime arm, no `workspace` field. |
|
|
2833
|
+
| How does change-monitor fit? | `virtualRuntime(async (ctx) => unionFs({ /repo, /host, /agent/skills: skillsFs(), … }))`. |
|
|
2834
|
+
| How does computer-use.md's VM fit? | A `cloudFs` mount in that same union. |
|
|
2835
|
+
| How does `runtime: "cloud"` fit? | `cloudRuntime(opts)` — opaque Runtime arm that owns its tree. |
|
|
2836
|
+
| `send({ runtime })`? | `CloudRuntime \| LocalRuntime`. Host-local + cloud = attach. Host-local + `localRuntime` = overlay. Cloud + `cloudRuntime` = replace (no merge). Grokbot + cloud refused. Virtual + either refused. `send({ cloud })` still merges (deprecated). |
|
|
2837
|
+
| What stays off `Runtime`? | `model`, `tools`, `builtinTools`, `hosting`, `architecture`, `workspaceFiles`. |
|
|
2838
|
+
| Subagents? | Behavior unchanged (`G12`). Internally `localRuntime()` on the parent workspace. No authoring `Runtime` on children. |
|
|
2839
|
+
| Can this ship without Cursor SDK changes? | No. Lookalikes put the model on MCP `CallMcpTool`, not native `read`. |
|
|
2840
|
+
| First PR? | This design document. |
|
|
2841
|
+
| First code PR? | Cursor SDK `local.workspace` + executor overrides (Phase 1). |
|
|
2842
|
+
| Then? | Agent SDK object form (Phase 2), change-monitor (Phase 3), published docs (Phase 4 / §9). After that: `Agent.create({ runtime })` on the SDK; other products plug into this type; see §8. |
|
|
2843
|
+
|
|
2844
|
+
The string was a convenient name for a bundle that no longer
|
|
2845
|
+
bundles cleanly. The replacement is a local Runtime arm you
|
|
2846
|
+
can point at any workspace, and two remote arms that have no
|
|
2847
|
+
`workspace` field until those products grow a machine API.
|
|
2848
|
+
Native tools bind to the injected workspace. That seam opens
|
|
2849
|
+
in Cursor SDK before `defineAgent` grows constructors.
|
|
1498
2850
|
|
|
1499
2851
|
---
|
|
1500
2852
|
|
|
@@ -2795,8 +4147,9 @@ Source: /docs/guides/mcp-oauth.md
|
|
|
2795
4147
|
Use host MCP OAuth when your agent talks to a remote MCP server that
|
|
2796
4148
|
speaks OAuth, and you want credentials on the serve host (or the hosted
|
|
2797
4149
|
engine) instead of a Cursor account connector. Local login writes tokens
|
|
2798
|
-
next to your Cursor credentials. `--store` copies
|
|
2799
|
-
deployment as secrets so prod can reconnect after a redeploy.
|
|
4150
|
+
next to your Cursor credentials. `--store` copies those tokens onto the
|
|
4151
|
+
deployment as secrets so prod can reconnect after a redeploy. Hosted
|
|
4152
|
+
Connect lets the current process retry.
|
|
2800
4153
|
|
|
2801
4154
|
The companion skill is
|
|
2802
4155
|
[`skills/mcp-auth/SKILL.md`](https://github.com/cursor/cursor/blob/main/packages/agent-serve/skills/mcp-auth/SKILL.md).
|
|
@@ -2807,8 +4160,8 @@ The companion skill is
|
|
|
2807
4160
|
flow (`agent-sdk mcp oauth <connection>`)
|
|
2808
4161
|
- Keep tokens in `mcp-auth.json` under the CLI config directory, bound
|
|
2809
4162
|
to that connection's resource URL
|
|
2810
|
-
- Upsert deployment secrets with `--store` so
|
|
2811
|
-
same tokens from env
|
|
4163
|
+
- Upsert deployment secrets with `--store` so the next deploy seeds the
|
|
4164
|
+
same tokens from env. Hosted Connect lets the current process retry.
|
|
2812
4165
|
- Use `advertiseTools: true` when local turns should call the server by
|
|
2813
4166
|
name. Host tools can still call it through `ctx.host.mcp`.
|
|
2814
4167
|
|
|
@@ -2888,7 +4241,16 @@ connection URL (tokens are bound to the resource URL).
|
|
|
2888
4241
|
|
|
2889
4242
|
## How do I store credentials on a hosted deployment?
|
|
2890
4243
|
|
|
2891
|
-
Authorize once
|
|
4244
|
+
Authorize once. Then get the tokens onto the deployment.
|
|
4245
|
+
|
|
4246
|
+
On a hosted engine, finishing Connect lets the current process retry.
|
|
4247
|
+
Self-hosted serve writes tokens locally only.
|
|
4248
|
+
|
|
4249
|
+
`--store` from a signed-in laptop is the reliable way to put
|
|
4250
|
+
`MCP_OAUTH_*` on the next deploy. If `agent-sdk secrets list` is
|
|
4251
|
+
empty, run `--store` and redeploy.
|
|
4252
|
+
|
|
4253
|
+
From a laptop, push secrets with the CLI:
|
|
2892
4254
|
|
|
2893
4255
|
```bash
|
|
2894
4256
|
agent-sdk mcp oauth inventory --store
|
|
@@ -2906,13 +4268,21 @@ agent-sdk mcp oauth inventory --store
|
|
|
2906
4268
|
| `MCP_OAUTH_<NAME>_CLIENT_ID` | dynamic client id when registration returned one |
|
|
2907
4269
|
|
|
2908
4270
|
You must be signed in (`agent-sdk login`) with permission to set secrets
|
|
2909
|
-
on that slug. Secrets apply on the **next** deploy
|
|
2910
|
-
`agent-sdk deploy`
|
|
4271
|
+
on that slug. Secrets apply on the **next** deploy. Run
|
|
4272
|
+
`agent-sdk deploy` after `--store`. A fresh pod sees `MCP_OAUTH_*`
|
|
4273
|
+
only after that deploy.
|
|
2911
4274
|
|
|
2912
4275
|
On the engine, when `mcp-auth.json` is empty, serve seeds the OAuth
|
|
2913
4276
|
provider from those env vars so host MCP calls work without a browser
|
|
2914
4277
|
on the pod.
|
|
2915
4278
|
|
|
4279
|
+
These secrets are deployment-wide. Anyone who can list or set secrets
|
|
4280
|
+
on that slug, and the hosted engine's runtime env, can use them. That
|
|
4281
|
+
is the same blast radius as `--store`. They are not scoped to the
|
|
4282
|
+
caller who clicked Connect. `cursorAccount: true` tokens stay on the
|
|
4283
|
+
Cursor backend for the deployment service account and do not use
|
|
4284
|
+
`MCP_OAUTH_*`.
|
|
4285
|
+
|
|
2916
4286
|
## How do host tools call the server?
|
|
2917
4287
|
|
|
2918
4288
|
```ts
|
|
@@ -2932,7 +4302,7 @@ an allowlist or other deterministic gate.
|
|
|
2932
4302
|
| --- | --- |
|
|
2933
4303
|
| `must be defineConnection({ url, oauth: true })` | Add `oauth: true` on that connection, or pick the right connection name |
|
|
2934
4304
|
| Callback never completes | Keep port `8787` free; finish the browser login on this machine |
|
|
2935
|
-
| Hosted calls unauthorized after `--store` | Confirm secrets with `agent-sdk secrets list <slug>`, then redeploy |
|
|
4305
|
+
| Hosted calls unauthorized after Connect or `--store` | Confirm secrets with `agent-sdk secrets list <slug>`, then redeploy |
|
|
2936
4306
|
| Tokens ignored after URL change | Expected: resource URL binding drops stale entries. Re-run `mcp oauth` |
|
|
2937
4307
|
|
|
2938
4308
|
## What's next
|
|
@@ -3162,16 +4532,14 @@ Source: /docs/guides/slack.md
|
|
|
3162
4532
|
|
|
3163
4533
|
# Slack agents
|
|
3164
4534
|
|
|
3165
|
-
The Slack channel
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
`agent-sdk slack init --manual` and paste the manifests at
|
|
3169
|
-
[api.slack.com](https://api.slack.com/apps). Socket Mode has no
|
|
3170
|
-
public Request URL. Replies stream in threads, with tool "thinking" steps,
|
|
3171
|
-
suggested prompts, and opt-in approval buttons.
|
|
4535
|
+
The Slack channel is a Socket Mode bot. The app connects out, so you
|
|
4536
|
+
don't need a public Request URL. Mentions and DMs start a thread.
|
|
4537
|
+
Replies stream there, with thinking steps and suggested prompts.
|
|
3172
4538
|
|
|
3173
|
-
|
|
3174
|
-
|
|
4539
|
+
Use `agent-sdk slack create` when Cursor should own the Slack app. Use
|
|
4540
|
+
`agent-sdk slack init --manual` when you own it. Commands and flags live
|
|
4541
|
+
in the [CLI reference](/docs/reference/cli.md#slack). Coding agents should
|
|
4542
|
+
follow [`skills/setup-slack/SKILL.md`](https://github.com/cursor/cursor/blob/main/packages/agent-serve/skills/setup-slack/SKILL.md).
|
|
3175
4543
|
|
|
3176
4544
|
## Define the channel
|
|
3177
4545
|
|
|
@@ -3181,35 +4549,23 @@ Author `agent/channels/slack.ts` with `slackChannel()` from
|
|
|
3181
4549
|
```ts
|
|
3182
4550
|
import { slackChannel } from "@cursor/july/channels/slack";
|
|
3183
4551
|
|
|
3184
|
-
// Single agent: reads SLACK_BOT_TOKEN + SLACK_APP_TOKEN
|
|
3185
4552
|
export default slackChannel();
|
|
3186
|
-
|
|
3187
|
-
// Multi-agent serve: one Slack app (and token pair) per agent
|
|
3188
|
-
export default slackChannel({ envPrefix: "WEATHER_AGENT" });
|
|
3189
|
-
// → WEATHER_AGENT_SLACK_BOT_TOKEN + WEATHER_AGENT_SLACK_APP_TOKEN
|
|
3190
4553
|
```
|
|
3191
4554
|
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
`
|
|
3195
|
-
|
|
3196
|
-
sessions with `continuationToken = channelId:threadTs`, so a thread is
|
|
3197
|
-
one durable conversation. Replies stream (`chat.startStream` /
|
|
3198
|
-
`appendStream` / `stopStream`) with a plain-message fallback, a rotating
|
|
3199
|
-
status shows while the agent works, and the pack sets thread titles and
|
|
3200
|
-
suggested prompts. When a Slack API is unavailable (`method_not_found`,
|
|
3201
|
-
`missing_scope`), the pack degrades to `postMessage` instead of failing.
|
|
4555
|
+
`slackChannel()` reads `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`. Pass
|
|
4556
|
+
`envPrefix: "WEATHER_AGENT"` to read `WEATHER_AGENT_SLACK_BOT_TOKEN`
|
|
4557
|
+
and `WEATHER_AGENT_SLACK_APP_TOKEN` instead. Use a prefix when one
|
|
4558
|
+
host serves several Slack bots.
|
|
3202
4559
|
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
4560
|
+
A thread is one conversation. The bot answers mentions and DMs. It
|
|
4561
|
+
refuses Slack Connect users, guests, and people from other workspaces.
|
|
4562
|
+
Missing tokens leave this channel idle. The rest of `serve` still
|
|
4563
|
+
starts.
|
|
3206
4564
|
|
|
3207
4565
|
## Control who can message the agent
|
|
3208
4566
|
|
|
3209
|
-
External senders are blocked
|
|
3210
|
-
people
|
|
3211
|
-
handler. Set `blockExternals: false` only when the agent should serve
|
|
3212
|
-
people outside your org:
|
|
4567
|
+
External senders are blocked. Set `blockExternals: false` only when
|
|
4568
|
+
people outside your org should reach the agent:
|
|
3213
4569
|
|
|
3214
4570
|
```ts
|
|
3215
4571
|
export default slackChannel({
|
|
@@ -3217,9 +4573,8 @@ export default slackChannel({
|
|
|
3217
4573
|
});
|
|
3218
4574
|
```
|
|
3219
4575
|
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
`respondTo: "author"`:
|
|
4576
|
+
Anyone in the thread can continue it. Restrict follow-ups to the person
|
|
4577
|
+
who started the session with `respondTo: "author"`:
|
|
3223
4578
|
|
|
3224
4579
|
```ts
|
|
3225
4580
|
export default slackChannel({
|
|
@@ -3229,237 +4584,143 @@ export default slackChannel({
|
|
|
3229
4584
|
|
|
3230
4585
|
## Set it up
|
|
3231
4586
|
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
a manifest at [api.slack.com](https://api.slack.com/apps) unless you
|
|
3237
|
-
are on the [manual setup](#manual-setup) path.
|
|
3238
|
-
|
|
3239
|
-
Bots that already have tokens in `.env.local` keep working. Skip the
|
|
3240
|
-
wizard unless you want a Cursor-managed app.
|
|
4587
|
+
`slack create` opens the Cursor dashboard wizard. Finish Slack consent
|
|
4588
|
+
and the bot name there. The CLI writes tokens to `.env.local` and never
|
|
4589
|
+
prints them. Skip the wizard if those tokens are already set, unless
|
|
4590
|
+
you want a Cursor-managed app.
|
|
3241
4591
|
|
|
3242
4592
|
### Provision with the CLI
|
|
3243
4593
|
|
|
3244
4594
|
```bash
|
|
3245
|
-
agent-sdk slack create --dir .
|
|
3246
|
-
agent-sdk slack create --dir . --prod
|
|
4595
|
+
agent-sdk slack create --dir .
|
|
4596
|
+
agent-sdk slack create --dir . --prod
|
|
3247
4597
|
```
|
|
3248
4598
|
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
to this agent** in the browser as the same Cursor account. The CLI
|
|
3253
|
-
writes `<PREFIX>_SLACK_BOT_TOKEN` and `<PREFIX>_SLACK_APP_TOKEN` into
|
|
3254
|
-
`<dir>/.env.local`, then runs `doctor`. Serve and smoke it as described
|
|
3255
|
-
[below](#wire-the-env-and-verify).
|
|
4599
|
+
Sign in first (`agent-sdk login` or `CURSOR_API_KEY`). Finish **Add
|
|
4600
|
+
Slack to this agent** as the same Cursor account. Then run
|
|
4601
|
+
`agent-sdk slack doctor --prefix MY_AGENT` and serve:
|
|
3256
4602
|
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
4603
|
+
```bash
|
|
4604
|
+
agent-sdk serve --dir . --dev
|
|
4605
|
+
```
|
|
4606
|
+
|
|
4607
|
+
Invite the bot and mention it, or DM it. Expect a Thinking status,
|
|
4608
|
+
then a threaded streaming reply.
|
|
4609
|
+
|
|
4610
|
+
If Slack needs a workspace admin, keep the CLI running. Open the
|
|
4611
|
+
**Request approval** link it prints. After an admin approves, click
|
|
3261
4612
|
**Retry** in the wizard.
|
|
3262
4613
|
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
that Slack app. It keeps the Slack app id and replaces the manifest and
|
|
3267
|
-
tokens. `agent-sdk slack destroy` deletes the provisioned app.
|
|
3268
|
-
`agent-sdk slack icon <source>` updates the icon later.
|
|
4614
|
+
`slack destroy` deletes the Cursor-managed app. `slack icon` updates
|
|
4615
|
+
its icon. A second `slack create` for the same agent and environment
|
|
4616
|
+
replaces that app's manifest and tokens.
|
|
3269
4617
|
|
|
3270
4618
|
### Provision from the dashboard
|
|
3271
4619
|
|
|
3272
4620
|
The same wizard is on [Deployed Agents](https://cursor.com/dashboard).
|
|
3273
|
-
Open **Integrations**
|
|
3274
|
-
or follow the URL `slack create` printed.
|
|
4621
|
+
Open **Integrations** and click **Add Slack to this agent**.
|
|
3275
4622
|
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
**Deploy** injects the stored secrets; there is no Slack-only Redeploy.
|
|
4623
|
+
Local `agent-sdk serve` reads `.env.local`. The next deploy injects the
|
|
4624
|
+
stored secrets.
|
|
3279
4625
|
|
|
3280
|
-
If Slack needs admin approval,
|
|
3281
|
-
|
|
3282
|
-
reminder). After an admin approves, click **Retry**.
|
|
4626
|
+
If Slack needs admin approval, click **Request approval**, then
|
|
4627
|
+
**Retry** after an admin approves.
|
|
3283
4628
|
|
|
3284
|
-
The app row's menu covers
|
|
3285
|
-
|
|
4629
|
+
The app row's menu covers rename, icon, token rotation, removal, and
|
|
4630
|
+
workspace disconnect.
|
|
3286
4631
|
|
|
3287
|
-
###
|
|
4632
|
+
### Create your own Slack app
|
|
3288
4633
|
|
|
3289
|
-
`
|
|
3290
|
-
|
|
3291
|
-
|
|
4634
|
+
`slack init --manual` is for an app you own. It writes the channel file
|
|
4635
|
+
and Slack manifests. When Slack CLI (`~/.slack/bin/slack`) is logged
|
|
4636
|
+
in, it installs the app. When it isn't, install the app yourself.
|
|
3292
4637
|
|
|
3293
4638
|
```bash
|
|
3294
|
-
agent-sdk slack
|
|
4639
|
+
agent-sdk slack init --manual --dir . --name "My Agent"
|
|
3295
4640
|
```
|
|
3296
4641
|
|
|
3297
|
-
|
|
3298
|
-
`auth_test` are all green. Then serve and smoke it:
|
|
4642
|
+
Install Slack CLI if you need it:
|
|
3299
4643
|
|
|
3300
4644
|
```bash
|
|
3301
|
-
|
|
3302
|
-
# expect: [agent-sdk/slack] Socket Mode connected
|
|
4645
|
+
curl -fsSL https://downloads.slack-edge.com/slack-cli/install.sh | bash
|
|
3303
4646
|
```
|
|
3304
4647
|
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
`
|
|
4648
|
+
Log in with `slack login --no-prompt`. In Slack, send
|
|
4649
|
+
`/slackauthticket <ticket>`, then
|
|
4650
|
+
`slack login --ticket <ticket> --challenge <code>`. Install the
|
|
4651
|
+
workspace app with `slack app install`. Create a separate production
|
|
4652
|
+
app the same way.
|
|
3309
4653
|
|
|
3310
|
-
|
|
4654
|
+
Or paste `.slack/manifest.dev.json` at
|
|
4655
|
+
[api.slack.com/apps](https://api.slack.com/apps) (**Create New App** →
|
|
4656
|
+
**From a manifest**). Repeat with `.slack/manifest.prod.json`. Dev and
|
|
4657
|
+
prod are separate Slack apps.
|
|
3311
4658
|
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
option.
|
|
3316
|
-
|
|
3317
|
-
```bash
|
|
3318
|
-
agent-sdk slack init --manual --dir . --name "My Agent"
|
|
3319
|
-
```
|
|
4659
|
+
Then put a bot token (`xoxb-...`) and an app-level token with
|
|
4660
|
+
`connections:write` (`xapp-...`) in `.env.local`. Use the names
|
|
4661
|
+
`slack init --manual` printed. Run `slack doctor` and serve as above.
|
|
3320
4662
|
|
|
3321
|
-
|
|
3322
|
-
project state directory, `env.example`, and `setup-status.json`.
|
|
3323
|
-
`--no-prefix` uses shared `SLACK_*` variables on
|
|
3324
|
-
a single-agent host. `--prefix CUSTOM` overrides the directory-derived
|
|
3325
|
-
prefix. `--channel-posts` subscribes the manifests to channel-post
|
|
3326
|
-
events.
|
|
3327
|
-
|
|
3328
|
-
`slack manifest --env both` regenerates the JSON files without
|
|
3329
|
-
touching the channel file.
|
|
3330
|
-
|
|
3331
|
-
#### Create the apps
|
|
3332
|
-
|
|
3333
|
-
This phase needs someone with permission to create Slack apps.
|
|
3334
|
-
|
|
3335
|
-
1. Open [api.slack.com/apps](https://api.slack.com/apps) → **Create New
|
|
3336
|
-
App** → **From a manifest**.
|
|
3337
|
-
2. Paste `manifest.dev.json` and create the dev app.
|
|
3338
|
-
3. Repeat with `manifest.prod.json`. Dev and prod are separate Slack
|
|
3339
|
-
apps with separate tokens.
|
|
3340
|
-
|
|
3341
|
-
#### Install and mint tokens
|
|
3342
|
-
|
|
3343
|
-
For each app: **Install to Workspace** and copy the Bot User OAuth Token
|
|
3344
|
-
(`xoxb-…`). Then under **Basic Information → App-Level Tokens** create a
|
|
3345
|
-
token with scope `connections:write` and copy it (`xapp-…`).
|
|
3346
|
-
|
|
3347
|
-
Put the pair in `.env.local` using the names from `env.example`, then
|
|
3348
|
-
run `slack doctor` and serve as [above](#wire-the-env-and-verify).
|
|
4663
|
+
Serve this app with Agent SDK. Do not run `slack deploy`.
|
|
3349
4664
|
|
|
3350
4665
|
## Choose when the agent engages
|
|
3351
4666
|
|
|
3352
|
-
Mentions and DMs are on
|
|
3353
|
-
|
|
4667
|
+
Mentions and DMs are on. Watching channel posts without a mention is
|
|
4668
|
+
opt-in:
|
|
3354
4669
|
|
|
3355
4670
|
```ts
|
|
3356
4671
|
export default slackChannel({
|
|
3357
4672
|
envPrefix: "TRIAGE",
|
|
3358
4673
|
engagement: {
|
|
3359
|
-
// mentions / directMessages default to true
|
|
3360
4674
|
channelPosts: {
|
|
3361
|
-
allow: ["#triage-alerts"],
|
|
3362
|
-
posts: "top-level",
|
|
3363
|
-
debounceMs: 15_000,
|
|
3364
|
-
includeBotPosts: false, // default: bot-authored posts never dispatch
|
|
4675
|
+
allow: ["#triage-alerts"],
|
|
4676
|
+
posts: "top-level",
|
|
4677
|
+
debounceMs: 15_000,
|
|
3365
4678
|
},
|
|
3366
4679
|
},
|
|
3367
|
-
onChannelPost: async (
|
|
3368
|
-
// same contract as onAppMention: return null to skip
|
|
4680
|
+
onChannelPost: async (_ctx, message) => {
|
|
3369
4681
|
return message.markdown.length > 20 ? {} : null;
|
|
3370
4682
|
},
|
|
3371
4683
|
});
|
|
3372
4684
|
```
|
|
3373
4685
|
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
`
|
|
3377
|
-
channel.
|
|
3378
|
-
|
|
3379
|
-
Set `allow: ["*"]` to watch every channel the bot has joined.
|
|
4686
|
+
Pass `--channel-posts` on `slack create` or `slack init --manual` so
|
|
4687
|
+
the Slack app receives those events. The bot must be in each watched
|
|
4688
|
+
channel. `allow: ["*"]` watches every joined channel.
|
|
3380
4689
|
|
|
3381
|
-
Set `includeBotPosts: true`
|
|
3382
|
-
|
|
3383
|
-
watching app's own posts stay dropped either way, matched by the `bot_id`
|
|
3384
|
-
and bot user id from `auth.test`, so an agent can never dispatch on its
|
|
3385
|
-
own replies. Use this for a bot-fed alerts channel.
|
|
4690
|
+
Set `includeBotPosts: true` for alert feeds and other bots. This app
|
|
4691
|
+
never wakes on its own posts.
|
|
3386
4692
|
|
|
3387
4693
|
## Prepare work on the host
|
|
3388
4694
|
|
|
3389
4695
|
Mention and DM handlers may return a prepared `message`,
|
|
3390
|
-
`workspaceFiles`, or `cloud` block.
|
|
4696
|
+
`workspaceFiles`, or `cloud` block. Use the same host-prep pattern as
|
|
3391
4697
|
[custom channels](/docs/guides/webhooks.md#prepare-on-the-host-then-hand-off).
|
|
3392
|
-
PR agents use it: extract a PR URL from the mention text and run the
|
|
3393
|
-
same host path as the HTTP channel.
|
|
3394
4698
|
|
|
3395
|
-
Slack file uploads
|
|
4699
|
+
Slack file uploads attach automatically. Images become vision input.
|
|
3396
4700
|
Supported documents become workspace files for the turn.
|
|
3397
4701
|
|
|
3398
4702
|
## Add approval buttons
|
|
3399
4703
|
|
|
3400
|
-
Tools with `needsApproval` park until a person decides.
|
|
3401
|
-
|
|
4704
|
+
Tools with `needsApproval` park until a person decides. Post those
|
|
4705
|
+
decisions in Slack with one flag:
|
|
3402
4706
|
|
|
3403
4707
|
```ts
|
|
3404
4708
|
export default slackChannel({
|
|
3405
|
-
toolApprovals: true,
|
|
4709
|
+
toolApprovals: true,
|
|
3406
4710
|
});
|
|
3407
4711
|
```
|
|
3408
4712
|
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
Cards show redacted, truncated arguments (Block Kit size limits);
|
|
3415
|
-
execution still uses the full validated input, so review sensitive tools
|
|
3416
|
-
in the playground when the arguments may exceed the card. Approvals
|
|
3417
|
-
exist for `execution: "server"` tools on the local runtime only, and
|
|
3418
|
-
parked calls do not survive a host restart. The full lifecycle is in
|
|
4713
|
+
The Slack app needs interactivity. Recreate it with `slack create` if
|
|
4714
|
+
clicks do nothing. Cards show redacted, truncated arguments. Execution
|
|
4715
|
+
still uses the full input, so review sensitive tools in the playground
|
|
4716
|
+
when the card may hide fields. The lifecycle is in
|
|
3419
4717
|
[Human-in-the-loop](/docs/guides/human-in-the-loop.md).
|
|
3420
4718
|
|
|
3421
4719
|
## Run several agents on one host
|
|
3422
4720
|
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
with their Slack channel idle while everything else serves normally.
|
|
3427
|
-
|
|
3428
|
-
## Keep the channel healthy
|
|
3429
|
-
|
|
3430
|
-
Two habits matter most.
|
|
3431
|
-
|
|
3432
|
-
- Don't `await` long work inside Slack dispatch handlers. The pack
|
|
3433
|
-
dispatches through `waitUntil` and streams as the turn progresses.
|
|
3434
|
-
- In `--dev` (loopback) or `--allow-anonymous` (trusted shared host), the
|
|
3435
|
-
playground can list and stream Slack sessions and resolve their parked
|
|
3436
|
-
approvals (the audit trail records the HTTP caller). Bearer-auth hosts
|
|
3437
|
-
stay strict: Slack approvals must come from Slack interactivity or a
|
|
3438
|
-
matching principal.
|
|
3439
|
-
|
|
3440
|
-
## CLI reference
|
|
3441
|
-
|
|
3442
|
-
The `slack` subcommands cover setup end to end.
|
|
3443
|
-
|
|
3444
|
-
```bash
|
|
3445
|
-
agent-sdk slack setup # printed setup guide
|
|
3446
|
-
agent-sdk slack create --dir . # dashboard wizard (dev app)
|
|
3447
|
-
agent-sdk slack create --dir . --prod # prod app
|
|
3448
|
-
agent-sdk slack destroy --dir . # delete the provisioned app
|
|
3449
|
-
agent-sdk slack icon ./icon.png --dir . # set the provisioned app's icon
|
|
3450
|
-
agent-sdk slack init --manual --dir . # manifests to paste at api.slack.com
|
|
3451
|
-
agent-sdk slack manifest --env both # regenerate those JSON files
|
|
3452
|
-
agent-sdk slack doctor --prefix MY_AGENT # token / connectivity checks
|
|
3453
|
-
```
|
|
3454
|
-
|
|
3455
|
-
## What's next
|
|
3456
|
-
|
|
3457
|
-
Continue with these pages:
|
|
3458
|
-
|
|
3459
|
-
- [Human-in-the-loop](/docs/guides/human-in-the-loop.md): the approval lifecycle
|
|
3460
|
-
behind `toolApprovals`
|
|
3461
|
-
- [Webhooks and custom channels](/docs/guides/webhooks.md): the mechanism this
|
|
3462
|
-
pack is built on
|
|
4721
|
+
Give each agent its own Slack app and token pair. Set `envPrefix` so
|
|
4722
|
+
the names don't collide (`WEATHER_AGENT_SLACK_*`, `TRIAGE_SLACK_*`).
|
|
4723
|
+
Agents without tokens stay idle. The others still serve.
|
|
3463
4724
|
|
|
3464
4725
|
---
|
|
3465
4726
|
|
|
@@ -4058,7 +5319,7 @@ npx @cursor/july docs
|
|
|
4058
5319
|
| Building a new agent with Cursor | [Scaffold an agent with Cursor](/docs/scaffolding-agents.md) |
|
|
4059
5320
|
| Turning a Cursor Automation into a project | [Convert a Cursor Automation](/docs/guides/convert-automation.md) |
|
|
4060
5321
|
| Wiring an agent to Slack | [Slack guide](/docs/guides/slack.md) |
|
|
4061
|
-
| Starting from a packaged template | [Demo](/docs/templates/demo.md), [Code wiki](/docs/templates/code-wiki.md), [Security reviewer](/docs/templates/security-reviewer.md), [Security help](/docs/templates/security-help.md), [Triage](/docs/templates/triage.md), or [Agentic Owners](/docs/templates/agentic-owners.md) |
|
|
5322
|
+
| Starting from a packaged template | [Demo](/docs/templates/demo.md), [Code wiki](/docs/templates/code-wiki.md), [Living AGENTS.md](/docs/templates/agents-md.md), [Security reviewer](/docs/templates/security-reviewer.md), [Security help](/docs/templates/security-help.md), [Triage](/docs/templates/triage.md), or [Agentic Owners](/docs/templates/agentic-owners.md) |
|
|
4062
5323
|
| Wiring an agent to GitHub webhooks | [GitHub guide](/docs/guides/github.md) |
|
|
4063
5324
|
| Driving PRs from a cloud VM | [PR autofixer template](/docs/templates/pr-autofixer.md) |
|
|
4064
5325
|
| Driving an agent from Linear (or another tracker) | [Webhooks guide: Linear example](/docs/guides/webhooks.md#example-linear-as-the-control-plane) |
|
|
@@ -4086,6 +5347,8 @@ npx @cursor/july docs
|
|
|
4086
5347
|
collects the PR, the model records, then comments.
|
|
4087
5348
|
- [Keep a code wiki current after merge](/docs/templates/code-wiki.md):
|
|
4088
5349
|
update in-repo `wiki/` pages when a pull request merges.
|
|
5350
|
+
- [Keep AGENTS.md current from last week's work](/docs/templates/agents-md.md):
|
|
5351
|
+
pull rules out of merged PRs and Slack, then open a follow-up PR.
|
|
4089
5352
|
- [Security reviewer](/docs/templates/security-reviewer.md): review pull
|
|
4090
5353
|
requests for exploitable bugs and post one comment.
|
|
4091
5354
|
- [Answer security questions in Slack](/docs/templates/security-help.md):
|
|
@@ -4119,8 +5382,8 @@ npx @cursor/july docs
|
|
|
4119
5382
|
- [Human-in-the-loop approvals](/docs/guides/human-in-the-loop.md): park a
|
|
4120
5383
|
tool call until a person signs off.
|
|
4121
5384
|
- [Host MCP OAuth](/docs/guides/mcp-oauth.md): authorize
|
|
4122
|
-
`oauth: true` connections, store tokens locally, and
|
|
4123
|
-
on hosted deployments.
|
|
5385
|
+
`oauth: true` connections, store tokens locally, and persist them
|
|
5386
|
+
on hosted deployments (`--store`).
|
|
4124
5387
|
- [Agent-to-agent](/docs/guides/agent-to-agent.md): every agent is an MCP
|
|
4125
5388
|
server; agents can delegate to each other.
|
|
4126
5389
|
- [Cloud runtime](/docs/guides/cloud-runtime.md): run turns on Cursor cloud
|
|
@@ -4727,8 +5990,8 @@ When every slot is busy, newly admitted turns queue FIFO instead of
|
|
|
4727
5990
|
failing: the stream records a durable `turn.queued` event with the
|
|
4728
5991
|
queue position, `GET /v1/sessions` reports `queued: true`, and each
|
|
4729
5992
|
queued turn starts as soon as a slot frees. A queued turn still counts
|
|
4730
|
-
as running for busy semantics: follow-ups preempt it, and direct
|
|
4731
|
-
calls get `409 session_busy`. Omit for unlimited.
|
|
5993
|
+
as running for busy semantics: follow-ups preempt it, and direct
|
|
5994
|
+
write-effect tool calls get `409 session_busy`. Omit for unlimited.
|
|
4732
5995
|
|
|
4733
5996
|
```ts
|
|
4734
5997
|
export default defineAgent({
|
|
@@ -5507,8 +6770,9 @@ the value before execution. A local call needs no inference credential.
|
|
|
5507
6770
|
A hosted call still needs Cursor credentials to reach the deployment.
|
|
5508
6771
|
|
|
5509
6772
|
`--session` runs the tool inside an existing session and records it on
|
|
5510
|
-
the event stream. If a model turn is active or pending,
|
|
5511
|
-
|
|
6773
|
+
the event stream. If a model turn is active or pending, a read-effect
|
|
6774
|
+
tool runs alongside it and a write-effect tool gets `session_busy`;
|
|
6775
|
+
retry after the turn finishes. The command
|
|
5512
6776
|
prints the server's JSON response and exits non-zero unless the HTTP
|
|
5513
6777
|
response succeeds with `ok: true`. See
|
|
5514
6778
|
[Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn).
|
|
@@ -5580,6 +6844,7 @@ non-zero when the reconstructed trajectory failed.
|
|
|
5580
6844
|
agent-sdk init ./my-agent # scaffold package.json, tsconfig.json, agent/ + a demo tool
|
|
5581
6845
|
agent-sdk init ./my-demo --template demo # record a PR walkthrough
|
|
5582
6846
|
agent-sdk init ./code-wiki --template code-wiki # keep wiki pages current after merge
|
|
6847
|
+
agent-sdk init ./agents-md --template agents-md # keep AGENTS.md current from last week's PRs and Slack
|
|
5583
6848
|
agent-sdk init ./my-reviewer --template security-reviewer # review PRs for security bugs
|
|
5584
6849
|
agent-sdk init ./security-help --template security-help # answer security questions in Slack
|
|
5585
6850
|
agent-sdk init ./my-triage --template triage-linear # comment on Linear issues
|
|
@@ -5597,8 +6862,9 @@ agent-sdk init # no directory: print the setup guide
|
|
|
5597
6862
|
`@cursor/july` resolves for `dev` and `run`.
|
|
5598
6863
|
|
|
5599
6864
|
Templates may ship `init.json`. On a TTY, `init` asks those questions
|
|
5600
|
-
before writing files. `code-wiki`, `pr-autofixer`,
|
|
5601
|
-
ask for GitHub repos.
|
|
6865
|
+
before writing files. `code-wiki`, `pr-autofixer`, `security-help`, and
|
|
6866
|
+
`agents-md` ask for GitHub repos. `agents-md` also asks for Slack
|
|
6867
|
+
channels. Repeat
|
|
5602
6868
|
`--var id=value` to answer without a prompt.
|
|
5603
6869
|
`--json` and non-TTY hosts skip the interview unless `--var` is set.
|
|
5604
6870
|
|
|
@@ -5756,7 +7022,8 @@ commands with an explicit `--json` flag skip this automatic check.
|
|
|
5756
7022
|
agent-sdk deploy [--dir <path>] [--slug <slug> | --all] [--team <id>]
|
|
5757
7023
|
[--repo <https-url>] [--ref <git-ref>] [--path <agent-path>]
|
|
5758
7024
|
[--cursor-events-repo owner/name]...
|
|
5759
|
-
[--allow-domain <domain>]...
|
|
7025
|
+
[--allow-domain <domain>]...
|
|
7026
|
+
[--no-wait] [--json]
|
|
5760
7027
|
```
|
|
5761
7028
|
|
|
5762
7029
|
Managed hosting requires team-admin permission and the team's
|
|
@@ -5909,6 +7176,8 @@ URL connections run a browser PKCE flow. Tokens are written to
|
|
|
5909
7176
|
`mcp-auth.json` under the CLI config directory (override with
|
|
5910
7177
|
`AGENT_SERVE_CONFIG_DIR`). Pass `--store` to upsert matching
|
|
5911
7178
|
`MCP_OAUTH_<CONNECTION>_*` secrets on the hosted deployment.
|
|
7179
|
+
`--store` is the path for the next deploy. Hosted Connect lets the
|
|
7180
|
+
current process retry.
|
|
5912
7181
|
|
|
5913
7182
|
Cursor-account connections authorize the hosted deployment's service
|
|
5914
7183
|
account through the Cursor backend's connector consent flow. Those
|
|
@@ -5987,6 +7256,7 @@ agent-sdk slack icon <https-url-or-file> [--dir <path>] [--prod]
|
|
|
5987
7256
|
[--slack-team <T…>] [--team <id>] [--json]
|
|
5988
7257
|
agent-sdk slack init --manual [--dir <path>] [--name <name>]
|
|
5989
7258
|
[--prefix <prefix> | --no-prefix] [--channel-posts]
|
|
7259
|
+
[--install | --no-install] [--slack-team <T…>] [--prod]
|
|
5990
7260
|
agent-sdk slack manifest [--dir <path>] [--name <name>]
|
|
5991
7261
|
[--env dev|prod|both] [--channel-posts] [--print]
|
|
5992
7262
|
agent-sdk slack doctor [--dir <path>] [--prefix <prefix> | --no-prefix] [--json]
|
|
@@ -6015,17 +7285,26 @@ a local png, jpg, or gif file of at most 512KB.
|
|
|
6015
7285
|
|
|
6016
7286
|
`slack init` without `--manual` exits non-zero and writes no files. Use
|
|
6017
7287
|
`slack create` for the dashboard wizard. `slack init --manual` writes
|
|
6018
|
-
the channel file, development and production manifests,
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
the
|
|
6028
|
-
|
|
7288
|
+
the channel file, development and production manifests, a Slack CLI
|
|
7289
|
+
`.slack/` project (hook + manifests, committed with the repo). When
|
|
7290
|
+
Slack CLI is logged in, it installs the app. Otherwise `next` asks
|
|
7291
|
+
you to install it. `--install` requires that install. `--no-install`
|
|
7292
|
+
skips it. `--prod` selects the deployed app.
|
|
7293
|
+
`--slack-team` picks the workspace. Slack CLI keeps install tokens in
|
|
7294
|
+
that process; copy `xoxb` and mint `xapp` (`connections:write`) into
|
|
7295
|
+
`.env.local`. Tokens that appear in `.env` during that install are
|
|
7296
|
+
copied onto the prefixed names. Paste `.slack/manifest.dev.json` at
|
|
7297
|
+
api.slack.com when the Slack CLI is missing. Do not run
|
|
7298
|
+
`slack deploy`. The command refuses to overwrite the channel file.
|
|
7299
|
+
It updates the Slack CLI hook and manifests when `.slack/` already
|
|
7300
|
+
exists. If a collision occurs, it exits non-zero; files created
|
|
7301
|
+
earlier in the run remain. The token prefix defaults to the directory
|
|
7302
|
+
basename normalized to uppercase snake case.
|
|
7303
|
+
Explicit `--prefix` values use the same normalization. For example,
|
|
7304
|
+
`pr-approver` becomes `PR_APPROVER_SLACK_BOT_TOKEN`. `--no-prefix`
|
|
7305
|
+
uses shared `SLACK_BOT_TOKEN` and `SLACK_APP_TOKEN`.
|
|
7306
|
+
`--channel-posts` subscribes the manifests to channel-post events.
|
|
7307
|
+
The command always prints a JSON summary.
|
|
6029
7308
|
|
|
6030
7309
|
`slack manifest` regenerates selected manifest files. `--env` defaults
|
|
6031
7310
|
to `both`, and `--name` defaults to the directory name. `--print` writes
|
|
@@ -6156,8 +7435,10 @@ Tokens come from env vars. Never hardcode them in the file.
|
|
|
6156
7435
|
## Host MCP OAuth
|
|
6157
7436
|
|
|
6158
7437
|
For servers that speak OAuth, set `oauth: true` and authorize with the
|
|
6159
|
-
CLI. Tokens live in `mcp-auth.json` under the CLI
|
|
6160
|
-
copies them onto the
|
|
7438
|
+
CLI or mid-run Connect. Tokens live in `mcp-auth.json` under the CLI
|
|
7439
|
+
config directory. `--store` copies them onto the deployment as
|
|
7440
|
+
`MCP_OAUTH_<NAME>_*` secrets. Hosted Connect lets the current
|
|
7441
|
+
process retry.
|
|
6161
7442
|
|
|
6162
7443
|
```ts
|
|
6163
7444
|
export default defineConnection({
|
|
@@ -6169,6 +7450,7 @@ export default defineConnection({
|
|
|
6169
7450
|
```bash
|
|
6170
7451
|
agent-sdk mcp oauth inventory # browser PKCE → local mcp-auth.json
|
|
6171
7452
|
agent-sdk mcp oauth inventory --store # also upsert deployment secrets
|
|
7453
|
+
# Hosted Connect retries this process. Self-hosted stays file-only.
|
|
6172
7454
|
```
|
|
6173
7455
|
|
|
6174
7456
|
Full walkthrough: [Host MCP OAuth](/docs/guides/mcp-oauth.md). Companion
|
|
@@ -6387,7 +7669,7 @@ lazily on first use.
|
|
|
6387
7669
|
|
|
6388
7670
|
Continue with these pages:
|
|
6389
7671
|
|
|
6390
|
-
- [Host MCP OAuth](/docs/guides/mcp-oauth.md): `mcp oauth`, `--store`
|
|
7672
|
+
- [Host MCP OAuth](/docs/guides/mcp-oauth.md): `mcp oauth`, Connect, `--store`
|
|
6391
7673
|
- [Agent-to-agent](/docs/guides/agent-to-agent.md): peers in depth
|
|
6392
7674
|
- [Tools](/docs/reference/tools.md): authored tools that wrap MCP connections
|
|
6393
7675
|
- [Webhooks](/docs/guides/webhooks.md): calling MCP connections from handlers
|
|
@@ -6548,6 +7830,7 @@ and `playgroundUrl` deep-links the session in the playground.
|
|
|
6548
7830
|
| `message` | Required user message |
|
|
6549
7831
|
| `title` | Session title |
|
|
6550
7832
|
| `dryRun` | Run read tools and stub write tools |
|
|
7833
|
+
| `asOf` | ISO-8601 instant with a timezone, frozen at create; the prompt states it, `ctx.now()` returns it, and tools declaring `timeArgs` refuse calls not bounded at or before it. `400` when unusable |
|
|
6551
7834
|
| `workspaceFiles` | UTF-8 files written into the session workspace |
|
|
6552
7835
|
| `cloud` | Per-session cloud options merged over the agent defaults |
|
|
6553
7836
|
|
|
@@ -6629,7 +7912,8 @@ curl -X POST http://127.0.0.1:3000/<slug>/v1/tools/inspect_pr \
|
|
|
6629
7912
|
It runs an authored server tool in-process: schema-validated, no model
|
|
6630
7913
|
turn. An optional `"sessionId"` in the body runs it inside an existing
|
|
6631
7914
|
session and records it on that session's stream (`409 session_busy`
|
|
6632
|
-
while a turn runs
|
|
7915
|
+
for a write-effect call while a turn runs; reads run alongside the
|
|
7916
|
+
turn). Agent-execution tools are rejected with `400`, and
|
|
6633
7917
|
unknown tools with `404` and the list of available names. For the
|
|
6634
7918
|
semantics, see [Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn).
|
|
6635
7919
|
|
|
@@ -7345,8 +8629,10 @@ message. Interrupted turns record `turn.failed` with
|
|
|
7345
8629
|
Slack `stop` / `@agent stop` does the same for that thread and clears
|
|
7346
8630
|
pending coalesced nudges.
|
|
7347
8631
|
|
|
7348
|
-
Session-bound deterministic tool calls share the
|
|
7349
|
-
|
|
8632
|
+
Session-bound deterministic tool calls share the lock only for writes: a
|
|
8633
|
+
write-effect call returns `409 session_busy` while a model turn is
|
|
8634
|
+
running, a read-effect call runs alongside the turn (see
|
|
8635
|
+
[Tools](/docs/reference/tools.md#call-a-tool-without-a-model-turn)).
|
|
7350
8636
|
|
|
7351
8637
|
## Which events can I stream?
|
|
7352
8638
|
|
|
@@ -7753,6 +9039,31 @@ export default defineTool({
|
|
|
7753
9039
|
`dryRunResult` keeps the result shape stable. Without it, a stubbed write
|
|
7754
9040
|
returns `"Operation acknowledged."`. Traces mark the result as stubbed.
|
|
7755
9041
|
|
|
9042
|
+
### Bound time arguments under `asOf`
|
|
9043
|
+
|
|
9044
|
+
Declare `timeArgs` with the input properties that carry time bounds. In a
|
|
9045
|
+
session created with `asOf`, every named argument must be an absolute
|
|
9046
|
+
ISO-8601 instant with a timezone, no later than the session's frozen
|
|
9047
|
+
instant. A call that omits one, or supplies a relative or later value, is
|
|
9048
|
+
refused with an error telling the model to retry with absolute bounds.
|
|
9049
|
+
Sessions without `asOf` ignore the declaration.
|
|
9050
|
+
|
|
9051
|
+
```ts
|
|
9052
|
+
export default defineTool({
|
|
9053
|
+
description: "Query telemetry between two instants.",
|
|
9054
|
+
inputSchema: z.object({ since: z.string(), until: z.string() }),
|
|
9055
|
+
effect: "read",
|
|
9056
|
+
timeArgs: ["since", "until"],
|
|
9057
|
+
async execute({ since, until }) {
|
|
9058
|
+
return queryTelemetry(since, until);
|
|
9059
|
+
},
|
|
9060
|
+
});
|
|
9061
|
+
```
|
|
9062
|
+
|
|
9063
|
+
Every name in `timeArgs` must exist in the input schema; a session with
|
|
9064
|
+
`asOf` refuses to start otherwise, because a misspelled name would silently
|
|
9065
|
+
check nothing.
|
|
9066
|
+
|
|
7756
9067
|
## Define an agent tool
|
|
7757
9068
|
|
|
7758
9069
|
Set `execution: "agent"` and the tool materializes as a shell script
|
|
@@ -7869,7 +9180,14 @@ when the call returns. Pass a `sessionId` (a body field over
|
|
|
7869
9180
|
HTTP, `--session` on the CLI, `options.sessionId` programmatically) to
|
|
7870
9181
|
run inside an existing session instead: the tool sees that session's
|
|
7871
9182
|
workspace, and the call is recorded on the session's event stream.
|
|
7872
|
-
|
|
9183
|
+
While a model turn is running, a session-bound call is admitted by its
|
|
9184
|
+
effect: a read-effect call (a declared `effect: "read"`, or an advertised
|
|
9185
|
+
MCP tool whose server annotates it read-only) runs alongside the turn,
|
|
9186
|
+
reading the workspace as the turn has left it, and is recorded under its
|
|
9187
|
+
own per-call `turnId` so trajectories keep it apart from the turn's own
|
|
9188
|
+
calls; a write-effect call — including an undeclared tool, which counts
|
|
9189
|
+
as a write — returns `409 session_busy` until the turn finishes, because
|
|
9190
|
+
a running turn owns the workspace. When the
|
|
7873
9191
|
session's harness cwd cannot be materialized, a read-effect call runs
|
|
7874
9192
|
in a scratch workspace instead and the outcome carries
|
|
7875
9193
|
`scratchWorkspace: true`; a write-effect call fails with
|
|
@@ -7879,7 +9197,7 @@ A session can also be addressed by its continuation token: an optional
|
|
|
7879
9197
|
`continuationToken` (`<channelId>:<key>`, as `/v1/sessions` lists it;
|
|
7880
9198
|
mutually exclusive with `sessionId`). A token that maps to a live
|
|
7881
9199
|
session behaves exactly like passing that session's id — same ownership
|
|
7882
|
-
check, same
|
|
9200
|
+
check, same busy semantics, same event recording. A token with no
|
|
7883
9201
|
session behind it runs the call scratch-bound with the token's channel
|
|
7884
9202
|
id and continuation key as the call's session identity, so a deployment
|
|
7885
9203
|
whose tools resolve state from the continuation key can serve it with
|
|
@@ -8333,6 +9651,116 @@ sessions skip GitHub writes.
|
|
|
8333
9651
|
|
|
8334
9652
|
---
|
|
8335
9653
|
|
|
9654
|
+
Source: /docs/templates/agents-md.md
|
|
9655
|
+
|
|
9656
|
+
# Keep AGENTS.md current from last week's work
|
|
9657
|
+
|
|
9658
|
+
This agent runs every Monday at 09:00 UTC. It reads last week's merged
|
|
9659
|
+
pull requests and the Slack channels you list. When a durable rule
|
|
9660
|
+
showed up, `apply_agents` parks the write until a person approves it,
|
|
9661
|
+
then opens a draft follow-up pull request against the default branch.
|
|
9662
|
+
Chores and rules the file already states stay quiet.
|
|
9663
|
+
|
|
9664
|
+
This template is one model turn: read the week, then call
|
|
9665
|
+
`apply_agents`.
|
|
9666
|
+
|
|
9667
|
+
Init once per app. Pass a different repo and channel list each time.
|
|
9668
|
+
|
|
9669
|
+
## Scaffold
|
|
9670
|
+
|
|
9671
|
+
```bash
|
|
9672
|
+
npx @cursor/july init ./agents-md --template agents-md
|
|
9673
|
+
```
|
|
9674
|
+
|
|
9675
|
+
`init` asks which GitHub repo to watch and which Slack channels to
|
|
9676
|
+
read. Pass `--var repos=acme/api --var slack=#eng-api,#api-private`
|
|
9677
|
+
to skip the prompt. Use `--var slack=none` when Slack is out of
|
|
9678
|
+
scope.
|
|
9679
|
+
|
|
9680
|
+
The answers land in `agent/lib/config.ts`. Edit that file when the
|
|
9681
|
+
set changes.
|
|
9682
|
+
|
|
9683
|
+
## Connect GitHub and Slack
|
|
9684
|
+
|
|
9685
|
+
Connect GitHub in Cursor for that repository (Settings or
|
|
9686
|
+
[cursor.com/dashboard](https://cursor.com/dashboard)). Sign the host
|
|
9687
|
+
in:
|
|
9688
|
+
|
|
9689
|
+
```bash
|
|
9690
|
+
cd agents-md
|
|
9691
|
+
agent-sdk login
|
|
9692
|
+
```
|
|
9693
|
+
|
|
9694
|
+
`cursorAccount` mints a short-lived, repo-scoped credential with
|
|
9695
|
+
`contents-write` so the agent can open the follow-up PR. See the
|
|
9696
|
+
[GitHub guide](/docs/guides/github.md).
|
|
9697
|
+
|
|
9698
|
+
Create the Slack app and invite the bot to each listed channel,
|
|
9699
|
+
including private ones:
|
|
9700
|
+
|
|
9701
|
+
```bash
|
|
9702
|
+
agent-sdk slack create --dir .
|
|
9703
|
+
```
|
|
9704
|
+
|
|
9705
|
+
See the [Slack guide](/docs/guides/slack.md).
|
|
9706
|
+
|
|
9707
|
+
## First weekly run
|
|
9708
|
+
|
|
9709
|
+
```bash
|
|
9710
|
+
agent-sdk dev
|
|
9711
|
+
```
|
|
9712
|
+
|
|
9713
|
+
Schedules never fire under `dev`. Dispatch the same path production
|
|
9714
|
+
uses:
|
|
9715
|
+
|
|
9716
|
+
```bash
|
|
9717
|
+
curl -X POST http://127.0.0.1:3000/v1/dev/schedules/weekly
|
|
9718
|
+
```
|
|
9719
|
+
|
|
9720
|
+
The playground can run `/queue/run` too.
|
|
9721
|
+
|
|
9722
|
+
The host writes `week/prs.md`, `week/slack.md`, `week/window.txt`,
|
|
9723
|
+
and `week/tree/**.txt` snapshots of the current `AGENTS.md` files,
|
|
9724
|
+
then starts a model turn. The
|
|
9725
|
+
model calls `apply_agents` once. A skip finishes. An update parks in
|
|
9726
|
+
the playground until someone approves; then a draft PR opens against
|
|
9727
|
+
the repository default branch. Later updates to the same base stack
|
|
9728
|
+
on `agents-md/<base>`. Pull requests from those branches are ignored
|
|
9729
|
+
so the follow-up cannot feed the next week.
|
|
9730
|
+
|
|
9731
|
+
Chat replies stay in the playground. `apply_agents` records a skip,
|
|
9732
|
+
or parks the write. Approve there or with
|
|
9733
|
+
`POST /v1/session/:id/approvals/:callId`.
|
|
9734
|
+
|
|
9735
|
+
## What to edit
|
|
9736
|
+
|
|
9737
|
+
- `agent/lib/config.ts`: repo and Slack channels
|
|
9738
|
+
- `agent/instructions.md`: what counts as an update
|
|
9739
|
+
- `evals/agents.eval.ts`: smoke cases
|
|
9740
|
+
|
|
9741
|
+
## Evals
|
|
9742
|
+
|
|
9743
|
+
Four smoke cases: a lint-only week, a billing rule that parks the
|
|
9744
|
+
write, the same rule already in `AGENTS.md`, and an empty week.
|
|
9745
|
+
|
|
9746
|
+
```bash
|
|
9747
|
+
agent-sdk eval
|
|
9748
|
+
```
|
|
9749
|
+
|
|
9750
|
+
Eval sessions skip the GitHub write.
|
|
9751
|
+
|
|
9752
|
+
## Deploy
|
|
9753
|
+
|
|
9754
|
+
```bash
|
|
9755
|
+
agent-sdk deploy
|
|
9756
|
+
```
|
|
9757
|
+
|
|
9758
|
+
Run it from the agent's git checkout. The command infers repository,
|
|
9759
|
+
ref, path, and slug. Production fires the Monday cron. The deployment
|
|
9760
|
+
uses the team's Cursor GitHub connection for the watched repo.
|
|
9761
|
+
|
|
9762
|
+
---
|
|
9763
|
+
|
|
8336
9764
|
Source: /docs/templates/code-wiki.md
|
|
8337
9765
|
|
|
8338
9766
|
# Keep a code wiki current after merge
|
|
@@ -8939,7 +10367,7 @@ not on `PATH`, use `npx @cursor/july`.
|
|
|
8939
10367
|
| What you see | What to do |
|
|
8940
10368
|
| --- | --- |
|
|
8941
10369
|
| `409` on a follow-up message | Refresh the `continuationToken` or confirm the session is a chat session. Task sessions do not accept follow-ups. |
|
|
8942
|
-
| `409 session_busy` on `call --session` | Wait for the
|
|
10370
|
+
| `409 session_busy` on `call --session` | The tool is a write (or declares no effect) and a model turn is running. Wait for the turn to finish, omit `--session` for a one-off call, or declare `effect: "read"` on a tool that only reads so it runs alongside turns. |
|
|
8943
10371
|
| `403` on stream or follow-up | Use the same auth identity that created the session. Off localhost, pass `--bearer-token` and send it on every request. |
|
|
8944
10372
|
| Works on localhost; blocked through a tunnel or LAN | Default auth allows only direct loopback callers. Share the host with `--bearer-token <secret>` (or authored `bearerAuth`). Use `--allow-anonymous` only on a trusted private network. |
|
|
8945
10373
|
| A channel route fails to compile with a schema type error | `GET` routes need a Zod `querySchema`. `POST` / `PUT` / `PATCH` need a Zod `bodySchema`. Use `z.object({})` or `z.unknown()` for open shapes. |
|
|
@@ -8967,7 +10395,7 @@ not on `PATH`, use `npx @cursor/july`.
|
|
|
8967
10395
|
| What you see | What to do |
|
|
8968
10396
|
| --- | --- |
|
|
8969
10397
|
| `must be defineConnection({ url, oauth: true })` | The connection file needs `oauth: true`, or you passed the wrong connection name to `agent-sdk mcp oauth`. |
|
|
8970
|
-
| Local auth works; hosted calls unauthorized |
|
|
10398
|
+
| Local auth works; hosted calls unauthorized | Finish Connect on the hosted engine, or run `agent-sdk mcp oauth <name> --store`. Confirm names with `agent-sdk secrets list <slug>`, then redeploy. |
|
|
8971
10399
|
| Model asks for `mcp_auth` or IDE MCP for a connector it already has | Attached MCP is behind meta-tools. Set `advertiseTools: true` for named tools on local turns, or call it from a host tool via `ctx.host.mcp`. |
|
|
8972
10400
|
|
|
8973
10401
|
See [Host MCP OAuth](/docs/guides/mcp-oauth.md) and
|