@cursor/july 0.1.1
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 +290 -0
- package/LICENSE.md +3 -0
- package/README.md +1233 -0
- package/dist/ab.d.ts +290 -0
- package/dist/ab.d.ts.map +1 -0
- package/dist/ab.js +386 -0
- package/dist/bin/agent-serve.d.ts +15 -0
- package/dist/bin/agent-serve.d.ts.map +1 -0
- package/dist/bin/agent-serve.js +754 -0
- package/dist/channels/github/api.d.ts +101 -0
- package/dist/channels/github/api.d.ts.map +1 -0
- package/dist/channels/github/api.js +229 -0
- package/dist/channels/github/auth.d.ts +67 -0
- package/dist/channels/github/auth.d.ts.map +1 -0
- package/dist/channels/github/auth.js +239 -0
- package/dist/channels/github/binding.d.ts +73 -0
- package/dist/channels/github/binding.d.ts.map +1 -0
- package/dist/channels/github/binding.js +114 -0
- package/dist/channels/github/defaults.d.ts +24 -0
- package/dist/channels/github/defaults.d.ts.map +1 -0
- package/dist/channels/github/defaults.js +100 -0
- package/dist/channels/github/events.d.ts +18 -0
- package/dist/channels/github/events.d.ts.map +1 -0
- package/dist/channels/github/events.js +59 -0
- package/dist/channels/github/forward.d.ts +137 -0
- package/dist/channels/github/forward.d.ts.map +1 -0
- package/dist/channels/github/forward.js +271 -0
- package/dist/channels/github/github-channel.d.ts +127 -0
- package/dist/channels/github/github-channel.d.ts.map +1 -0
- package/dist/channels/github/github-channel.js +1688 -0
- package/dist/channels/github/index.d.ts +52 -0
- package/dist/channels/github/index.d.ts.map +1 -0
- package/dist/channels/github/index.js +51 -0
- package/dist/channels/github/replay.d.ts +115 -0
- package/dist/channels/github/replay.d.ts.map +1 -0
- package/dist/channels/github/replay.js +282 -0
- package/dist/channels/github/state.d.ts +26 -0
- package/dist/channels/github/state.d.ts.map +1 -0
- package/dist/channels/github/state.js +50 -0
- package/dist/channels/github/types.d.ts +385 -0
- package/dist/channels/github/types.d.ts.map +1 -0
- package/dist/channels/github/types.js +1 -0
- package/dist/channels/slack/api.d.ts +117 -0
- package/dist/channels/slack/api.d.ts.map +1 -0
- package/dist/channels/slack/api.js +437 -0
- package/dist/channels/slack/approvals.d.ts +32 -0
- package/dist/channels/slack/approvals.d.ts.map +1 -0
- package/dist/channels/slack/approvals.js +157 -0
- package/dist/channels/slack/assistant.d.ts +23 -0
- package/dist/channels/slack/assistant.d.ts.map +1 -0
- package/dist/channels/slack/assistant.js +67 -0
- package/dist/channels/slack/auth.d.ts +40 -0
- package/dist/channels/slack/auth.d.ts.map +1 -0
- package/dist/channels/slack/auth.js +71 -0
- package/dist/channels/slack/channel-watch.d.ts +46 -0
- package/dist/channels/slack/channel-watch.d.ts.map +1 -0
- package/dist/channels/slack/channel-watch.js +180 -0
- package/dist/channels/slack/constants.d.ts +17 -0
- package/dist/channels/slack/constants.d.ts.map +1 -0
- package/dist/channels/slack/constants.js +27 -0
- package/dist/channels/slack/credentials.d.ts +35 -0
- package/dist/channels/slack/credentials.d.ts.map +1 -0
- package/dist/channels/slack/credentials.js +88 -0
- package/dist/channels/slack/cursor-account.d.ts +76 -0
- package/dist/channels/slack/cursor-account.d.ts.map +1 -0
- package/dist/channels/slack/cursor-account.js +88 -0
- package/dist/channels/slack/defaults.d.ts +32 -0
- package/dist/channels/slack/defaults.d.ts.map +1 -0
- package/dist/channels/slack/defaults.js +380 -0
- package/dist/channels/slack/dispatch.d.ts +43 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -0
- package/dist/channels/slack/dispatch.js +185 -0
- package/dist/channels/slack/doctor.d.ts +24 -0
- package/dist/channels/slack/doctor.d.ts.map +1 -0
- package/dist/channels/slack/doctor.js +115 -0
- package/dist/channels/slack/event-id-dedupe.d.ts +10 -0
- package/dist/channels/slack/event-id-dedupe.d.ts.map +1 -0
- package/dist/channels/slack/event-id-dedupe.js +23 -0
- package/dist/channels/slack/inbound.d.ts +41 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -0
- package/dist/channels/slack/inbound.js +166 -0
- package/dist/channels/slack/index.d.ts +45 -0
- package/dist/channels/slack/index.d.ts.map +1 -0
- package/dist/channels/slack/index.js +44 -0
- package/dist/channels/slack/init.d.ts +32 -0
- package/dist/channels/slack/init.d.ts.map +1 -0
- package/dist/channels/slack/init.js +82 -0
- package/dist/channels/slack/interactive.d.ts +19 -0
- package/dist/channels/slack/interactive.d.ts.map +1 -0
- package/dist/channels/slack/interactive.js +142 -0
- package/dist/channels/slack/links.d.ts +10 -0
- package/dist/channels/slack/links.d.ts.map +1 -0
- package/dist/channels/slack/links.js +19 -0
- package/dist/channels/slack/live-delivery.d.ts +75 -0
- package/dist/channels/slack/live-delivery.d.ts.map +1 -0
- package/dist/channels/slack/live-delivery.js +143 -0
- package/dist/channels/slack/log.d.ts +25 -0
- package/dist/channels/slack/log.d.ts.map +1 -0
- package/dist/channels/slack/log.js +47 -0
- package/dist/channels/slack/manifest.d.ts +57 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -0
- package/dist/channels/slack/manifest.js +110 -0
- package/dist/channels/slack/pr-ref.d.ts +18 -0
- package/dist/channels/slack/pr-ref.d.ts.map +1 -0
- package/dist/channels/slack/pr-ref.js +36 -0
- package/dist/channels/slack/setup.d.ts +14 -0
- package/dist/channels/slack/setup.d.ts.map +1 -0
- package/dist/channels/slack/setup.js +86 -0
- package/dist/channels/slack/slack-channel.d.ts +15 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -0
- package/dist/channels/slack/slack-channel.js +281 -0
- package/dist/channels/slack/socket-mode.d.ts +26 -0
- package/dist/channels/slack/socket-mode.d.ts.map +1 -0
- package/dist/channels/slack/socket-mode.js +116 -0
- package/dist/channels/slack/stream.d.ts +26 -0
- package/dist/channels/slack/stream.d.ts.map +1 -0
- package/dist/channels/slack/stream.js +39 -0
- package/dist/channels/slack/tool-approval-events.d.ts +33 -0
- package/dist/channels/slack/tool-approval-events.d.ts.map +1 -0
- package/dist/channels/slack/tool-approval-events.js +129 -0
- package/dist/channels/slack/types.d.ts +326 -0
- package/dist/channels/slack/types.d.ts.map +1 -0
- package/dist/channels/slack/types.js +4 -0
- package/dist/channels.d.ts +116 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/channels.js +294 -0
- package/dist/connections.d.ts +75 -0
- package/dist/connections.d.ts.map +1 -0
- package/dist/connections.js +75 -0
- package/dist/docs/404.html +22 -0
- package/dist/docs/ab.html +87 -0
- package/dist/docs/assets/ab.md.COdXkces.js +63 -0
- package/dist/docs/assets/ab.md.COdXkces.lean.js +1 -0
- package/dist/docs/assets/app.DqfFEmJd.js +1 -0
- package/dist/docs/assets/building-with-agents.md.CJCtZCyi.js +13 -0
- package/dist/docs/assets/building-with-agents.md.CJCtZCyi.lean.js +1 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +1 -0
- package/dist/docs/assets/chunks/VPLocalSearchBox.BaLEdS15.js +9 -0
- package/dist/docs/assets/chunks/framework.CAZyNGu9.js +19 -0
- package/dist/docs/assets/chunks/theme.CZRvu_0q.js +2 -0
- package/dist/docs/assets/concepts.md.Cfb9b-k1.js +4 -0
- package/dist/docs/assets/concepts.md.Cfb9b-k1.lean.js +1 -0
- package/dist/docs/assets/deployment.md.Dx1TYNk5.js +14 -0
- package/dist/docs/assets/deployment.md.Dx1TYNk5.lean.js +1 -0
- package/dist/docs/assets/evals.md.DPZ_MAnI.js +85 -0
- package/dist/docs/assets/evals.md.DPZ_MAnI.lean.js +1 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.js +10 -0
- package/dist/docs/assets/guides_agent-to-agent.md.CrtrsySy.lean.js +1 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.js +9 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CYlNMTNp.lean.js +1 -0
- package/dist/docs/assets/guides_github.md.DwbKhCeS.js +20 -0
- package/dist/docs/assets/guides_github.md.DwbKhCeS.lean.js +1 -0
- package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.js +28 -0
- package/dist/docs/assets/guides_human-in-the-loop.md.Dvuctx7s.lean.js +1 -0
- package/dist/docs/assets/guides_slack.md.bv41fHfW.js +37 -0
- package/dist/docs/assets/guides_slack.md.bv41fHfW.lean.js +1 -0
- package/dist/docs/assets/guides_webhooks.md.hFTik3lf.js +50 -0
- package/dist/docs/assets/guides_webhooks.md.hFTik3lf.lean.js +1 -0
- package/dist/docs/assets/hillclimbing.md.DHNast08.js +4 -0
- package/dist/docs/assets/hillclimbing.md.DHNast08.lean.js +1 -0
- package/dist/docs/assets/index.md.BPKcj5AI.js +20 -0
- package/dist/docs/assets/index.md.BPKcj5AI.lean.js +1 -0
- package/dist/docs/assets/inter-italic-cyrillic-ext.r48I6akx.woff2 +0 -0
- package/dist/docs/assets/inter-italic-cyrillic.By2_1cv3.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek-ext.1u6EdAuj.woff2 +0 -0
- package/dist/docs/assets/inter-italic-greek.DJ8dCoTZ.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin-ext.CN1xVJS-.woff2 +0 -0
- package/dist/docs/assets/inter-italic-latin.C2AdPX0b.woff2 +0 -0
- package/dist/docs/assets/inter-italic-vietnamese.BSbpV94h.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic-ext.BBPuwvHQ.woff2 +0 -0
- package/dist/docs/assets/inter-roman-cyrillic.C5lxZ8CY.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek-ext.CqjqNYQ-.woff2 +0 -0
- package/dist/docs/assets/inter-roman-greek.BBVDIX6e.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin-ext.4ZJIpNVo.woff2 +0 -0
- package/dist/docs/assets/inter-roman-latin.Di8DUHzh.woff2 +0 -0
- package/dist/docs/assets/inter-roman-vietnamese.BjW4sHH5.woff2 +0 -0
- package/dist/docs/assets/quickstart.md.tVPiGK_L.js +35 -0
- package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +1 -0
- package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.js +34 -0
- package/dist/docs/assets/reference_agent-config.md.Bpd7HQwf.lean.js +1 -0
- package/dist/docs/assets/reference_channels.md.D7JTR03W.js +53 -0
- package/dist/docs/assets/reference_channels.md.D7JTR03W.lean.js +1 -0
- package/dist/docs/assets/reference_cli.md.DA730zCu.js +33 -0
- package/dist/docs/assets/reference_cli.md.DA730zCu.lean.js +1 -0
- package/dist/docs/assets/reference_connections.md.C3vNH_DE.js +26 -0
- package/dist/docs/assets/reference_connections.md.C3vNH_DE.lean.js +1 -0
- package/dist/docs/assets/reference_hooks.md.BCEc3MyM.js +12 -0
- package/dist/docs/assets/reference_hooks.md.BCEc3MyM.lean.js +1 -0
- package/dist/docs/assets/reference_http-api.md.DBAahtdz.js +11 -0
- package/dist/docs/assets/reference_http-api.md.DBAahtdz.lean.js +1 -0
- package/dist/docs/assets/reference_instructions.md.BC05LEQ8.js +12 -0
- package/dist/docs/assets/reference_instructions.md.BC05LEQ8.lean.js +1 -0
- package/dist/docs/assets/reference_playground.md.CPZhfYaO.js +1 -0
- package/dist/docs/assets/reference_playground.md.CPZhfYaO.lean.js +1 -0
- package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.js +31 -0
- package/dist/docs/assets/reference_project-layout.md.Bd_CKtNS.lean.js +1 -0
- package/dist/docs/assets/reference_schedules.md.D7qijxLk.js +82 -0
- package/dist/docs/assets/reference_schedules.md.D7qijxLk.lean.js +1 -0
- package/dist/docs/assets/reference_sessions.md.DdjWabnN.js +8 -0
- package/dist/docs/assets/reference_sessions.md.DdjWabnN.lean.js +1 -0
- package/dist/docs/assets/reference_skills.md.VQnlBT3Q.js +15 -0
- package/dist/docs/assets/reference_skills.md.VQnlBT3Q.lean.js +1 -0
- package/dist/docs/assets/reference_subagents.md.CIRAVcPK.js +10 -0
- package/dist/docs/assets/reference_subagents.md.CIRAVcPK.lean.js +1 -0
- package/dist/docs/assets/reference_tools.md.DF5kwlt0.js +46 -0
- package/dist/docs/assets/reference_tools.md.DF5kwlt0.lean.js +1 -0
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +1 -0
- package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +1 -0
- package/dist/docs/assets/style.Bj-IXa5W.css +1 -0
- package/dist/docs/assets/troubleshooting.md.DkovjS7h.js +1 -0
- package/dist/docs/assets/troubleshooting.md.DkovjS7h.lean.js +1 -0
- package/dist/docs/building-with-agents.html +37 -0
- package/dist/docs/concepts.html +28 -0
- package/dist/docs/deployment.html +38 -0
- package/dist/docs/evals.html +109 -0
- package/dist/docs/guides/agent-to-agent.html +34 -0
- package/dist/docs/guides/cloud-runtime.html +33 -0
- package/dist/docs/guides/github.html +44 -0
- package/dist/docs/guides/human-in-the-loop.html +52 -0
- package/dist/docs/guides/slack.html +61 -0
- package/dist/docs/guides/webhooks.html +74 -0
- package/dist/docs/hashmap.json +1 -0
- package/dist/docs/hillclimbing.html +28 -0
- package/dist/docs/index.html +44 -0
- package/dist/docs/quickstart.html +59 -0
- package/dist/docs/reference/agent-config.html +58 -0
- package/dist/docs/reference/channels.html +77 -0
- package/dist/docs/reference/cli.html +57 -0
- package/dist/docs/reference/connections.html +50 -0
- package/dist/docs/reference/hooks.html +36 -0
- package/dist/docs/reference/http-api.html +35 -0
- package/dist/docs/reference/instructions.html +36 -0
- package/dist/docs/reference/playground.html +25 -0
- package/dist/docs/reference/project-layout.html +55 -0
- package/dist/docs/reference/schedules.html +106 -0
- package/dist/docs/reference/sessions.html +32 -0
- package/dist/docs/reference/skills.html +39 -0
- package/dist/docs/reference/subagents.html +34 -0
- package/dist/docs/reference/tools.html +70 -0
- package/dist/docs/scaffolding-agents.html +25 -0
- package/dist/docs/troubleshooting.html +25 -0
- package/dist/docs/vp-icons.css +0 -0
- package/dist/evals.d.ts +312 -0
- package/dist/evals.d.ts.map +1 -0
- package/dist/evals.js +254 -0
- package/dist/hooks.d.ts +23 -0
- package/dist/hooks.d.ts.map +1 -0
- package/dist/hooks.js +23 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +101 -0
- package/dist/internal/ab-collector.d.ts +42 -0
- package/dist/internal/ab-collector.d.ts.map +1 -0
- package/dist/internal/ab-collector.js +153 -0
- package/dist/internal/ab-fold.d.ts +36 -0
- package/dist/internal/ab-fold.d.ts.map +1 -0
- package/dist/internal/ab-fold.js +171 -0
- package/dist/internal/ab-snapshot.d.ts +70 -0
- package/dist/internal/ab-snapshot.d.ts.map +1 -0
- package/dist/internal/ab-snapshot.js +208 -0
- package/dist/internal/approval-gate.d.ts +34 -0
- package/dist/internal/approval-gate.d.ts.map +1 -0
- package/dist/internal/approval-gate.js +85 -0
- package/dist/internal/auth.d.ts +8 -0
- package/dist/internal/auth.d.ts.map +1 -0
- package/dist/internal/auth.js +27 -0
- package/dist/internal/authored-alias-hooks.d.ts +31 -0
- package/dist/internal/authored-alias-hooks.d.ts.map +1 -0
- package/dist/internal/authored-alias-hooks.js +43 -0
- package/dist/internal/authored-loaders.d.ts +25 -0
- package/dist/internal/authored-loaders.d.ts.map +1 -0
- package/dist/internal/authored-loaders.js +129 -0
- package/dist/internal/bounded-int.d.ts +11 -0
- package/dist/internal/bounded-int.d.ts.map +1 -0
- package/dist/internal/bounded-int.js +13 -0
- package/dist/internal/brand.d.ts +6 -0
- package/dist/internal/brand.d.ts.map +1 -0
- package/dist/internal/brand.js +17 -0
- package/dist/internal/chat-client.d.ts +19 -0
- package/dist/internal/chat-client.d.ts.map +1 -0
- package/dist/internal/chat-client.js +327 -0
- package/dist/internal/cli-ax.d.ts +58 -0
- package/dist/internal/cli-ax.d.ts.map +1 -0
- package/dist/internal/cli-ax.js +345 -0
- package/dist/internal/cli-cursor.d.ts +20 -0
- package/dist/internal/cli-cursor.d.ts.map +1 -0
- package/dist/internal/cli-cursor.js +227 -0
- package/dist/internal/cli-github.d.ts +47 -0
- package/dist/internal/cli-github.d.ts.map +1 -0
- package/dist/internal/cli-github.js +644 -0
- package/dist/internal/cli-slack.d.ts +18 -0
- package/dist/internal/cli-slack.d.ts.map +1 -0
- package/dist/internal/cli-slack.js +144 -0
- package/dist/internal/cloud-merge.d.ts +23 -0
- package/dist/internal/cloud-merge.d.ts.map +1 -0
- package/dist/internal/cloud-merge.js +28 -0
- package/dist/internal/cron.d.ts +17 -0
- package/dist/internal/cron.d.ts.map +1 -0
- package/dist/internal/cron.js +60 -0
- package/dist/internal/cursor/account-mcp.d.ts +95 -0
- package/dist/internal/cursor/account-mcp.d.ts.map +1 -0
- package/dist/internal/cursor/account-mcp.js +342 -0
- package/dist/internal/cursor/backend-client.d.ts +118 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -0
- package/dist/internal/cursor/backend-client.js +477 -0
- package/dist/internal/cursor/credentials.d.ts +112 -0
- package/dist/internal/cursor/credentials.d.ts.map +1 -0
- package/dist/internal/cursor/credentials.js +329 -0
- package/dist/internal/cursor-event-relay.d.ts +111 -0
- package/dist/internal/cursor-event-relay.d.ts.map +1 -0
- package/dist/internal/cursor-event-relay.js +181 -0
- package/dist/internal/cursor-relay-core.d.ts +113 -0
- package/dist/internal/cursor-relay-core.d.ts.map +1 -0
- package/dist/internal/cursor-relay-core.js +364 -0
- package/dist/internal/cursor-slack-relay.d.ts +94 -0
- package/dist/internal/cursor-slack-relay.d.ts.map +1 -0
- package/dist/internal/cursor-slack-relay.js +171 -0
- package/dist/internal/describe-error.d.ts +3 -0
- package/dist/internal/describe-error.d.ts.map +1 -0
- package/dist/internal/describe-error.js +4 -0
- package/dist/internal/dev-mode.d.ts +13 -0
- package/dist/internal/dev-mode.d.ts.map +1 -0
- package/dist/internal/dev-mode.js +17 -0
- package/dist/internal/discovery.d.ts +55 -0
- package/dist/internal/discovery.d.ts.map +1 -0
- package/dist/internal/discovery.js +1427 -0
- package/dist/internal/distribution.d.ts +47 -0
- package/dist/internal/distribution.d.ts.map +1 -0
- package/dist/internal/distribution.js +82 -0
- package/dist/internal/docs-site.d.ts +28 -0
- package/dist/internal/docs-site.d.ts.map +1 -0
- package/dist/internal/docs-site.js +169 -0
- package/dist/internal/duration.d.ts +8 -0
- package/dist/internal/duration.d.ts.map +1 -0
- package/dist/internal/duration.js +35 -0
- package/dist/internal/eval-run-store.d.ts +81 -0
- package/dist/internal/eval-run-store.d.ts.map +1 -0
- package/dist/internal/eval-run-store.js +396 -0
- package/dist/internal/eval-runner.d.ts +76 -0
- package/dist/internal/eval-runner.d.ts.map +1 -0
- package/dist/internal/eval-runner.js +336 -0
- package/dist/internal/event-log.d.ts +51 -0
- package/dist/internal/event-log.d.ts.map +1 -0
- package/dist/internal/event-log.js +167 -0
- package/dist/internal/event-mapper.d.ts +46 -0
- package/dist/internal/event-mapper.d.ts.map +1 -0
- package/dist/internal/event-mapper.js +226 -0
- package/dist/internal/frontmatter.d.ts +12 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +61 -0
- package/dist/internal/github-fanout.d.ts +32 -0
- package/dist/internal/github-fanout.d.ts.map +1 -0
- package/dist/internal/github-fanout.js +122 -0
- package/dist/internal/handleAgentServeTrigger.d.ts +59 -0
- package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -0
- package/dist/internal/handleAgentServeTrigger.js +115 -0
- package/dist/internal/host-platforms.d.ts +40 -0
- package/dist/internal/host-platforms.d.ts.map +1 -0
- package/dist/internal/host-platforms.js +113 -0
- package/dist/internal/http-channel.d.ts +54 -0
- package/dist/internal/http-channel.d.ts.map +1 -0
- package/dist/internal/http-channel.js +382 -0
- package/dist/internal/ids.d.ts +19 -0
- package/dist/internal/ids.d.ts.map +1 -0
- package/dist/internal/ids.js +34 -0
- package/dist/internal/init-project.d.ts +22 -0
- package/dist/internal/init-project.d.ts.map +1 -0
- package/dist/internal/init-project.js +125 -0
- package/dist/internal/json-dir-store.d.ts +32 -0
- package/dist/internal/json-dir-store.d.ts.map +1 -0
- package/dist/internal/json-dir-store.js +100 -0
- package/dist/internal/json-schema.d.ts +18 -0
- package/dist/internal/json-schema.d.ts.map +1 -0
- package/dist/internal/json-schema.js +53 -0
- package/dist/internal/log-ring.d.ts +23 -0
- package/dist/internal/log-ring.d.ts.map +1 -0
- package/dist/internal/log-ring.js +42 -0
- package/dist/internal/map-pool.d.ts +6 -0
- package/dist/internal/map-pool.d.ts.map +1 -0
- package/dist/internal/map-pool.js +41 -0
- package/dist/internal/mcp-endpoint.d.ts +59 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -0
- package/dist/internal/mcp-endpoint.js +355 -0
- package/dist/internal/mcp-host.d.ts +28 -0
- package/dist/internal/mcp-host.d.ts.map +1 -0
- package/dist/internal/mcp-host.js +162 -0
- package/dist/internal/net.d.ts +5 -0
- package/dist/internal/net.d.ts.map +1 -0
- package/dist/internal/net.js +30 -0
- package/dist/internal/peer-connections.d.ts +44 -0
- package/dist/internal/peer-connections.d.ts.map +1 -0
- package/dist/internal/peer-connections.js +67 -0
- package/dist/internal/playground/static.d.ts +35 -0
- package/dist/internal/playground/static.d.ts.map +1 -0
- package/dist/internal/playground/static.js +162 -0
- package/dist/internal/playground/toolchain.d.ts +57 -0
- package/dist/internal/playground/toolchain.d.ts.map +1 -0
- package/dist/internal/playground/toolchain.js +226 -0
- package/dist/internal/reminder-runner.d.ts +62 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -0
- package/dist/internal/reminder-runner.js +582 -0
- package/dist/internal/reminder-schedule.d.ts +22 -0
- package/dist/internal/reminder-schedule.d.ts.map +1 -0
- package/dist/internal/reminder-schedule.js +82 -0
- package/dist/internal/reminder-store.d.ts +58 -0
- package/dist/internal/reminder-store.d.ts.map +1 -0
- package/dist/internal/reminder-store.js +106 -0
- package/dist/internal/resolved-connections.d.ts +41 -0
- package/dist/internal/resolved-connections.d.ts.map +1 -0
- package/dist/internal/resolved-connections.js +73 -0
- package/dist/internal/review-comments.d.ts +50 -0
- package/dist/internal/review-comments.d.ts.map +1 -0
- package/dist/internal/review-comments.js +194 -0
- package/dist/internal/router.d.ts +33 -0
- package/dist/internal/router.d.ts.map +1 -0
- package/dist/internal/router.js +93 -0
- package/dist/internal/run-client.d.ts +49 -0
- package/dist/internal/run-client.d.ts.map +1 -0
- package/dist/internal/run-client.js +208 -0
- package/dist/internal/schedule-runner.d.ts +31 -0
- package/dist/internal/schedule-runner.d.ts.map +1 -0
- package/dist/internal/schedule-runner.js +126 -0
- package/dist/internal/sdk-runner.d.ts +41 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -0
- package/dist/internal/sdk-runner.js +221 -0
- package/dist/internal/server.d.ts +25 -0
- package/dist/internal/server.d.ts.map +1 -0
- package/dist/internal/server.js +1374 -0
- package/dist/internal/session-engine.d.ts +287 -0
- package/dist/internal/session-engine.d.ts.map +1 -0
- package/dist/internal/session-engine.js +1377 -0
- package/dist/internal/session-store.d.ts +29 -0
- package/dist/internal/session-store.d.ts.map +1 -0
- package/dist/internal/session-store.js +140 -0
- package/dist/internal/session-urls.d.ts +10 -0
- package/dist/internal/session-urls.d.ts.map +1 -0
- package/dist/internal/session-urls.js +25 -0
- package/dist/internal/stream-progress.d.ts +14 -0
- package/dist/internal/stream-progress.d.ts.map +1 -0
- package/dist/internal/stream-progress.js +195 -0
- package/dist/internal/tool-result.d.ts +20 -0
- package/dist/internal/tool-result.d.ts.map +1 -0
- package/dist/internal/tool-result.js +45 -0
- package/dist/internal/trajectory.d.ts +56 -0
- package/dist/internal/trajectory.d.ts.map +1 -0
- package/dist/internal/trajectory.js +248 -0
- package/dist/internal/workspace.d.ts +42 -0
- package/dist/internal/workspace.d.ts.map +1 -0
- package/dist/internal/workspace.js +199 -0
- package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
- package/dist/playground/assets/index-1K-hG-7p.css +1 -0
- package/dist/playground/assets/index-FlWjhg3x.js +79 -0
- package/dist/playground/index.html +17 -0
- package/dist/reminders.d.ts +23 -0
- package/dist/reminders.d.ts.map +1 -0
- package/dist/reminders.js +22 -0
- package/dist/schedules.d.ts +23 -0
- package/dist/schedules.d.ts.map +1 -0
- package/dist/schedules.js +26 -0
- package/dist/skills.d.ts +22 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +22 -0
- package/dist/tools.d.ts +92 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +68 -0
- package/dist/types.d.ts +1609 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +80 -0
- package/docs/README.md +174 -0
- package/docs/ab.md +355 -0
- package/docs/building-with-agents.md +129 -0
- package/docs/concepts.md +177 -0
- package/docs/deployment.md +218 -0
- package/docs/evals.md +383 -0
- package/docs/guides/agent-to-agent.md +118 -0
- package/docs/guides/cloud-runtime.md +122 -0
- package/docs/guides/github.md +172 -0
- package/docs/guides/human-in-the-loop.md +131 -0
- package/docs/guides/slack.md +245 -0
- package/docs/guides/webhooks.md +201 -0
- package/docs/hillclimbing.md +93 -0
- package/docs/quickstart.md +183 -0
- package/docs/reference/agent-config.md +154 -0
- package/docs/reference/channels.md +206 -0
- package/docs/reference/cli.md +257 -0
- package/docs/reference/connections.md +137 -0
- package/docs/reference/hooks.md +74 -0
- package/docs/reference/http-api.md +219 -0
- package/docs/reference/instructions.md +77 -0
- package/docs/reference/playground.md +67 -0
- package/docs/reference/project-layout.md +119 -0
- package/docs/reference/schedules.md +220 -0
- package/docs/reference/sessions.md +165 -0
- package/docs/reference/skills.md +79 -0
- package/docs/reference/subagents.md +74 -0
- package/docs/reference/tools.md +203 -0
- package/docs/scaffolding-agents.md +113 -0
- package/docs/troubleshooting.md +95 -0
- package/package.json +185 -0
- package/skills/ab/SKILL.md +139 -0
- package/skills/create-agent/SKILL.md +219 -0
- package/skills/debug/SKILL.md +71 -0
- package/skills/evals/SKILL.md +217 -0
- package/skills/framework-map/SKILL.md +142 -0
- package/skills/github/SKILL.md +144 -0
- package/skills/hillclimb/SKILL.md +162 -0
- package/skills/setup-slack/SKILL.md +180 -0
- package/src/ab.ts +675 -0
- package/src/bin/agent-serve.ts +949 -0
- package/src/channels/github/api.ts +374 -0
- package/src/channels/github/auth.ts +275 -0
- package/src/channels/github/binding.ts +223 -0
- package/src/channels/github/defaults.ts +111 -0
- package/src/channels/github/events.ts +72 -0
- package/src/channels/github/forward.ts +381 -0
- package/src/channels/github/github-channel.ts +2270 -0
- package/src/channels/github/index.ts +52 -0
- package/src/channels/github/replay.ts +387 -0
- package/src/channels/github/state.ts +95 -0
- package/src/channels/github/types.ts +490 -0
- package/src/channels/slack/api.ts +580 -0
- package/src/channels/slack/approvals.ts +198 -0
- package/src/channels/slack/assistant.ts +76 -0
- package/src/channels/slack/auth.ts +112 -0
- package/src/channels/slack/channel-watch.ts +219 -0
- package/src/channels/slack/constants.ts +30 -0
- package/src/channels/slack/credentials.ts +90 -0
- package/src/channels/slack/cursor-account.ts +166 -0
- package/src/channels/slack/defaults.ts +450 -0
- package/src/channels/slack/dispatch.ts +293 -0
- package/src/channels/slack/doctor.ts +135 -0
- package/src/channels/slack/event-id-dedupe.ts +30 -0
- package/src/channels/slack/inbound.ts +219 -0
- package/src/channels/slack/index.ts +45 -0
- package/src/channels/slack/init.ts +133 -0
- package/src/channels/slack/interactive.ts +194 -0
- package/src/channels/slack/links.ts +23 -0
- package/src/channels/slack/live-delivery.ts +201 -0
- package/src/channels/slack/log.ts +83 -0
- package/src/channels/slack/manifest.ts +179 -0
- package/src/channels/slack/pr-ref.ts +50 -0
- package/src/channels/slack/setup.ts +97 -0
- package/src/channels/slack/slack-channel.ts +374 -0
- package/src/channels/slack/socket-mode.ts +154 -0
- package/src/channels/slack/stream.ts +63 -0
- package/src/channels/slack/tool-approval-events.ts +136 -0
- package/src/channels/slack/types.ts +349 -0
- package/src/channels.ts +385 -0
- package/src/connections.ts +89 -0
- package/src/evals.ts +501 -0
- package/src/hooks.ts +33 -0
- package/src/index.ts +184 -0
- package/src/internal/ab-collector.ts +210 -0
- package/src/internal/ab-fold.ts +228 -0
- package/src/internal/ab-snapshot.ts +335 -0
- package/src/internal/approval-gate.ts +115 -0
- package/src/internal/auth.ts +39 -0
- package/src/internal/authored-alias-hooks.ts +69 -0
- package/src/internal/authored-loaders.ts +138 -0
- package/src/internal/bounded-int.ts +27 -0
- package/src/internal/brand.ts +29 -0
- package/src/internal/chat-client.ts +407 -0
- package/src/internal/cli-ax.ts +474 -0
- package/src/internal/cli-cursor.ts +275 -0
- package/src/internal/cli-github.ts +849 -0
- package/src/internal/cli-slack.ts +175 -0
- package/src/internal/cloud-merge.ts +41 -0
- package/src/internal/cron.ts +78 -0
- package/src/internal/cursor/account-mcp.ts +472 -0
- package/src/internal/cursor/backend-client.ts +614 -0
- package/src/internal/cursor/credentials.ts +401 -0
- package/src/internal/cursor-event-relay.ts +281 -0
- package/src/internal/cursor-relay-core.ts +444 -0
- package/src/internal/cursor-slack-relay.ts +242 -0
- package/src/internal/describe-error.ts +4 -0
- package/src/internal/dev-mode.ts +20 -0
- package/src/internal/discovery.ts +1709 -0
- package/src/internal/distribution.ts +97 -0
- package/src/internal/docs-site.ts +191 -0
- package/src/internal/duration.ts +39 -0
- package/src/internal/eval-run-store.ts +519 -0
- package/src/internal/eval-runner.ts +522 -0
- package/src/internal/event-log.ts +175 -0
- package/src/internal/event-mapper.ts +272 -0
- package/src/internal/frontmatter.ts +77 -0
- package/src/internal/github-fanout.ts +159 -0
- package/src/internal/handleAgentServeTrigger.ts +156 -0
- package/src/internal/host-platforms.ts +144 -0
- package/src/internal/http-channel.ts +546 -0
- package/src/internal/ids.ts +44 -0
- package/src/internal/init-project.ts +165 -0
- package/src/internal/json-dir-store.ts +109 -0
- package/src/internal/json-schema.ts +69 -0
- package/src/internal/log-ring.ts +59 -0
- package/src/internal/map-pool.ts +32 -0
- package/src/internal/mcp-endpoint.ts +518 -0
- package/src/internal/mcp-host.ts +211 -0
- package/src/internal/net.ts +31 -0
- package/src/internal/peer-connections.ts +100 -0
- package/src/internal/playground/static.ts +189 -0
- package/src/internal/playground/toolchain.ts +277 -0
- package/src/internal/reminder-runner.ts +756 -0
- package/src/internal/reminder-schedule.ts +106 -0
- package/src/internal/reminder-store.ts +145 -0
- package/src/internal/resolved-connections.ts +145 -0
- package/src/internal/review-comments.ts +268 -0
- package/src/internal/router.ts +145 -0
- package/src/internal/run-client.ts +320 -0
- package/src/internal/schedule-runner.ts +161 -0
- package/src/internal/sdk-runner.ts +315 -0
- package/src/internal/server.ts +1737 -0
- package/src/internal/session-engine.ts +1810 -0
- package/src/internal/session-store.ts +147 -0
- package/src/internal/session-urls.ts +40 -0
- package/src/internal/stream-progress.ts +254 -0
- package/src/internal/tool-result.ts +54 -0
- package/src/internal/trajectory.ts +306 -0
- package/src/internal/workspace.ts +223 -0
- package/src/reminders.ts +35 -0
- package/src/schedules.ts +37 -0
- package/src/skills.ts +27 -0
- package/src/tools.ts +127 -0
- package/src/types.ts +1848 -0
package/README.md
ADDED
|
@@ -0,0 +1,1233 @@
|
|
|
1
|
+
# @cursor/july
|
|
2
|
+
|
|
3
|
+
> [!WARNING]
|
|
4
|
+
> **Early alpha.** `@cursor/july` is the codename release of Cursor's agent
|
|
5
|
+
> framework (the CLI is `agentkit`). It is under active development: APIs,
|
|
6
|
+
> the CLI surface, and the package name itself will change before a stable
|
|
7
|
+
> release, and 0.x versions may ship breaking changes without notice.
|
|
8
|
+
|
|
9
|
+
A filesystem-first framework for building and serving Cursor agents.
|
|
10
|
+
Customers define an agent as ordinary files — markdown for prose,
|
|
11
|
+
TypeScript for typed behavior — under an `agent/` directory. The framework
|
|
12
|
+
discovers those files, compiles them into a manifest, and serves the agent
|
|
13
|
+
over channels, using the Cursor SDK (`@cursor/sdk`) and the Cursor harness
|
|
14
|
+
as the execution engine.
|
|
15
|
+
|
|
16
|
+
**User-facing documentation lives in [`docs/`](./docs/README.md)** —
|
|
17
|
+
also served at `/docs` by every running `agent-serve serve` host —
|
|
18
|
+
getting started, concepts, guides (Slack, GitHub webhooks, approvals,
|
|
19
|
+
agent-to-agent, cloud runtime), evals, live A/B metrics, hillclimbing,
|
|
20
|
+
deployment, troubleshooting, and reference pages for each folder. This
|
|
21
|
+
README is the compact package reference.
|
|
22
|
+
|
|
23
|
+
Instead of one large configuration object, each part of the agent gets a
|
|
24
|
+
clear home. Instructions go in one file, tools in one folder, channels in
|
|
25
|
+
another; a file's location says what it does, and its path gives it its
|
|
26
|
+
name. There is no registry to keep in sync: add the file and it is
|
|
27
|
+
discovered, move or rename it and its identity moves with it.
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
my-agent/
|
|
31
|
+
├── package.json
|
|
32
|
+
└── agent/
|
|
33
|
+
├── agent.ts # runtime config: the Cursor model
|
|
34
|
+
├── instructions.md # the always-on system prompt
|
|
35
|
+
├── tools/
|
|
36
|
+
│ └── get_weather.ts # one typed tool per file
|
|
37
|
+
├── skills/
|
|
38
|
+
│ └── forecast.md # on-demand procedures (SKILL.md convention)
|
|
39
|
+
├── mcp-connections/
|
|
40
|
+
│ └── linear.ts # tools from external MCP servers
|
|
41
|
+
├── subagents/
|
|
42
|
+
│ └── researcher/ # specialist child agents
|
|
43
|
+
├── channels/
|
|
44
|
+
│ └── webhook.ts # HTTP surfaces beyond the built-in session API
|
|
45
|
+
├── hooks/
|
|
46
|
+
│ └── audit.ts # observe the runtime event stream
|
|
47
|
+
├── ab.ts # optional live A/B experiment
|
|
48
|
+
├── ab/ # optional: more experiments
|
|
49
|
+
├── schedules/
|
|
50
|
+
│ └── heartbeat.md # cron-driven runs
|
|
51
|
+
├── sandbox/workspace/ # files seeded into each session's workspace
|
|
52
|
+
└── lib/ # shared code (import-only, never discovered)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Scaffold a starter project with `agent-serve init ./my-agent` — a minimal
|
|
56
|
+
agent (`package.json`, `agent/agent.ts`, `agent/instructions.md`, and a demo
|
|
57
|
+
`agent/tools/echo.ts`) that serves immediately. From there, grow it folder by
|
|
58
|
+
folder; the [docs](./docs/README.md) walk a weather agent from a single
|
|
59
|
+
`get_weather` tool through skills, channels, evals, and deployment.
|
|
60
|
+
|
|
61
|
+
Serve one project with `agent-serve serve --dir ./my-agent --dev`, or point
|
|
62
|
+
`serve` at a folder of agent projects to mount every child under its
|
|
63
|
+
directory name.
|
|
64
|
+
|
|
65
|
+
## Node only — do not run under Bun
|
|
66
|
+
|
|
67
|
+
Run agent-serve with **Node 22+** (from source: `pnpm exec tsx
|
|
68
|
+
src/bin/agent-serve.ts …`, or the built `dist/bin/agent-serve.js`). Do not
|
|
69
|
+
run it under Bun: Bun's HTTP/2 client corrupts the Cursor SDK's local
|
|
70
|
+
harness tool-result streams (`NGHTTP2_FRAME_SIZE_ERROR`), so every built-in
|
|
71
|
+
read/grep the model makes fails and turns degrade into failed-read retry
|
|
72
|
+
loops (we measured an 8-minute review that takes ~1 minute under Node).
|
|
73
|
+
The `mise` tasks in this package already use tsx.
|
|
74
|
+
|
|
75
|
+
## Serving many agents at once
|
|
76
|
+
|
|
77
|
+
Point `serve` at a **folder of agent projects** and it hosts all of them on
|
|
78
|
+
one port, each under its own slug (its directory name):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
agent-serve serve --dir ./agents --dev
|
|
82
|
+
# 2 agents listening on http://127.0.0.1:3000
|
|
83
|
+
# index: http://127.0.0.1:3000/
|
|
84
|
+
# /weather-agent → /weather-agent/playground, /weather-agent/v1/*
|
|
85
|
+
# /concierge → /concierge/playground, /concierge/v1/*
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`serve` always uses multi-agent layout by default: agents are mounted under
|
|
89
|
+
`/<slug>/v1/*` with an index at `/`. A directory that is itself an agent
|
|
90
|
+
project is mounted under its directory name. Opt into the unslugged
|
|
91
|
+
`/v1/*` surface with `serve(dir, { mode: "single" })`.
|
|
92
|
+
|
|
93
|
+
In multi-agent mode:
|
|
94
|
+
|
|
95
|
+
- `GET /` is a web index listing every agent, linking to its playground;
|
|
96
|
+
`GET /v1/agents` is the JSON equivalent.
|
|
97
|
+
- `GET /v1/health` is host-level liveness (for ALB/ECS); each agent also has
|
|
98
|
+
`/<slug>/v1/health`.
|
|
99
|
+
- each agent is fully namespaced: `/<slug>/v1/session`,
|
|
100
|
+
`/<slug>/v1/session/:id/stream`, `/<slug>/playground`, `/<slug>/v1/info`,
|
|
101
|
+
custom channels at `/<slug>/v1/channels/<id>`, etc.
|
|
102
|
+
- sessions are isolated per agent (`<stateRoot>/<slug>/`), and the playground
|
|
103
|
+
gains a "← all agents" link back to the index.
|
|
104
|
+
- point the terminal client at a slug:
|
|
105
|
+
`agent-serve chat --url http://127.0.0.1:3000/weather-agent`.
|
|
106
|
+
|
|
107
|
+
Slugs come from directory names and must match `[A-Za-z0-9][A-Za-z0-9_-]*`
|
|
108
|
+
and not collide with the reserved `v1` / `playground` / `docs` path
|
|
109
|
+
segments.
|
|
110
|
+
|
|
111
|
+
## Agent-to-agent: every agent is an MCP server
|
|
112
|
+
|
|
113
|
+
Every mounted agent also serves the **Model Context Protocol** over
|
|
114
|
+
streamable HTTP at `/<slug>/v1/mcp` (or `/v1/mcp` in single mode), so other
|
|
115
|
+
agents — and any MCP client — can delegate work to it. The surface is
|
|
116
|
+
stateless (session identity travels in tool arguments) and runs the same
|
|
117
|
+
route auth chain as the session API. Tools:
|
|
118
|
+
|
|
119
|
+
| Tool | Behavior |
|
|
120
|
+
| ---- | -------- |
|
|
121
|
+
| `ask` | Send a message; runs a model turn in **this agent's own session, tools, and context** and returns `{ status, sessionId, reply }`. Omit `sessionId` for a fresh session; pass it to follow up. |
|
|
122
|
+
| `check` | Wait for / poll a running session (`waitSeconds: 0` for a non-blocking poll). |
|
|
123
|
+
| `call_tool` | Call one of the agent's deterministic server tools directly (no model turn). Registered only when the agent has server tools. |
|
|
124
|
+
|
|
125
|
+
Waits are bounded (~50s, below MCP client request timeouts): a long turn
|
|
126
|
+
returns `status: "running"` and the caller keeps waiting with `check`.
|
|
127
|
+
Sessions created this way live on the `mcp` channel, bind to the calling
|
|
128
|
+
principal, and show up in the playground and `GET /v1/sessions` like any
|
|
129
|
+
other session.
|
|
130
|
+
|
|
131
|
+
**Peer MCP connections** make delegation first-class between agents on the
|
|
132
|
+
same host. Author an MCP connection whose transport is a peer slug:
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// agents/concierge/agent/mcp-connections/weather.ts
|
|
136
|
+
import { defineConnection } from "@cursor/july/connections";
|
|
137
|
+
|
|
138
|
+
export default defineConnection({
|
|
139
|
+
agent: "weather-agent",
|
|
140
|
+
description: "Delegate weather questions to the weather agent.",
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The parent model then sees the peer's `ask` / `check` (/ `call_tool`) tools
|
|
145
|
+
under the `weather` server name — subagent-style delegation where the peer
|
|
146
|
+
keeps its **own** instructions, tools, MCP connections, and sessions. Peer URLs
|
|
147
|
+
resolve when the server starts (so `run` / `eval` ephemeral ports work):
|
|
148
|
+
|
|
149
|
+
- **Local-runtime turns** (and host-side `ctx.host.mcp` / channel handlers)
|
|
150
|
+
call the peer over loopback — works out of the box under the default
|
|
151
|
+
`localDevStrict()` auth.
|
|
152
|
+
- **Cloud-runtime turns** execute on a cloud VM that cannot reach this
|
|
153
|
+
host's loopback address. Pass `--public-url https://agent-serve.example.com`
|
|
154
|
+
(or `serve(dir, { publicUrl })`) so peers resolve to a reachable URL;
|
|
155
|
+
without it, peers are omitted from cloud turns (the server warns at
|
|
156
|
+
startup). With `--bearer-token`, the token is attached to peer calls
|
|
157
|
+
automatically so they pass the target agent's auth chain.
|
|
158
|
+
|
|
159
|
+
Unknown peer slugs and self-references fail at serve startup. Peers require
|
|
160
|
+
the multi-agent layout (each agent mounted under its slug). There is no
|
|
161
|
+
cross-host loop protection yet: if agent A's instructions delegate to B and
|
|
162
|
+
B's delegate back to A, they can recurse — scope each agent's delegation
|
|
163
|
+
instructions narrowly (the concierge above delegates *weather questions* to
|
|
164
|
+
`weather-agent`, not everything).
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
agent-serve serve --dir ./agents --dev
|
|
168
|
+
agent-serve chat --url http://127.0.0.1:3000/concierge \
|
|
169
|
+
--message "What's the weather in Paris right now?"
|
|
170
|
+
# concierge → weather.ask → weather-agent's own session/tools → reply
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Quick start
|
|
174
|
+
|
|
175
|
+
A minimal agent is two files.
|
|
176
|
+
|
|
177
|
+
`agent/instructions.md`:
|
|
178
|
+
|
|
179
|
+
```md
|
|
180
|
+
You are a concise assistant. Use tools when they are available.
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
`agent/agent.ts`:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
import { defineAgent } from "@cursor/july";
|
|
187
|
+
|
|
188
|
+
export default defineAgent({
|
|
189
|
+
// optional — defaults to grok-4.5 with effort=high and fast=true
|
|
190
|
+
// runtime: "local", // default — Cursor SDK local harness
|
|
191
|
+
// runtime: "cloud",
|
|
192
|
+
// cloud: {
|
|
193
|
+
// repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
|
|
194
|
+
// },
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Serve it (turns run on the Cursor harness, so the host needs a Cursor
|
|
199
|
+
credential — sign in once, or export an API key):
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
agent-serve login # browser sign-in; mints + stores a revocable API key
|
|
203
|
+
# or: export CURSOR_API_KEY=key_...
|
|
204
|
+
agent-serve serve --dir . --port 3000
|
|
205
|
+
# e.g. playground at http://127.0.0.1:3000/<dirname>/playground
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Start a session, then follow the NDJSON event stream (replace `<slug>` with
|
|
209
|
+
the agent directory name):
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
curl -X POST http://127.0.0.1:3000/<slug>/v1/session \
|
|
213
|
+
-H 'content-type: application/json' \
|
|
214
|
+
-d '{"message":"What can you do?"}'
|
|
215
|
+
# {"ok":true,"sessionId":"ses_...","continuationToken":"http:..."}
|
|
216
|
+
|
|
217
|
+
curl -N http://127.0.0.1:3000/<slug>/v1/session/ses_.../stream
|
|
218
|
+
# {"type":"session.started",...}
|
|
219
|
+
# {"type":"message.appended","data":{"delta":"I can","text":"I can"},...}
|
|
220
|
+
# {"type":"message.completed",...}
|
|
221
|
+
# {"type":"turn.completed",...}
|
|
222
|
+
# {"type":"session.waiting",...}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Send a follow-up to the same durable session with the continuation token
|
|
226
|
+
from the previous response (each accepted follow-up rotates it):
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
curl -X POST http://127.0.0.1:3000/<slug>/v1/session/ses_... \
|
|
230
|
+
-H 'content-type: application/json' \
|
|
231
|
+
-d '{"continuationToken":"http:...","message":"Shorter, please."}'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Inspect the discovered surface at any time:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
agent-serve info --dir . # or GET /<slug>/v1/info on a running server
|
|
238
|
+
agent-serve validate --dir . # exit non-zero on error diagnostics
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Terminal client
|
|
242
|
+
|
|
243
|
+
`agent-serve chat` talks to a running server over the same public API and
|
|
244
|
+
renders the reply live — streamed text, tool calls, and a per-turn usage
|
|
245
|
+
footer. Pass `--json` for a compact trajectory (same shape as `run`).
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
# interactive REPL against a local server
|
|
249
|
+
agent-serve chat --url http://127.0.0.1:3000
|
|
250
|
+
|
|
251
|
+
# one-shot (good for piping / scripts)
|
|
252
|
+
agent-serve chat --url http://127.0.0.1:3000 --message "Weather in Paris?"
|
|
253
|
+
|
|
254
|
+
# JSON trajectory (for coding agents / scripts)
|
|
255
|
+
agent-serve chat --url http://127.0.0.1:3000 --message "hello" --json
|
|
256
|
+
|
|
257
|
+
# against an authed server
|
|
258
|
+
agent-serve chat --url https://my-agent.example.com --bearer-token "$TOKEN"
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Agent loop
|
|
262
|
+
|
|
263
|
+
Coding agents should prefer the JSON-first commands: edit files → inspect
|
|
264
|
+
→ run a turn → assert with filesystem evals.
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
agent-serve validate --dir .
|
|
268
|
+
agent-serve info --dir . --json
|
|
269
|
+
|
|
270
|
+
# Ephemeral server + one (or more) turns; JSON trajectory on stdout
|
|
271
|
+
agent-serve run --dir . --message "What's the temperature in NYC?"
|
|
272
|
+
|
|
273
|
+
# Optional event log for later summarization
|
|
274
|
+
agent-serve run --dir . --message "..." --events /tmp/run.ndjson
|
|
275
|
+
agent-serve trajectory --events /tmp/run.ndjson
|
|
276
|
+
|
|
277
|
+
# Scaffold (or print the setup guide with no directory)
|
|
278
|
+
agent-serve init ./my-agent
|
|
279
|
+
agent-serve init
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
`run` / `eval` boot a temporary server on port `0` with a temp `stateRoot`
|
|
283
|
+
outside the project so session workspaces do not inherit ambient monorepo
|
|
284
|
+
`AGENTS.md` / `.cursor` rules. Pass `--url` to target an already-running
|
|
285
|
+
agent instead.
|
|
286
|
+
|
|
287
|
+
### Evals
|
|
288
|
+
|
|
289
|
+
Author cases under `evals/**/*.eval.ts` with `defineEval`. Drive the
|
|
290
|
+
agent and assert inline:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { defineEval, includes } from "@cursor/july/evals";
|
|
294
|
+
|
|
295
|
+
export default defineEval({
|
|
296
|
+
description: "Uses get_weather for temperature questions.",
|
|
297
|
+
tags: ["smoke"],
|
|
298
|
+
async test(t) {
|
|
299
|
+
await t.send("What's the temperature in NYC?");
|
|
300
|
+
t.succeeded();
|
|
301
|
+
t.calledTool("get_weather");
|
|
302
|
+
t.check(t.reply, includes(/°|[FC]/));
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Case id is the path under `evals/` for a single-`test` file, or
|
|
308
|
+
`<fileId>/<case.id>` when using `cases` in one file.
|
|
309
|
+
|
|
310
|
+
Every project with evals needs `evals/evals.config.ts` with a required
|
|
311
|
+
`maxConcurrency` (hard-capped at 200 due to model provider request limits).
|
|
312
|
+
Playground batches are in-memory by default; set optional `persistRuns` to
|
|
313
|
+
keep them across serve restarts. Optional `maxPlaygroundRuns` caps how many
|
|
314
|
+
batches the playground / `/v1/dev/evals*` history keeps (default 20):
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
import {
|
|
318
|
+
defineEvalConfig,
|
|
319
|
+
persistEvalRunsToDir,
|
|
320
|
+
} from "@cursor/july/evals";
|
|
321
|
+
|
|
322
|
+
export default defineEvalConfig({
|
|
323
|
+
maxConcurrency: 20,
|
|
324
|
+
// Optional — playground /v1/dev/evals history window (default 20):
|
|
325
|
+
// maxPlaygroundRuns: 50,
|
|
326
|
+
// Optional — survive serve restarts (omit to keep runs in memory only):
|
|
327
|
+
persistRuns: persistEvalRunsToDir(".agent-serve/eval-runs"),
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
agent-serve eval --dir . --list
|
|
333
|
+
agent-serve eval --dir . --json
|
|
334
|
+
agent-serve eval --dir . weather/nyc # one datapoint
|
|
335
|
+
agent-serve eval --dir . weather # whole file
|
|
336
|
+
agent-serve eval --dir . weather forecast # multiple files
|
|
337
|
+
agent-serve eval --dir . --tag smoke
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
See [`docs/`](./docs/README.md) for the full documentation set,
|
|
341
|
+
[`AGENTS.md`](./AGENTS.md) for the coding-agent guide, and
|
|
342
|
+
[`skills/`](./skills/) for task-shaped guides (create an agent, author
|
|
343
|
+
evals, hillclimb, GitHub webhooks, Slack setup, debugging).
|
|
344
|
+
|
|
345
|
+
## Web playground
|
|
346
|
+
|
|
347
|
+
Every served agent ships with a built-in playground at
|
|
348
|
+
`http://127.0.0.1:3000/playground` — a Vite + React SPA (reusing
|
|
349
|
+
Cursor's internal UI components) over the same public HTTP API, made for quick
|
|
350
|
+
manual testing and demo recordings:
|
|
351
|
+
|
|
352
|
+
- chat with the agent and watch text/reasoning stream live, rendered as
|
|
353
|
+
markdown (headings, lists, tables, blockquotes, links) with syntax
|
|
354
|
+
highlighting for fenced code blocks (C-like languages, Python, shell,
|
|
355
|
+
and diffs),
|
|
356
|
+
- invoke custom channels as **slash commands** in the composer (e.g. a
|
|
357
|
+
`/drive https://github.com/org/repo/pull/1` channel route) — routes from `GET /v1/info`,
|
|
358
|
+
with `/help` and autocomplete; same HTTP as the Agent surface **Try**
|
|
359
|
+
buttons,
|
|
360
|
+
- see tool calls inline (args, output, error state) as `actions.requested`
|
|
361
|
+
/ `action.result` events arrive,
|
|
362
|
+
- browse every session (chat, custom-channel, and schedule task sessions)
|
|
363
|
+
and replay their durable event streams,
|
|
364
|
+
- dispatch schedules by hand in dev mode,
|
|
365
|
+
- inspect the discovered agent surface (tools, skills, subagents, MCP
|
|
366
|
+
connections, channels, hooks),
|
|
367
|
+
- flip on the raw NDJSON pane to see the exact wire events.
|
|
368
|
+
|
|
369
|
+
The SPA is a static bundle. `agent-serve serve` auto-builds `dist/playground/`
|
|
370
|
+
when it is missing and the local vite toolchain is present (`pnpm run build`
|
|
371
|
+
also emits it for publish). The server serves the bundle and every call it
|
|
372
|
+
makes runs the normal route auth chain (there's a bearer-token field for
|
|
373
|
+
non-loopback setups). Disable it with `--no-playground` (CLI) or
|
|
374
|
+
`serve(dir, { playground: false })`. When serving many agents, each has its
|
|
375
|
+
own playground at `/<slug>/playground` and `/` is an index of them all (see
|
|
376
|
+
"Serving many agents at once").
|
|
377
|
+
|
|
378
|
+
### Developing the playground
|
|
379
|
+
|
|
380
|
+
`serve --dev` also starts Vite HMR (default `:5273`) and prints
|
|
381
|
+
`playground (HMR): …`. Single-agent proxies `/v1` to the serve URL; multi-agent
|
|
382
|
+
serves the agents index at `/` and each SPA at `/<slug>/playground`:
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# single agent — auto-build + HMR in one process
|
|
386
|
+
pnpm exec tsx src/bin/agent-serve.ts serve --dir ./my-agent --dev
|
|
387
|
+
|
|
388
|
+
# monorepo dev: multi-agent HMR
|
|
389
|
+
mise //packages/agent-serve:start
|
|
390
|
+
# → backend :3000, playground HMR :5273 (open /, then /<slug>/playground)
|
|
391
|
+
|
|
392
|
+
# pin one slug at the HMR root, or UI-only against an already-running serve
|
|
393
|
+
AGENT_SERVE_BASE=/my-agent mise //packages/agent-serve:start
|
|
394
|
+
AGENT_SERVE_MULTI=1 mise //packages/agent-serve:dev-playground
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
The Vite dev server proxies API calls to the backend (`AGENT_SERVE_TARGET`,
|
|
398
|
+
default `http://127.0.0.1:3000`). Multi-agent HMR proxies `/<slug>/v1/*` as-is
|
|
399
|
+
and serves each SPA at `/<slug>/playground`; a single-slug pin uses
|
|
400
|
+
`AGENT_SERVE_BASE=/<slug>` at the HMR root. Editing anything under
|
|
401
|
+
`playground/src` hot-reloads in the browser. The playground source lives in
|
|
402
|
+
`playground/` (entry `playground/src/main.tsx`); markdown rendering and the
|
|
403
|
+
trace model are plain modules under `playground/src/lib` with unit tests.
|
|
404
|
+
|
|
405
|
+
To share the server beyond localhost (a tunnel, a LAN address, a phone),
|
|
406
|
+
pass `--bearer-token <secret>` (or `serve(dir, { authToken })`). That
|
|
407
|
+
replaces the loopback-only default with `bearerAuth(secret)` on every
|
|
408
|
+
channel that doesn't author its own auth. The default
|
|
409
|
+
`localDevStrict()` only admits direct loopback callers, rejects
|
|
410
|
+
proxy-forwarding headers (`X-Forwarded-For`, `X-Real-IP`, `Forwarded`,
|
|
411
|
+
`X-Forwarded-Host`), and requires a loopback `Host` header, so
|
|
412
|
+
same-host reverse proxies / tunnels do not accidentally re-expose the
|
|
413
|
+
default routes. Open the playground on the remote device and paste the
|
|
414
|
+
token into the top-right field.
|
|
415
|
+
|
|
416
|
+
Session follow-up, stream, and list routes also bind to the principal that
|
|
417
|
+
created the session (`403` when a different admitted principal addresses
|
|
418
|
+
someone else's handle). Session ids are restricted to a single safe path
|
|
419
|
+
segment before they touch `<stateRoot>/sessions`.
|
|
420
|
+
|
|
421
|
+
## How it runs on the Cursor harness
|
|
422
|
+
|
|
423
|
+
Every session is one Cursor SDK agent (`Agent.create` / `Agent.resume`).
|
|
424
|
+
Local agents use the session id as the SDK agent id; cloud agents persist
|
|
425
|
+
a separate `sdkAgentId` (typically `bc-…`). Each session gets its own
|
|
426
|
+
workspace directory, materialized from the authored files and handed to
|
|
427
|
+
the local harness as its working directory:
|
|
428
|
+
|
|
429
|
+
| Folder or file | Runtime mapping |
|
|
430
|
+
| ------------------------ | ------------------------------------------------------------------------------- |
|
|
431
|
+
| `instructions.*` | Local: `AGENTS.md` in the session workspace. Cloud: prepended to the first prompt. |
|
|
432
|
+
| `tools/*.ts` (`execution: "server"`, default) | Local only — in-process SDK custom tools. Not available on cloud. |
|
|
433
|
+
| `tools/*.ts` (`execution: "agent"`) | Local: scripts under `.agent-serve/tools/` + catalog in `AGENTS.md`. Cloud: catalog + script bodies on the first prompt. |
|
|
434
|
+
| `skills/*` | Local: `.cursor/skills/<name>/SKILL.md` in the workspace. Cloud: only if present in the cloud repo. |
|
|
435
|
+
| `mcp-connections/*.ts` | Always three places: Cursor agent via SDK `mcpServers` (local + cloud), host-side `ctx.host.mcp` for in-process tools, and `args.host.mcp` on channel/schedule handlers. |
|
|
436
|
+
| `subagents/<id>/` | SDK custom subagents (the model delegates via the harness `task` tool) |
|
|
437
|
+
| `sandbox/workspace/**` | Local session workspace seed on first turn; ignored for cloud runtime. |
|
|
438
|
+
| `channels/`, `schedules/`, `hooks/` | Served by this framework around the harness |
|
|
439
|
+
|
|
440
|
+
Conversation state for local agents persists through the SDK's local store
|
|
441
|
+
under `.agent-serve/runner/`, and every session's event stream is recorded
|
|
442
|
+
to `.agent-serve/sessions/<id>/events.ndjson` — sessions survive server
|
|
443
|
+
restarts, and streams replay from any `startIndex`.
|
|
444
|
+
|
|
445
|
+
## Folder structure
|
|
446
|
+
|
|
447
|
+
### `agent.ts`
|
|
448
|
+
|
|
449
|
+
```ts
|
|
450
|
+
import { defineAgent } from "@cursor/july";
|
|
451
|
+
|
|
452
|
+
export default defineAgent({
|
|
453
|
+
model: {
|
|
454
|
+
id: "grok-4.5",
|
|
455
|
+
params: [
|
|
456
|
+
{ id: "effort", value: "high" },
|
|
457
|
+
{ id: "fast", value: "true" },
|
|
458
|
+
],
|
|
459
|
+
}, // optional; this is the default
|
|
460
|
+
runtime: "local", // default — or "cloud"
|
|
461
|
+
// cloud: {
|
|
462
|
+
// repos: [{ url: "https://github.com/org/repo", startingRef: "main" }],
|
|
463
|
+
// env: { type: "cloud" },
|
|
464
|
+
// },
|
|
465
|
+
});
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
`model` accepts a Cursor model id string or `{ id, params }`. When omitted
|
|
469
|
+
on the root agent, agent-serve defaults to `grok-4.5` with `effort=high`
|
|
470
|
+
and `fast=true`.
|
|
471
|
+
|
|
472
|
+
`runtime` selects where turns execute:
|
|
473
|
+
|
|
474
|
+
| Value | Behavior |
|
|
475
|
+
| --------- | ------- |
|
|
476
|
+
| `"local"` | Cursor SDK local harness on this machine. Session id doubles as the SDK agent id. Authored tools, skills, and sandbox seeds apply. |
|
|
477
|
+
| `"cloud"` | Cursor cloud agents. Pass a `cloud` block (repos, env, envVars, …) forwarded to the SDK. In-process server tools are not available; instructions and agent-tool catalogs are prepended to the first prompt because the local session workspace is not the cloud VM. |
|
|
478
|
+
|
|
479
|
+
Discovery warns when `runtime: "cloud"` is combined with server tools, skills, or sandbox seeds that only apply locally.
|
|
480
|
+
|
|
481
|
+
### Instructions
|
|
482
|
+
|
|
483
|
+
`agent/instructions.md` is the always-on system prompt (required on the
|
|
484
|
+
root agent). When the prompt needs to be generated, use
|
|
485
|
+
`agent/instructions.ts` with `defineInstructions({ markdown })` or a plain
|
|
486
|
+
string default export, or split prose across an `agent/instructions/`
|
|
487
|
+
directory (composed in filename order).
|
|
488
|
+
|
|
489
|
+
### Tools (`agent/tools/*.ts`)
|
|
490
|
+
|
|
491
|
+
One file per tool; the filename is the tool name the model sees.
|
|
492
|
+
|
|
493
|
+
`execution` chooses where the tool body runs:
|
|
494
|
+
|
|
495
|
+
| Value | Behavior |
|
|
496
|
+
| ----- | -------- |
|
|
497
|
+
| `"server"` (default) | In-process on the agent-serve host via Cursor SDK custom tools. Requires `execute`. Only available when the agent `runtime` is `"local"`. |
|
|
498
|
+
| `"agent"` | Materialized into the agent environment as a shell script. Requires `script` (JSON on stdin, result on stdout). Works with local and cloud agent runtimes. |
|
|
499
|
+
|
|
500
|
+
Server tool (default):
|
|
501
|
+
|
|
502
|
+
```ts
|
|
503
|
+
import { defineTool } from "@cursor/july/tools";
|
|
504
|
+
import { z } from "zod";
|
|
505
|
+
|
|
506
|
+
export default defineTool({
|
|
507
|
+
description: "Get the current weather for a city.",
|
|
508
|
+
// execution: "server", // default
|
|
509
|
+
inputSchema: z.object({ city: z.string() }),
|
|
510
|
+
async execute({ city }, ctx) {
|
|
511
|
+
return { city, condition: "Sunny", temperatureF: 72 };
|
|
512
|
+
},
|
|
513
|
+
});
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
Human-in-the-loop: set `needsApproval: true` (or an
|
|
517
|
+
`(input) => boolean` predicate on validated args) so the host parks the in-flight
|
|
518
|
+
tool call until a human approves or denies. The turn stays running; the
|
|
519
|
+
stream emits `action.approval_requested` / `action.approval_resolved`.
|
|
520
|
+
|
|
521
|
+
**Resolve surfaces** (park is always on when `needsApproval` is set):
|
|
522
|
+
|
|
523
|
+
- Playground Approve / Deny buttons
|
|
524
|
+
- HTTP: `POST /v1/session/:sessionId/approvals/:callId` with
|
|
525
|
+
`{"decision":"approve"|"deny"}`
|
|
526
|
+
- **Slack** (opt-in channel surface) — set `toolApprovals: true` on
|
|
527
|
+
`slackChannel`, and enable `interactivity` in the Slack app
|
|
528
|
+
manifest:
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
import { slackChannel } from "@cursor/july/channels/slack";
|
|
532
|
+
|
|
533
|
+
export default slackChannel({
|
|
534
|
+
credentials: { /* … */ },
|
|
535
|
+
toolApprovals: true, // posts Block Kit Approve/Deny + routes clicks
|
|
536
|
+
});
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Or compose the builders yourself:
|
|
540
|
+
|
|
541
|
+
```typescript
|
|
542
|
+
import {
|
|
543
|
+
buildDefaultEvents,
|
|
544
|
+
buildToolApprovalEvents,
|
|
545
|
+
slackChannel,
|
|
546
|
+
} from "@cursor/july/channels/slack";
|
|
547
|
+
|
|
548
|
+
export default slackChannel({
|
|
549
|
+
deliverDefaults: false,
|
|
550
|
+
// Required so Socket Mode routes Approve/Deny clicks when composing
|
|
551
|
+
// buildToolApprovalEvents by hand (toolApprovals: true also enables this).
|
|
552
|
+
interactivity: true,
|
|
553
|
+
events: {
|
|
554
|
+
...buildDefaultEvents({ credentials }),
|
|
555
|
+
...buildToolApprovalEvents({ credentials }),
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
In `--dev`, the playground can list and stream Slack sessions, and Approve/Deny
|
|
561
|
+
parked tools on those sessions (audit `by` records the HTTP caller). That bridge
|
|
562
|
+
does **not** open cross-owner approval for HTTP sessions. Production and
|
|
563
|
+
bearer-auth hosts stay strict: Slack resolve must come from Slack interactivity
|
|
564
|
+
(or a matching principal).
|
|
565
|
+
|
|
566
|
+
`--allow-anonymous` is for trusted-network demos only — every HTTP caller shares
|
|
567
|
+
the same `anonymous` principal. Prefer `--bearer-token` when the host is shared.
|
|
568
|
+
|
|
569
|
+
Slack cards show **redacted / truncated** args for Block Kit limits; execution
|
|
570
|
+
still uses the full validated tool input. Review sensitive tools in the
|
|
571
|
+
playground or a private surface when args may exceed the card.
|
|
572
|
+
|
|
573
|
+
Example tool:
|
|
574
|
+
|
|
575
|
+
```ts
|
|
576
|
+
export default defineTool({
|
|
577
|
+
description: "Post a weather alert to ops.",
|
|
578
|
+
needsApproval: true,
|
|
579
|
+
inputSchema: z.object({
|
|
580
|
+
city: z.string(),
|
|
581
|
+
message: z.string(),
|
|
582
|
+
}),
|
|
583
|
+
async execute({ city, message }) {
|
|
584
|
+
return { posted: true, city, message };
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Approvals are only supported for `execution: "server"` tools on the
|
|
590
|
+
`local` runtime. Exact resume of a parked SDK tool call does **not**
|
|
591
|
+
survive host process restart — pending approvals left after a crash are
|
|
592
|
+
treated as interrupted.
|
|
593
|
+
Agent tool (runs where the Cursor agent runs — local harness or cloud VM):
|
|
594
|
+
|
|
595
|
+
```ts
|
|
596
|
+
import { defineTool } from "@cursor/july/tools";
|
|
597
|
+
import { z } from "zod";
|
|
598
|
+
|
|
599
|
+
export default defineTool({
|
|
600
|
+
description: "Echo a message from the agent workspace.",
|
|
601
|
+
execution: "agent",
|
|
602
|
+
inputSchema: z.object({ message: z.string() }),
|
|
603
|
+
script: `#!/usr/bin/env bash
|
|
604
|
+
set -euo pipefail
|
|
605
|
+
message=$(python3 -c 'import json,sys; print(json.load(sys.stdin)["message"])')
|
|
606
|
+
printf '%s\\n' "$message"
|
|
607
|
+
`,
|
|
608
|
+
});
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
`inputSchema` is a zod schema (validated and typed for server tools) or a
|
|
612
|
+
plain JSON Schema object (forwarded as-is). For server tools, `ctx` carries
|
|
613
|
+
`{ toolCallId, session, workspaceDir }`. Return a string, a JSON value, or
|
|
614
|
+
`{ content: [...], isError? }` for rich results.
|
|
615
|
+
|
|
616
|
+
#### Deterministic tool calls
|
|
617
|
+
|
|
618
|
+
Server tools can also be called **deterministically** — you pick the tool
|
|
619
|
+
and the input, no model turn decides anything. The input is validated
|
|
620
|
+
against the tool's schema and `execute` runs in-process; the result comes
|
|
621
|
+
back exactly as the model would receive it. No Cursor API key is needed.
|
|
622
|
+
|
|
623
|
+
Over HTTP (`POST /v1/tools/:toolName`, same auth chain as the session API):
|
|
624
|
+
|
|
625
|
+
```bash
|
|
626
|
+
curl -X POST http://127.0.0.1:3000/<slug>/v1/tools/get_weather \
|
|
627
|
+
-H 'content-type: application/json' \
|
|
628
|
+
-d '{"input":{"city":"NYC"}}'
|
|
629
|
+
# {"ok":true,"toolName":"get_weather","callId":"tool_get_weather_...",
|
|
630
|
+
# "isError":false,"result":{...},"durationMs":12}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
From the CLI (boots an ephemeral server unless `--url` targets a running
|
|
634
|
+
one):
|
|
635
|
+
|
|
636
|
+
```bash
|
|
637
|
+
agent-serve call get_weather --dir . --input '{"city":"NYC"}'
|
|
638
|
+
agent-serve call get_weather --url http://127.0.0.1:3000/<slug> --input '{"city":"NYC"}'
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
Programmatically, `callTool(toolName, input, options?)` is available on the
|
|
642
|
+
serve handle, on channel route handlers and `onStart` args, and on schedule
|
|
643
|
+
`run` handlers — so a channel can mix deterministic tool calls with model
|
|
644
|
+
turns (e.g. fetch PR metadata deterministically, then `send()` the review
|
|
645
|
+
prompt):
|
|
646
|
+
|
|
647
|
+
```ts
|
|
648
|
+
const outcome = await handle.callTool("get_weather", { city: "NYC" });
|
|
649
|
+
// { toolName, callId, isError, result, durationMs }
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
By default the tool runs against an **ephemeral** scratch workspace under
|
|
653
|
+
`<stateRoot>/tool-calls/<callId>` with a synthetic `direct` session context —
|
|
654
|
+
materialized like a session workspace (AGENTS.md, skills, seed files) and
|
|
655
|
+
removed once the call returns. Pass `sessionId` (body field over HTTP,
|
|
656
|
+
`--session` on the CLI, `options.sessionId` programmatically) to run it
|
|
657
|
+
**inside an existing session** instead: the tool sees that session's info
|
|
658
|
+
and materialized workspace, and the call is recorded on the session's event
|
|
659
|
+
stream as `actions.requested` / `action.result` under a per-call `turnId` —
|
|
660
|
+
visible in the playground, NDJSON trace, and trajectories like any
|
|
661
|
+
model-initiated call. Session-bound calls are serialized with model turns:
|
|
662
|
+
while a turn is running the call is rejected with `409 session_busy`.
|
|
663
|
+
|
|
664
|
+
Unknown tools are rejected with the available tool names, `execution:
|
|
665
|
+
"agent"` tools cannot be called on the host (400), schema-invalid input is a
|
|
666
|
+
400 before the tool body runs (zod schemas validate; plain JSON Schema
|
|
667
|
+
inputs pass through unvalidated, matching the model path), and a tool body
|
|
668
|
+
that throws is reported as `isError: true` with the same
|
|
669
|
+
`{ content, isError }` envelope the model would see.
|
|
670
|
+
|
|
671
|
+
### Skills (`agent/skills/*`)
|
|
672
|
+
|
|
673
|
+
Skills follow the `SKILL.md` convention: model-loadable procedures the
|
|
674
|
+
harness advertises by description and loads on demand. Author them as flat
|
|
675
|
+
markdown (`skills/forecast.md`, optional `description` frontmatter — the
|
|
676
|
+
first body line is the fallback), packaged directories
|
|
677
|
+
(`skills/research/SKILL.md` plus `references/…`, which require `description`
|
|
678
|
+
frontmatter), or TypeScript (`defineSkill` from
|
|
679
|
+
`@cursor/july/skills`) when content must be generated.
|
|
680
|
+
|
|
681
|
+
### MCP connections (`agent/mcp-connections/*.ts`)
|
|
682
|
+
|
|
683
|
+
```ts
|
|
684
|
+
import { defineConnection } from "@cursor/july/connections";
|
|
685
|
+
|
|
686
|
+
export default defineConnection({
|
|
687
|
+
url: "https://mcp.linear.app/mcp",
|
|
688
|
+
headers: { authorization: `Bearer ${process.env.LINEAR_TOKEN}` },
|
|
689
|
+
});
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
The filename becomes the MCP server name. `{ command, args, env, cwd }`
|
|
693
|
+
declares a local stdio server instead, and `{ agent: "<slug>" }` declares a
|
|
694
|
+
**peer MCP connection** to another agent mounted on the same serve host (see
|
|
695
|
+
"Agent-to-agent: every agent is an MCP server").
|
|
696
|
+
|
|
697
|
+
`{ cursorAccount: true }` declares a **Cursor account MCP connection**: the
|
|
698
|
+
agent gets the MCP connectors the signed-in Cursor account already
|
|
699
|
+
authorized (dashboard → MCP) with zero token plumbing. Every tool executes
|
|
700
|
+
on the Cursor backend with the account's stored OAuth credentials — raw
|
|
701
|
+
tokens never reach the serve host, session workspaces, or traces. Optional
|
|
702
|
+
`servers: ["Linear", …]` restricts which connectors are exposed.
|
|
703
|
+
|
|
704
|
+
```ts
|
|
705
|
+
// agent/mcp-connections/cursor.ts — the account's connectors, one line
|
|
706
|
+
export default defineConnection({ cursorAccount: true, servers: ["Linear"] });
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
Requirements and behavior:
|
|
710
|
+
|
|
711
|
+
- The host must be signed in (`agent-serve login` or `CURSOR_API_KEY`);
|
|
712
|
+
`serve` fails fast at startup otherwise, and logs each connector's live
|
|
713
|
+
status (`connected` / `needsAuth` / `error`) as it starts.
|
|
714
|
+
- Local turns and host-side calls go through a loopback bridge route guarded
|
|
715
|
+
by a per-boot secret. Cloud-runtime turns reach the same bridge through the
|
|
716
|
+
serve `--public-url` (so `servers` filters apply there too); without one,
|
|
717
|
+
cloud turns fall back to the account's natively hydrated connectors
|
|
718
|
+
(unfiltered) and the serve host logs why.
|
|
719
|
+
- Backend execution covers the account's HTTP/SSE servers; stdio servers
|
|
720
|
+
cannot run server-side (author a `{ command }` MCP connection for those).
|
|
721
|
+
- **Exposure**: whoever can talk to the agent can drive these connectors
|
|
722
|
+
(they are ordinary agent tools). `serve` refuses to start when
|
|
723
|
+
`--allow-anonymous` is combined with Cursor account MCP connections; use
|
|
724
|
+
`--bearer-token` on shared hosts.
|
|
725
|
+
|
|
726
|
+
Every MCP connection is always available in three places:
|
|
727
|
+
|
|
728
|
+
1. the Cursor agent (local or cloud), via SDK `mcpServers`
|
|
729
|
+
2. the agent-serve host, for in-process tools via `ctx.host.mcp`
|
|
730
|
+
3. channel / schedule handlers, via `args.host.mcp` (deterministic — no agent loop)
|
|
731
|
+
|
|
732
|
+
```ts
|
|
733
|
+
export default defineTool({
|
|
734
|
+
description: "Search Linear issues.",
|
|
735
|
+
inputSchema: z.object({ query: z.string() }),
|
|
736
|
+
async execute({ query }, ctx) {
|
|
737
|
+
return ctx.host.mcp.callTool("linear", "list_issues", { query });
|
|
738
|
+
},
|
|
739
|
+
});
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
```ts
|
|
743
|
+
// agent/channels/webhook.ts — call MCP directly from a webhook
|
|
744
|
+
POST("/sync", {
|
|
745
|
+
bodySchema: z.object({}),
|
|
746
|
+
handler: async (_req, { host }) => {
|
|
747
|
+
const result = await host.mcp.callTool("linear", "list_issues", {});
|
|
748
|
+
return Response.json(result);
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
`host.mcp.names()` lists MCP connection names; `listTools(name)` and
|
|
754
|
+
`callTool(name, tool, args)` open the client lazily on first use.
|
|
755
|
+
|
|
756
|
+
### Subagents (`agent/subagents/<id>/`)
|
|
757
|
+
|
|
758
|
+
A subagent is its own directory with the same `agent.ts` +
|
|
759
|
+
`instructions.md` shape. `description` is required — the parent model reads
|
|
760
|
+
it to decide when to delegate — and `model` is optional (`inherit` by
|
|
761
|
+
default). On the Cursor harness, subagents run as SDK custom subagents:
|
|
762
|
+
they inherit the parent's execution surface, so per-subagent `tools/`,
|
|
763
|
+
`skills/`, and `mcp-connections/` are reported as warnings and ignored for now.
|
|
764
|
+
|
|
765
|
+
### Channels (`agent/channels/*.ts`)
|
|
766
|
+
|
|
767
|
+
The **built-in HTTP channel** is always mounted (under `/<slug>` in the
|
|
768
|
+
default multi-agent layout; at the server root with `mode: "single"`):
|
|
769
|
+
|
|
770
|
+
- `POST /v1/session` — start a session (`{"message": "..."}`; returns
|
|
771
|
+
`sessionId` + `continuationToken`)
|
|
772
|
+
- `POST /v1/session/:sessionId` — follow-up (`{"message", "continuationToken"}`;
|
|
773
|
+
rotates the token; works for any chat session including custom channels like
|
|
774
|
+
`drive`; `409` on stale tokens, busy sessions, or task/schedule sessions;
|
|
775
|
+
`403` if the caller is not the session owner)
|
|
776
|
+
- `GET /v1/session/:sessionId/stream?startIndex=N` — replay + live NDJSON
|
|
777
|
+
(same owner check)
|
|
778
|
+
- `GET /v1/session/:sessionId/approvals` — pending human-in-the-loop
|
|
779
|
+
tool approvals for the session
|
|
780
|
+
- `POST /v1/session/:sessionId/approvals/:callId` — approve or deny
|
|
781
|
+
(`{"decision":"approve"|"deny"}`)
|
|
782
|
+
- `GET /v1/sessions` — sessions owned by the calling principal
|
|
783
|
+
- `POST /v1/tools/:toolName` — call a server tool deterministically
|
|
784
|
+
(`{"input": {...}, "sessionId"?}`; see "Deterministic tool calls")
|
|
785
|
+
- `GET /v1/health`, `GET /v1/info` — liveness and the manifest snapshot
|
|
786
|
+
Author `agent/channels/http.ts` only to override its defaults:
|
|
787
|
+
|
|
788
|
+
```ts
|
|
789
|
+
import {
|
|
790
|
+
bearerAuth,
|
|
791
|
+
httpChannel,
|
|
792
|
+
localDevStrict,
|
|
793
|
+
} from "@cursor/july/channels";
|
|
794
|
+
|
|
795
|
+
export default httpChannel({
|
|
796
|
+
auth: [localDevStrict(), bearerAuth(process.env.AGENT_TOKEN ?? "")],
|
|
797
|
+
onMessage: (message, { auth }) =>
|
|
798
|
+
`[caller ${auth?.principalId ?? "anonymous"}] ${message}`,
|
|
799
|
+
});
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
**Custom channels** declare their own routes (mounted under
|
|
803
|
+
`/v1/channels/<id>`), observe stream events for the sessions they own, and
|
|
804
|
+
control their continuation-token format (e.g. a thread id):
|
|
805
|
+
|
|
806
|
+
```ts
|
|
807
|
+
import { defineChannel, POST } from "@cursor/july/channels";
|
|
808
|
+
import { z } from "zod";
|
|
809
|
+
|
|
810
|
+
export default defineChannel({
|
|
811
|
+
routes: [
|
|
812
|
+
POST("/message", {
|
|
813
|
+
description: "Enqueue a chat turn on this channel",
|
|
814
|
+
bodySchema: z.object({
|
|
815
|
+
message: z.string(),
|
|
816
|
+
thread: z.string().optional(),
|
|
817
|
+
}),
|
|
818
|
+
handler: async (_req, { send, body }) => {
|
|
819
|
+
const { message, thread } = body;
|
|
820
|
+
// `auth` defaults to the request principal resolved by the auth chain.
|
|
821
|
+
const session = await send(message, {
|
|
822
|
+
continuationToken: thread, // stable key: same thread, same session
|
|
823
|
+
});
|
|
824
|
+
return Response.json({ sessionId: session.id });
|
|
825
|
+
},
|
|
826
|
+
}),
|
|
827
|
+
],
|
|
828
|
+
events: {
|
|
829
|
+
"message.completed"(event, channel, ctx) {
|
|
830
|
+
// deliver the reply back to the surface that owns this channel
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
});
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
`GET` requires a Zod `querySchema` and `POST` / `PUT` / `PATCH` require a
|
|
837
|
+
Zod `bodySchema` at compile time — plain JSON Schema objects will not
|
|
838
|
+
type-check. Use `z.object({})` or `z.unknown()` when the surface is
|
|
839
|
+
intentionally open. Schemas are validated by the host before the handler
|
|
840
|
+
runs (handlers get typed `args.body` / `args.query`) and projected on
|
|
841
|
+
`GET /v1/info` so the playground Agent surface can **Try** the route, and
|
|
842
|
+
so the composer can offer matching **slash commands** (e.g. `/drive`).
|
|
843
|
+
|
|
844
|
+
Route handlers receive a Fetch `Request` and helpers: `send`, `getSession`,
|
|
845
|
+
`receive` (cross-channel hand-off), `params`, `requestIp`, `auth`, `host`
|
|
846
|
+
(shared host services — MCP / GitHub / Slack; same as tool `ctx.host`), and
|
|
847
|
+
`waitUntil`. Channel `state` declares initial per-session adapter state,
|
|
848
|
+
persisted across events; handlers receive it on `channel.state`.
|
|
849
|
+
|
|
850
|
+
**Auth**: every route runs an auth-policy chain (`auth` on the channel).
|
|
851
|
+
The default is `[localDevStrict()]` — direct loopback callers only, with
|
|
852
|
+
proxy-forwarding headers and non-loopback `Host` rejected — so nothing
|
|
853
|
+
is exposed publicly until you add real auth (`bearerAuth(...)`, a custom
|
|
854
|
+
policy, or the explicit `allowAll()`).
|
|
855
|
+
|
|
856
|
+
**Slack** (`@cursor/july/channels/slack`): a platform channel pack
|
|
857
|
+
that defaults to **Socket Mode**. Author `agent/channels/slack.ts` with
|
|
858
|
+
`slackChannel()`:
|
|
859
|
+
|
|
860
|
+
```ts
|
|
861
|
+
import { slackChannel } from "@cursor/july/channels/slack";
|
|
862
|
+
|
|
863
|
+
// Single agent — SLACK_BOT_TOKEN + SLACK_APP_TOKEN
|
|
864
|
+
export default slackChannel();
|
|
865
|
+
|
|
866
|
+
// Multi-agent serve — one Slack app (and token pair) per agent
|
|
867
|
+
export default slackChannel({ envPrefix: "WEATHER_AGENT" });
|
|
868
|
+
// → WEATHER_AGENT_SLACK_BOT_TOKEN + WEATHER_AGENT_SLACK_APP_TOKEN
|
|
869
|
+
|
|
870
|
+
// Cursor account connection — no dedicated Slack app. `@Cursor Weatherbot …`
|
|
871
|
+
// routes here; replies post as "Weatherbot" through the Cursor Slack app.
|
|
872
|
+
export default slackChannel({ cursorAccount: true, agentName: "Weatherbot" });
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
Transport uses `@slack/socket-mode` + `@slack/web-api`. Socket Mode starts
|
|
876
|
+
on channel mount when both tokens are present; otherwise the channel stays
|
|
877
|
+
**idle** (`channel idle … missing credentials`) so multi-agent serve can
|
|
878
|
+
mount agents that do not have Slack tokens configured yet.
|
|
879
|
+
|
|
880
|
+
The pack dispatches via `waitUntil`, streams assistant text (`chat.startStream` /
|
|
881
|
+
`appendStream` / `stopStream`) with postMessage fallback, shows rotating status
|
|
882
|
+
+ tool thinking steps, and sets thread titles / suggested prompts. Handlers may
|
|
883
|
+
return a prepared `message` / `workspaceFiles` / `cloud` to host-prepare PR
|
|
884
|
+
reviews or attach
|
|
885
|
+
cloud repos from an `@mention`.
|
|
886
|
+
|
|
887
|
+
**Engagement:** by default the agent is summoned, never proactive — it
|
|
888
|
+
dispatches only on `app_mention` and DMs. Channel watch is an explicit opt-in:
|
|
889
|
+
|
|
890
|
+
```ts
|
|
891
|
+
export default slackChannel({
|
|
892
|
+
envPrefix: "TRIAGE",
|
|
893
|
+
engagement: {
|
|
894
|
+
// mentions / directMessages default to true
|
|
895
|
+
channelPosts: {
|
|
896
|
+
allow: ["#triage-alerts"], // explicit allowlist only; no wildcard
|
|
897
|
+
posts: "top-level", // default: never dispatch on thread replies
|
|
898
|
+
debounceMs: 15_000, // optional: let rapid edits settle
|
|
899
|
+
},
|
|
900
|
+
},
|
|
901
|
+
onChannelPost: async (ctx, message) => {
|
|
902
|
+
// Same contract as onAppMention: return null to skip.
|
|
903
|
+
return message.markdown.length > 20 ? {} : null;
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
Watched posts dispatch with the same thread-scoped principal as mentions
|
|
909
|
+
(so a later `@mention` continues the session) plus an
|
|
910
|
+
`engagement: "channel_post"` auth attribute. Posts that mention the bot are
|
|
911
|
+
left to the `app_mention` path; bot-authored posts never dispatch; deleting
|
|
912
|
+
a post inside the debounce window cancels its dispatch. The Slack app must
|
|
913
|
+
subscribe to `message.channels` / `message.groups`
|
|
914
|
+
(`agent-serve slack init --channel-posts`, or add the events to an existing
|
|
915
|
+
app) and be a member of each watched channel. Watched channels surface in
|
|
916
|
+
`info` via `meta.slackChannelPosts`.
|
|
917
|
+
|
|
918
|
+
**HITL compose:** park/resume is independent (`needsApproval` on tools). Opt in
|
|
919
|
+
to Slack delivery with `toolApprovals: true` (or spread
|
|
920
|
+
`buildToolApprovalEvents` into `events` and set `interactivity: true` on
|
|
921
|
+
`slackChannel` so Approve/Deny buttons resolve). That posts Block Kit
|
|
922
|
+
Approve/Deny cards and routes Socket Mode `interactive` clicks to
|
|
923
|
+
`resolveApproval`. Enable `interactivity: true` on
|
|
924
|
+
`buildSlackManifest` when you use that surface. Threads bind with
|
|
925
|
+
`continuationToken = channelId:threadTs`. Dev and prod are
|
|
926
|
+
**separate Slack apps**. Requires `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN`
|
|
927
|
+
(App-Level Token with `connections:write`).
|
|
928
|
+
|
|
929
|
+
Most demos under `examples/` use the Cursor Slack connection
|
|
930
|
+
(`cursorAccount: true`) with a unique `agentName` — `@Cursor Benny …`,
|
|
931
|
+
`@Cursor Bugbot …`, etc. Sign the host in, then mention the agent; no
|
|
932
|
+
per-agent Slack app required for chat. Agents that need channel watch or
|
|
933
|
+
tool approvals keep a Socket Mode app in a second channel file (e.g.
|
|
934
|
+
`slack-app.ts`) with their own env prefix (`<PREFIX>_SLACK_BOT_TOKEN` +
|
|
935
|
+
`<PREFIX>_SLACK_APP_TOKEN`, derived from the directory name by `slack init`).
|
|
936
|
+
|
|
937
|
+
| Agent | `@Cursor` name | Optional Socket Mode app |
|
|
938
|
+
| --- | --- | --- |
|
|
939
|
+
| `weather-agent` | Weather | `slack-app.ts` (`WEATHER_AGENT_SLACK_*`, tool approvals) |
|
|
940
|
+
| `slack-agent` | Slack Agent | — |
|
|
941
|
+
| `bugbot` | Bugbot | — |
|
|
942
|
+
| `fsd` | FSD | — |
|
|
943
|
+
|
|
944
|
+
PR-oriented demos (`bugbot`, `fsd`) extract a GitHub PR URL / `owner/repo#N`
|
|
945
|
+
from the mention and run the same host path as their HTTP channels.
|
|
946
|
+
`security-reviewer` is GitHub-task-only (no Slack channel); `concierge` is
|
|
947
|
+
agent-to-agent only (no Slack channel).
|
|
948
|
+
|
|
949
|
+
For Socket Mode apps (channel watch / approvals): generate manifests →
|
|
950
|
+
create apps → install + mint app token → env →
|
|
951
|
+
`agent-serve slack doctor --prefix …` → `serve`.
|
|
952
|
+
|
|
953
|
+
CLI (`agent-serve slack …`; guided setup in `skills/setup-slack/SKILL.md`):
|
|
954
|
+
|
|
955
|
+
```bash
|
|
956
|
+
agent-serve slack setup
|
|
957
|
+
agent-serve slack init --dir ./my-agent --name "My Agent"
|
|
958
|
+
agent-serve slack doctor --prefix MY_AGENT
|
|
959
|
+
```
|
|
960
|
+
|
|
961
|
+
`init` / `manifest` write `.agent-serve/slack/manifest.{dev,prod}.json` and
|
|
962
|
+
`env.example`.
|
|
963
|
+
|
|
964
|
+
**GitHub** (`@cursor/july/channels/github`): a webhook channel pack
|
|
965
|
+
(Eve-compatible dispatch). Author `agent/channels/github.ts` with
|
|
966
|
+
`githubChannel()` and declare inbound hooks:
|
|
967
|
+
|
|
968
|
+
```ts
|
|
969
|
+
import { defaultGitHubAuth, githubChannel } from "@cursor/july/channels/github";
|
|
970
|
+
|
|
971
|
+
export default githubChannel({
|
|
972
|
+
botName: "my-agent", // or GITHUB_APP_SLUG
|
|
973
|
+
credentials: { webhookSecret: () => process.env.GITHUB_WEBHOOK_SECRET },
|
|
974
|
+
onPullRequest: (ctx, pr) =>
|
|
975
|
+
pr.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null,
|
|
976
|
+
onCheckSuite: (ctx, suite) =>
|
|
977
|
+
suite.conclusion === "failure" ? { task: () => triage(ctx) } : null,
|
|
978
|
+
});
|
|
979
|
+
```
|
|
980
|
+
|
|
981
|
+
Mounts at `POST /<slug>/v1/channels/github`. When a webhook secret is set the
|
|
982
|
+
channel verifies `X-Hub-Signature-256` before parsing (the HMAC becomes the
|
|
983
|
+
request auth); without one it stays loopback-only (`localDevStrict()`) — except
|
|
984
|
+
under `serve --dev`, which admits unsigned loopback deliveries so
|
|
985
|
+
`gh webhook forward` and fixtures work with zero config. Hooks return
|
|
986
|
+
`{ auth }` to start a model turn as the actor, `{ task }` for host-side work
|
|
987
|
+
(202 ACK, runs past GitHub's ~10s timeout), or `null` to skip.
|
|
988
|
+
|
|
989
|
+
**Testing GitHub agents locally.** API auth prefers GitHub App installation
|
|
990
|
+
tokens when `GITHUB_APP_ID` / `GITHUB_APP_PRIVATE_KEY` (and an installation id)
|
|
991
|
+
are set; for local testing you can omit those and use `GITHUB_TOKEN` /
|
|
992
|
+
`GH_TOKEN` or `gh auth login` instead. The channel publishes the webhook events
|
|
993
|
+
it dispatches on (derived from the declared hooks, or pinned via
|
|
994
|
+
`webhookEvents`), so `agent-serve github …` can forward live deliveries with
|
|
995
|
+
zero hand-listing — it wraps [`gh webhook
|
|
996
|
+
forward`](https://docs.github.com/en/webhooks/testing-and-troubleshooting-webhooks/using-the-github-cli-to-forward-webhooks-for-testing):
|
|
997
|
+
|
|
998
|
+
```bash
|
|
999
|
+
# One-time: gh + the cli/gh-webhook extension
|
|
1000
|
+
agent-serve github doctor --install
|
|
1001
|
+
|
|
1002
|
+
# Terminal A: serve the agent (dev accepts unsigned loopback deliveries)
|
|
1003
|
+
agent-serve serve --dir ./my-agent --dev
|
|
1004
|
+
|
|
1005
|
+
# Terminal B: forward this repo's deliveries to the discovered github channel
|
|
1006
|
+
# (URL + events auto-derived; repo inferred from the git remote). No secret
|
|
1007
|
+
# needed against a --dev server; set GITHUB_WEBHOOK_SECRET to verify signatures.
|
|
1008
|
+
agent-serve github forward --dir ./my-agent
|
|
1009
|
+
|
|
1010
|
+
# Inspect what would be forwarded (URL + events per agent)
|
|
1011
|
+
agent-serve github events --dir ./agents --json
|
|
1012
|
+
|
|
1013
|
+
# Forward to EVERY discovered github channel at once
|
|
1014
|
+
agent-serve github forward --dir ./agents
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
When several channels match (e.g. a folder of agent projects with more than
|
|
1018
|
+
one github channel), one forwarder fans out to all of them: `gh webhook
|
|
1019
|
+
forward` runs against a local proxy that re-posts each raw (still-signed)
|
|
1020
|
+
delivery to the channels whose event set matches. This is required — GitHub
|
|
1021
|
+
allows only one forwarder per repo, and `gh webhook forward` targets a single
|
|
1022
|
+
URL, so N processes would collide with `Hook already exists`.
|
|
1023
|
+
|
|
1024
|
+
`gh webhook forward` needs **admin** on the repo (or org owner for `--org`) —
|
|
1025
|
+
it registers a real webhook — and authenticates its relay with the GitHub CLI's
|
|
1026
|
+
own login. If `GITHUB_TOKEN` / `GH_TOKEN` is set in your env, deliveries fail
|
|
1027
|
+
with **HTTP 401** (the relay rejects env tokens); blank it for the command
|
|
1028
|
+
(`GITHUB_TOKEN= GH_TOKEN= agent-serve github forward …`) or `unset` it.
|
|
1029
|
+
`agent-serve github doctor` / `forward` warn when they detect this.
|
|
1030
|
+
|
|
1031
|
+
Pass `--repo owner/repo` / `--org ORG` to override the inferred target,
|
|
1032
|
+
`--events a,b,c` to narrow the set, `--url` for a custom endpoint (e.g. a
|
|
1033
|
+
tunnel), and `--slug` / `--channel` to forward to just one of several agents.
|
|
1034
|
+
Against a `--dev` server no secret is needed; set `GITHUB_WEBHOOK_SECRET` (or
|
|
1035
|
+
`--secret`) to exercise signature verification (required for a non-dev target).
|
|
1036
|
+
Only one forwarder per repo/org at a time (a GitHub limitation). Fixture replay
|
|
1037
|
+
still works too — `POST` a saved payload with an `x-github-event` header (no
|
|
1038
|
+
signature needed in `--dev`).
|
|
1039
|
+
|
|
1040
|
+
**No admin? Hillclimbing? Use `github replay`.** `gh webhook forward` needs repo
|
|
1041
|
+
admin and a live event. `agent-serve github replay <pr_url>` instead **reads**
|
|
1042
|
+
the PR (pull access is enough — no admin, no relay, and `GITHUB_TOKEN` is fine)
|
|
1043
|
+
and synthesizes GitHub-shaped payloads it POSTs straight at the channel:
|
|
1044
|
+
|
|
1045
|
+
```bash
|
|
1046
|
+
# Replay a pull_request delivery for a PR to the discovered channel
|
|
1047
|
+
agent-serve github replay https://github.com/owner/repo/pull/123 --dir ./my-agent
|
|
1048
|
+
|
|
1049
|
+
# Replay everything the channel listens for (its declared events), CI failing
|
|
1050
|
+
agent-serve github replay owner/repo#123 --dir ./my-agent --events '*' --conclusion failure
|
|
1051
|
+
|
|
1052
|
+
# Inspect payloads without POSTing (and snapshot them as reusable fixtures)
|
|
1053
|
+
agent-serve github replay owner/repo#123 --dir ./my-agent --events '*' --dry-run --out fixtures/github
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
`--events` defaults to `pull_request` (`*` = the channel's declared events);
|
|
1057
|
+
`--action` / `--conclusion` / `--comment` / `--context` tune each synthesized
|
|
1058
|
+
event; `--secret` (or `GITHUB_WEBHOOK_SECRET`) signs them so a secret-configured
|
|
1059
|
+
channel verifies. Because replay sends a clean, signed loopback request, it
|
|
1060
|
+
passes both the `localDevStrict()` and `allowAll()`+signature auth modes — and
|
|
1061
|
+
it's fully deterministic, which is what hillclimbing wants.
|
|
1062
|
+
|
|
1063
|
+
**Production alternative: pull from Cursor (`--cursor-events`).** If the
|
|
1064
|
+
Cursor GitHub App is on the repo, `serve --cursor-events --repo owner/repo`
|
|
1065
|
+
long-polls `/v0/scm-events` with the host's Cursor account (no public URL /
|
|
1066
|
+
repo admin). Offset + consumer id live under `<state-root>/cursor-events/`.
|
|
1067
|
+
The stream is read as your Cursor user, so the flag **requires a signed-in
|
|
1068
|
+
host** (`agent-serve login`, `CURSOR_API_KEY`, or `serve({ apiKey })`) — serve
|
|
1069
|
+
fails fast rather than starting with a relay that can never receive events.
|
|
1070
|
+
|
|
1071
|
+
### Hooks (`agent/hooks/*.ts`)
|
|
1072
|
+
Observe-only subscribers that run after each event is recorded — audit
|
|
1073
|
+
logs, metrics, mirroring transcripts into your own store. Keys are event
|
|
1074
|
+
types (or `*`); handler errors are logged, never fatal.
|
|
1075
|
+
|
|
1076
|
+
### Schedules (`agent/schedules/*`)
|
|
1077
|
+
|
|
1078
|
+
Markdown form (fire-and-forget task session):
|
|
1079
|
+
|
|
1080
|
+
```md
|
|
1081
|
+
---
|
|
1082
|
+
cron: "0 9 * * 1-5"
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
Pull open incidents and post a summary to the metrics endpoint.
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
Handler form (full control, hand off to a channel):
|
|
1089
|
+
|
|
1090
|
+
```ts
|
|
1091
|
+
import { defineSchedule } from "@cursor/july/schedules";
|
|
1092
|
+
import webhook from "../channels/webhook.js";
|
|
1093
|
+
|
|
1094
|
+
export default defineSchedule({
|
|
1095
|
+
cron: "*/30 * * * *",
|
|
1096
|
+
async run({ receive, waitUntil, appAuth, mcp }) {
|
|
1097
|
+
// optional: await mcp.callTool("units", "celsius_to_fahrenheit", { value: 0 });
|
|
1098
|
+
waitUntil(
|
|
1099
|
+
receive(webhook, {
|
|
1100
|
+
message: "Check for new critical alerts. Report only when there are any.",
|
|
1101
|
+
auth: appAuth,
|
|
1102
|
+
})
|
|
1103
|
+
);
|
|
1104
|
+
},
|
|
1105
|
+
});
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
Cron expressions are standard 5-field, evaluated in UTC with minute
|
|
1109
|
+
granularity. In production mode (`agent-serve serve`) schedules fire on
|
|
1110
|
+
cadence; in dev mode (`--dev`) they never fire automatically — dispatch one
|
|
1111
|
+
by hand, exactly once, through the same path production uses:
|
|
1112
|
+
|
|
1113
|
+
```bash
|
|
1114
|
+
curl -X POST http://127.0.0.1:3000/<slug>/v1/dev/schedules/heartbeat
|
|
1115
|
+
# {"scheduleId":"heartbeat","sessionIds":["ses_..."]}
|
|
1116
|
+
```
|
|
1117
|
+
|
|
1118
|
+
## Sessions, events, and streaming
|
|
1119
|
+
|
|
1120
|
+
Two handles do two jobs: the **continuation token** resumes a conversation
|
|
1121
|
+
(owned by the channel; one active continuation per session, stale tokens
|
|
1122
|
+
rejected), and the **session id** streams and inspects it (owned by the
|
|
1123
|
+
runtime).
|
|
1124
|
+
|
|
1125
|
+
### Reminders
|
|
1126
|
+
|
|
1127
|
+
Per-session durable wakes (distinct from deploy-time `agent/schedules/`):
|
|
1128
|
+
|
|
1129
|
+
```ts
|
|
1130
|
+
await handle.createReminder({
|
|
1131
|
+
purpose: "ci_recheck",
|
|
1132
|
+
channelId: "drive",
|
|
1133
|
+
continuationToken: "pr-owner-repo-1",
|
|
1134
|
+
delay: "2h",
|
|
1135
|
+
prompt: "Re-check CI. Only act if still failing.",
|
|
1136
|
+
until: "Cancel once CI is green or the PR is merged.",
|
|
1137
|
+
});
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
Host/policy packs may pass `run` (return `stop` / `skip` / `delivered`)
|
|
1141
|
+
instead of prompts. In `--dev`, use `POST /v1/dev/reminders/:id` (or
|
|
1142
|
+
`handle.dispatchReminder`) to fire; auto-timers follow
|
|
1143
|
+
`ServeOptions.reminders` (default `!dev`). Run handlers are in-memory —
|
|
1144
|
+
after restart those reminders are disarmed (`handler_lost_on_restart`);
|
|
1145
|
+
re-arm from enroll/policy.
|
|
1146
|
+
|
|
1147
|
+
The NDJSON stream vocabulary — one JSON object per line, each carrying
|
|
1148
|
+
`{ type, index, sessionId, turnId?, at, data }`:
|
|
1149
|
+
|
|
1150
|
+
| Event | Meaning |
|
|
1151
|
+
| --------------------- | -------------------------------------------------------------- |
|
|
1152
|
+
| `session.started` | A durable session was created. |
|
|
1153
|
+
| `agent.bound` | Cursor agent id is known (`sdkAgentId`; cloud: `bc-…` + URL). |
|
|
1154
|
+
| `ab.assigned` | Sticky A/B enrollment (`experiment`, `variant` or `null` skip). |
|
|
1155
|
+
| `message.received` | An inbound user message was accepted. |
|
|
1156
|
+
| `turn.started` | A turn began. |
|
|
1157
|
+
| `step.started` / `step.completed` | Model step boundaries (with duration). |
|
|
1158
|
+
| `reasoning.appended` / `reasoning.completed` | Reasoning deltas and the block end. |
|
|
1159
|
+
| `message.appended` | Assistant text delta (with cumulative text so far). |
|
|
1160
|
+
| `message.completed` | A finalized assistant text block (`finishReason`: `stop` or `tool_call`). |
|
|
1161
|
+
| `actions.requested` | The model requested a tool call (streams before execution). |
|
|
1162
|
+
| `action.approval_requested` | A `needsApproval` tool is parked awaiting a human. |
|
|
1163
|
+
| `action.approval_resolved` | Human approved or denied the parked tool call. |
|
|
1164
|
+
| `action.result` | A tool call returned (`output`, `isError`). |
|
|
1165
|
+
| `subagent.called` / `subagent.completed` | Delegation to a subagent. |
|
|
1166
|
+
| `turn.completed` | The turn finished (`result`, `usage`). |
|
|
1167
|
+
| `turn.failed` | The turn failed (`message`). |
|
|
1168
|
+
| `session.waiting` | The session parked, ready for the next message. |
|
|
1169
|
+
| `session.completed` / `session.failed` | Terminal states for task-mode (schedule) sessions. |
|
|
1170
|
+
|
|
1171
|
+
The stream is durable: reconnect with `?startIndex=<n>` to replay from any
|
|
1172
|
+
point, or from 0 to rewind the whole session.
|
|
1173
|
+
|
|
1174
|
+
## Serving programmatically
|
|
1175
|
+
|
|
1176
|
+
```ts
|
|
1177
|
+
import { serve } from "@cursor/july";
|
|
1178
|
+
|
|
1179
|
+
const handle = await serve("./my-agent", {
|
|
1180
|
+
port: 3000,
|
|
1181
|
+
apiKey: process.env.CURSOR_API_KEY, // optional — see credential order below
|
|
1182
|
+
});
|
|
1183
|
+
console.log(`listening on ${handle.url}`);
|
|
1184
|
+
// handle.dispatchSchedule("heartbeat"), handle.project, await handle.close()
|
|
1185
|
+
```
|
|
1186
|
+
|
|
1187
|
+
The Cursor credential resolves in one order everywhere (SDK turns, cloud
|
|
1188
|
+
runtime, Cursor account MCP connections): explicit `apiKey` option / `--api-key`
|
|
1189
|
+
→ `CURSOR_API_KEY` → the key stored by `agent-serve login`. `whoami` shows
|
|
1190
|
+
which one is active; `logout` removes the stored key (revoke it in the
|
|
1191
|
+
Cursor dashboard to kill it outright).
|
|
1192
|
+
|
|
1193
|
+
Local-dev note: login/account RPCs honor `CURSOR_API_BASE_URL` while the
|
|
1194
|
+
Cursor SDK harness honors `CURSOR_BACKEND_URL`. When pointing at a
|
|
1195
|
+
non-production backend, set **both** to the same URL — a key minted on one
|
|
1196
|
+
backend is rejected by the other.
|
|
1197
|
+
|
|
1198
|
+
`serve` refuses to start when discovery produced error diagnostics; run
|
|
1199
|
+
`agent-serve validate` (or read `project.diagnostics`) to see why.
|
|
1200
|
+
|
|
1201
|
+
State lives under `<project>/.agent-serve/` (override with `stateRoot` /
|
|
1202
|
+
`--state-root`): `sessions/<id>/{session.json,events.ndjson,workspace/}`
|
|
1203
|
+
plus the SDK conversation store under `runner/`. Delete a session directory
|
|
1204
|
+
to forget that conversation.
|
|
1205
|
+
|
|
1206
|
+
Session workspaces are real Cursor project directories, so the harness also
|
|
1207
|
+
loads ambient project config from **ancestor** directories (nested
|
|
1208
|
+
`AGENTS.md` / `.cursor` rules and skills). That's usually what you want when
|
|
1209
|
+
the agent project is its own repository — but when it sits inside a large
|
|
1210
|
+
monorepo, either set `local: { cwd }` on `defineAgent` to a directory outside
|
|
1211
|
+
the monorepo (each session uses `<cwd>/<sessionId>`), or point `--state-root`
|
|
1212
|
+
somewhere outside (e.g. under `/tmp` or XDG state) so sessions don't inherit
|
|
1213
|
+
the monorepo's rules into context.
|
|
1214
|
+
|
|
1215
|
+
## Not supported (yet)
|
|
1216
|
+
|
|
1217
|
+
Deliberately out of scope for now, and reported as warnings where the
|
|
1218
|
+
corresponding folder exists: per-subagent tools/skills/mcp-connections, nested
|
|
1219
|
+
subagents, WebSocket channel routes, platform channel packs for Discord
|
|
1220
|
+
and Teams (Slack is supported via `@cursor/july/channels/slack`;
|
|
1221
|
+
other platforms still use the authored `defineChannel` webhook form),
|
|
1222
|
+
OAuth-brokered MCP connections beyond the Cursor account
|
|
1223
|
+
(`{ cursorAccount: true }` covers connectors the account already
|
|
1224
|
+
authorized in Cursor), custom sandbox backends (`sandbox.ts`),
|
|
1225
|
+
instrumentation modules, dynamic per-caller capabilities, structured
|
|
1226
|
+
output schemas, and file uploads. Human-in-the-loop tool approvals
|
|
1227
|
+
(`needsApproval` on `execution: "server"` tools) are supported; exact
|
|
1228
|
+
resume of a parked tool call after host restart is not. Filesystem
|
|
1229
|
+
evals (`defineEval` under `evals/`), live A/B metrics (`defineAB` under
|
|
1230
|
+
`agent/ab`), and the `run` / `trajectory` commands are supported;
|
|
1231
|
+
LLM-as-judge scoring and external reporters are not yet. Context
|
|
1232
|
+
compaction is handled by the Cursor harness rather than
|
|
1233
|
+
configured here.
|