@cursor/july 0.1.107 → 0.1.108
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/README.md +10 -5
- package/dist/bin/agent-serve.js +30 -8
- package/dist/channels/github/github-channel.js +8 -5
- package/dist/channels/github/types.d.ts +5 -1
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.d.ts.map +1 -1
- package/dist/channels/origin/origin-channel.js +27 -13
- package/dist/channels/origin/types.d.ts +5 -1
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels.d.ts +45 -0
- package/dist/channels.d.ts.map +1 -1
- package/dist/channels.js +106 -7
- package/dist/connections.d.ts +2 -1
- package/dist/connections.d.ts.map +1 -1
- package/dist/connections.js +2 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +4 -4
- package/dist/docs/assets/{app.CtuckIiL.js → app.BASKM3M4.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BHZYsNVi.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.Bkkhnu8K.js → VPLocalSearchBox.BOKxlYGP.js} +1 -1
- package/dist/docs/assets/chunks/{theme.DhpIe0Pa.js → theme.DbDZW-zb.js} +2 -2
- package/dist/docs/assets/{deployment.md.MFPKqsqI.js → deployment.md.JenwuCCB.js} +4 -4
- package/dist/docs/assets/{deployment.md.MFPKqsqI.lean.js → deployment.md.JenwuCCB.lean.js} +1 -1
- package/dist/docs/assets/{evals.md.CbMoebP1.js → evals.md.C1ekS3k2.js} +1 -1
- package/dist/docs/assets/guides_cloud-agents.md.BsloyHdY.js +9 -0
- package/dist/docs/assets/{guides_cloud-agents.md.DS8RIjwx.lean.js → guides_cloud-agents.md.BsloyHdY.lean.js} +1 -1
- package/dist/docs/assets/{guides_github.md.c0gdGJ-D.js → guides_github.md.BH33UEBJ.js} +1 -1
- package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.js → guides_grokbot-agents.md.DyV-WArv.js} +1 -1
- package/dist/docs/assets/guides_improve.md.BGVk32bK.js +14 -0
- package/dist/docs/assets/guides_improve.md.BGVk32bK.lean.js +1 -0
- package/dist/docs/assets/{guides_slack.md.9oHPye9o.js → guides_slack.md.D4RVMM4G.js} +3 -3
- package/dist/docs/assets/{guides_slack.md.9oHPye9o.lean.js → guides_slack.md.D4RVMM4G.lean.js} +1 -1
- package/dist/docs/assets/index.md.BjH1w2ZW.js +5 -0
- package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.js → reference_agent-config.md.CvoL6pof.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.Dm67hd2D.js → reference_cli.md.Ca26u0Es.js} +6 -6
- package/dist/docs/assets/{reference_cli.md.Dm67hd2D.lean.js → reference_cli.md.Ca26u0Es.lean.js} +1 -1
- package/dist/docs/assets/{reference_connections.md.Di6jJAXF.js → reference_connections.md.BojkC6c5.js} +1 -1
- package/dist/docs/assets/{reference_extensions.md.CGmMLblt.js → reference_extensions.md.CPWt00ds.js} +1 -1
- package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.js → scaffolding-agents.md.Kctn3OVb.js} +1 -1
- package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.js → skills_create-agent.md.Q3h6Je-e.js} +1 -1
- package/dist/docs/assets/{skills_debug.md.CDbPhHfg.js → skills_debug.md.CVjCXMFF.js} +1 -1
- package/dist/docs/assets/{skills_debug.md.CDbPhHfg.lean.js → skills_debug.md.CVjCXMFF.lean.js} +1 -1
- package/dist/docs/assets/{skills_evals.md.723kpUmA.js → skills_evals.md.BhovOvrl.js} +1 -1
- package/dist/docs/assets/skills_framework-map.md.D-tFZhFS.js +1 -0
- package/dist/docs/assets/{skills_framework-map.md.haibFyoB.lean.js → skills_framework-map.md.D-tFZhFS.lean.js} +1 -1
- package/dist/docs/assets/{skills_github.md.D0JahM8c.js → skills_github.md.hgFX_oKY.js} +1 -1
- package/dist/docs/assets/troubleshooting.md.CYEAO9bM.js +1 -0
- package/dist/docs/building-with-agents.html +4 -4
- package/dist/docs/concepts.html +4 -4
- package/dist/docs/deployment.html +7 -7
- package/dist/docs/deployment.md +7 -2
- package/dist/docs/evals.html +6 -6
- package/dist/docs/evals.md +3 -2
- package/dist/docs/guides/agent-to-agent.html +4 -4
- package/dist/docs/guides/cloud-agents.html +6 -6
- package/dist/docs/guides/cloud-agents.md +4 -3
- package/dist/docs/guides/convert-automation.html +4 -4
- package/dist/docs/guides/github.html +5 -5
- package/dist/docs/guides/github.md +2 -1
- package/dist/docs/guides/grokbot-agents.html +6 -6
- package/dist/docs/guides/grokbot-agents.md +2 -2
- package/dist/docs/guides/human-in-the-loop.html +4 -4
- package/dist/docs/guides/improve.html +40 -0
- package/dist/docs/guides/improve.md +91 -0
- package/dist/docs/guides/mcp-oauth.html +4 -4
- package/dist/docs/guides/opentelemetry.html +5 -5
- package/dist/docs/guides/slack.html +6 -6
- package/dist/docs/guides/slack.md +2 -1
- package/dist/docs/guides/webhooks.html +4 -4
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +4 -4
- package/dist/docs/index.html +6 -6
- package/dist/docs/index.md +4 -0
- package/dist/docs/llms-full.txt +158 -33
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +4 -4
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/agent-config.md +5 -2
- package/dist/docs/reference/artifacts.html +4 -4
- package/dist/docs/reference/channels.html +4 -4
- package/dist/docs/reference/cli.html +10 -10
- package/dist/docs/reference/cli.md +18 -11
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/connections.md +2 -1
- package/dist/docs/reference/extensions.html +6 -6
- package/dist/docs/reference/extensions.md +2 -0
- package/dist/docs/reference/hooks.html +4 -4
- package/dist/docs/reference/http-api.html +4 -4
- package/dist/docs/reference/instructions.html +4 -4
- package/dist/docs/reference/playground.html +4 -4
- package/dist/docs/reference/project-layout.html +4 -4
- package/dist/docs/reference/prompt.html +4 -4
- package/dist/docs/reference/result.html +4 -4
- package/dist/docs/reference/schedules.html +4 -4
- package/dist/docs/reference/sessions.html +4 -4
- package/dist/docs/reference/skills.html +4 -4
- package/dist/docs/reference/subagents.html +4 -4
- package/dist/docs/reference/tools.html +4 -4
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/scaffolding-agents.md +2 -1
- package/dist/docs/skills/ab.html +4 -4
- package/dist/docs/skills/create-agent.html +6 -6
- package/dist/docs/skills/create-agent.md +1 -1
- package/dist/docs/skills/debug.html +5 -5
- package/dist/docs/skills/debug.md +1 -1
- package/dist/docs/skills/evals.html +6 -6
- package/dist/docs/skills/evals.md +1 -1
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/framework-map.md +4 -2
- package/dist/docs/skills/github.html +6 -6
- package/dist/docs/skills/github.md +1 -1
- package/dist/docs/skills/hillclimb.html +4 -4
- package/dist/docs/skills/index.html +4 -4
- package/dist/docs/skills/mcp-auth.html +4 -4
- package/dist/docs/skills/otel.html +4 -4
- package/dist/docs/skills/setup-slack.html +4 -4
- package/dist/docs/storage.html +4 -4
- package/dist/docs/templates/agentic-owners.html +4 -4
- package/dist/docs/templates/agents-md.html +4 -4
- package/dist/docs/templates/code-wiki.html +4 -4
- package/dist/docs/templates/demo.html +4 -4
- package/dist/docs/templates/grokbot-agents.html +4 -4
- package/dist/docs/templates/pr-autofixer.html +4 -4
- package/dist/docs/templates/security-help.html +4 -4
- package/dist/docs/templates/security-reviewer.html +4 -4
- package/dist/docs/templates/triage.html +4 -4
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/docs/troubleshooting.md +1 -1
- package/dist/extensions/improve/extension.d.ts +46 -0
- package/dist/extensions/improve/extension.d.ts.map +1 -0
- package/dist/extensions/improve/extension.js +41 -0
- package/dist/extensions/improve/skills/yourself.d.ts +4 -0
- package/dist/extensions/improve/skills/yourself.d.ts.map +1 -0
- package/dist/extensions/improve/skills/yourself.js +43 -0
- package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -1
- package/dist/internal/builtin-tools/artifacts.js +2 -3
- package/dist/internal/channel-route-dispatch.d.ts +13 -0
- package/dist/internal/channel-route-dispatch.d.ts.map +1 -0
- package/dist/internal/channel-route-dispatch.js +62 -0
- package/dist/internal/channel-state.d.ts +17 -0
- package/dist/internal/channel-state.d.ts.map +1 -0
- package/dist/internal/channel-state.js +78 -0
- package/dist/internal/cli-ax.d.ts +7 -0
- package/dist/internal/cli-ax.d.ts.map +1 -1
- package/dist/internal/cli-ax.js +99 -1
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +2 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +38 -14
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
- package/dist/internal/cli-mcp-oauth.js +15 -0
- package/dist/internal/cli-mcp.d.ts.map +1 -1
- package/dist/internal/cli-mcp.js +12 -0
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +8 -2
- package/dist/internal/cursor/credentials.d.ts +8 -2
- package/dist/internal/cursor/credentials.d.ts.map +1 -1
- package/dist/internal/cursor/credentials.js +27 -5
- package/dist/internal/deploy-client.d.ts +27 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +32 -0
- package/dist/internal/deploy-manifest.d.ts +23 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +84 -1
- package/dist/internal/describe-error.d.ts +1 -0
- package/dist/internal/describe-error.d.ts.map +1 -1
- package/dist/internal/describe-error.js +25 -0
- package/dist/internal/guest-network.d.ts +4 -10
- package/dist/internal/guest-network.d.ts.map +1 -1
- package/dist/internal/guest-network.js +42 -26
- package/dist/internal/hosted-admission-adapter.d.ts +2 -0
- package/dist/internal/hosted-admission-adapter.d.ts.map +1 -1
- package/dist/internal/hosted-catch-protocol.d.ts +51 -0
- package/dist/internal/hosted-catch-protocol.d.ts.map +1 -0
- package/dist/internal/hosted-catch-protocol.js +103 -0
- package/dist/internal/hosted-catch.d.ts +40 -0
- package/dist/internal/hosted-catch.d.ts.map +1 -0
- package/dist/internal/hosted-catch.js +149 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +41 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +249 -0
- package/dist/internal/hosted-delivery.d.ts +10 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +100 -22
- package/dist/internal/hosted-execution-diag.d.ts +4 -1
- package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
- package/dist/internal/hosted-execution-diag.js +13 -4
- package/dist/internal/hosted-execution-flush.d.ts +3 -0
- package/dist/internal/hosted-execution-flush.d.ts.map +1 -1
- package/dist/internal/hosted-execution-flush.js +2 -4
- package/dist/internal/http-control-plane-session.d.ts +30 -0
- package/dist/internal/http-control-plane-session.d.ts.map +1 -0
- package/dist/internal/http-control-plane-session.js +83 -0
- package/dist/internal/legacy-command-guard.d.ts +22 -0
- package/dist/internal/legacy-command-guard.d.ts.map +1 -0
- package/dist/internal/legacy-command-guard.js +51 -0
- package/dist/internal/platform-timers.d.ts +7 -0
- package/dist/internal/platform-timers.d.ts.map +1 -1
- package/dist/internal/platform-timers.js +139 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts +2 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -1
- package/dist/internal/reminder-control-plane-protocol.js +9 -2
- package/dist/internal/reminder-runner.d.ts +8 -0
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +36 -9
- package/dist/internal/resolve-prod-target.d.ts +2 -0
- package/dist/internal/resolve-prod-target.d.ts.map +1 -1
- package/dist/internal/resolve-prod-target.js +13 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +4 -3
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +57 -56
- package/dist/internal/session-engine.d.ts +2 -6
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +95 -67
- package/dist/playground/assets/{index-Bq2HpEQB.js → index-BMDqAeXu.js} +28 -28
- package/dist/playground/assets/index-LUgJoWdL.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage-backends/cursor-hosted.d.ts +6 -0
- package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/storage-backends/cursor-hosted.js +29 -1
- package/dist/types.d.ts +33 -5
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +4 -0
- package/docs/deployment.md +7 -2
- package/docs/evals.md +3 -2
- package/docs/guides/cloud-agents.md +4 -3
- package/docs/guides/github.md +2 -1
- package/docs/guides/grokbot-agents.md +2 -2
- package/docs/guides/improve.md +96 -0
- package/docs/guides/slack.md +2 -1
- package/docs/reference/agent-config.md +5 -2
- package/docs/reference/cli.md +18 -11
- package/docs/reference/connections.md +2 -1
- package/docs/reference/extensions.md +2 -0
- package/docs/scaffolding-agents.md +2 -1
- package/docs/troubleshooting.md +1 -1
- package/package.json +8 -1
- package/skills/create-agent/SKILL.md +1 -1
- package/skills/debug/SKILL.md +1 -1
- package/skills/evals/SKILL.md +1 -1
- package/skills/framework-map/SKILL.md +4 -2
- package/skills/github/SKILL.md +1 -1
- package/src/bin/agent-serve.ts +27 -2
- package/src/channels/github/github-channel.ts +8 -8
- package/src/channels/github/types.ts +5 -0
- package/src/channels/origin/origin-channel.ts +29 -13
- package/src/channels/origin/types.ts +5 -0
- package/src/channels.ts +157 -10
- package/src/connections.ts +2 -1
- package/src/extensions/improve/extension.ts +70 -0
- package/src/extensions/improve/skills/yourself.ts +50 -0
- package/src/internal/builtin-tools/artifacts.ts +2 -3
- package/src/internal/channel-route-dispatch.ts +66 -0
- package/src/internal/channel-state.ts +96 -0
- package/src/internal/cli-ax.ts +110 -1
- package/src/internal/cli-cursor.ts +4 -1
- package/src/internal/cli-deploy.ts +50 -10
- package/src/internal/cli-mcp-oauth.ts +18 -0
- package/src/internal/cli-mcp.ts +11 -0
- package/src/internal/cli-slack.ts +15 -2
- package/src/internal/cursor/credentials.ts +35 -7
- package/src/internal/deploy-client.ts +54 -0
- package/src/internal/deploy-manifest.ts +115 -1
- package/src/internal/describe-error.ts +28 -0
- package/src/internal/guest-network.ts +43 -29
- package/src/internal/hosted-admission-adapter.ts +2 -0
- package/src/internal/hosted-catch-protocol.ts +130 -0
- package/src/internal/hosted-catch.ts +192 -0
- package/src/internal/hosted-delivery-protocol.ts +387 -0
- package/src/internal/hosted-delivery.ts +155 -22
- package/src/internal/hosted-execution-diag.ts +21 -3
- package/src/internal/hosted-execution-flush.ts +6 -3
- package/src/internal/http-control-plane-session.ts +104 -0
- package/src/internal/legacy-command-guard.ts +59 -0
- package/src/internal/platform-timers.ts +191 -0
- package/src/internal/reminder-control-plane-protocol.ts +15 -2
- package/src/internal/reminder-runner.ts +60 -9
- package/src/internal/resolve-prod-target.ts +15 -0
- package/src/internal/sdk-runner.ts +2 -1
- package/src/internal/server.ts +89 -79
- package/src/internal/session-engine.ts +141 -83
- package/src/storage-backends/cursor-hosted.ts +40 -1
- package/src/types.ts +39 -6
- package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +0 -1
- package/dist/docs/assets/guides_cloud-agents.md.DS8RIjwx.js +0 -9
- package/dist/docs/assets/index.md.BW_6tOgR.js +0 -5
- package/dist/docs/assets/skills_framework-map.md.haibFyoB.js +0 -1
- package/dist/docs/assets/troubleshooting.md.Cus_YZga.js +0 -1
- package/dist/playground/assets/index-CZKKNlmb.css +0 -1
- /package/dist/docs/assets/{evals.md.CbMoebP1.lean.js → evals.md.C1ekS3k2.lean.js} +0 -0
- /package/dist/docs/assets/{guides_github.md.c0gdGJ-D.lean.js → guides_github.md.BH33UEBJ.lean.js} +0 -0
- /package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.lean.js → guides_grokbot-agents.md.DyV-WArv.lean.js} +0 -0
- /package/dist/docs/assets/{index.md.BW_6tOgR.lean.js → index.md.BjH1w2ZW.lean.js} +0 -0
- /package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.lean.js → reference_agent-config.md.CvoL6pof.lean.js} +0 -0
- /package/dist/docs/assets/{reference_connections.md.Di6jJAXF.lean.js → reference_connections.md.BojkC6c5.lean.js} +0 -0
- /package/dist/docs/assets/{reference_extensions.md.CGmMLblt.lean.js → reference_extensions.md.CPWt00ds.lean.js} +0 -0
- /package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.lean.js → scaffolding-agents.md.Kctn3OVb.lean.js} +0 -0
- /package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.lean.js → skills_create-agent.md.Q3h6Je-e.lean.js} +0 -0
- /package/dist/docs/assets/{skills_evals.md.723kpUmA.lean.js → skills_evals.md.BhovOvrl.lean.js} +0 -0
- /package/dist/docs/assets/{skills_github.md.D0JahM8c.lean.js → skills_github.md.hgFX_oKY.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.Cus_YZga.lean.js → troubleshooting.md.CYEAO9bM.lean.js} +0 -0
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
* executes fires.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import {
|
|
12
|
+
REMINDER_CP_PROTOCOL_VERSION,
|
|
13
|
+
type ReminderControlPlane,
|
|
14
|
+
type ReminderCreateIntent,
|
|
15
|
+
type ReminderExecutionOutcome,
|
|
16
|
+
type ReminderFireClaim,
|
|
17
|
+
type ReminderOutcomeApplyResult,
|
|
18
|
+
} from "./reminder-control-plane-protocol.js";
|
|
11
19
|
import { isReminderRecord, type ReminderRecord } from "./reminder-store.js";
|
|
12
20
|
|
|
13
21
|
const DEFAULT_CURSOR_EXTERNAL_API_URL = "https://api.cursor.com";
|
|
@@ -171,3 +179,186 @@ export function createCursorHostedPlatformTimers(
|
|
|
171
179
|
},
|
|
172
180
|
};
|
|
173
181
|
}
|
|
182
|
+
|
|
183
|
+
/** v2 reminder CP is factory ECS (`/internal/agentsdk/*`), not timed-work. */
|
|
184
|
+
const AGENTSDK_REMINDERS_PREFIX = "/internal/agentsdk";
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Bounded retries for idempotent CP outcome writes after the guest handler
|
|
188
|
+
* has already run. Delivery is at-least-once: a transient apply failure may
|
|
189
|
+
* re-enter the handler on the next Temporal admission. Do not claim
|
|
190
|
+
* exactly-once handler execution.
|
|
191
|
+
*/
|
|
192
|
+
const APPLY_REMINDER_OUTCOME_MAX_ATTEMPTS = 3;
|
|
193
|
+
const APPLY_REMINDER_OUTCOME_RETRY_DELAY_MS = 50;
|
|
194
|
+
|
|
195
|
+
async function sleepMs(ms: number): Promise<void> {
|
|
196
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function withBoundedApplyRetries<T>(run: () => Promise<T>): Promise<T> {
|
|
200
|
+
let lastError: unknown;
|
|
201
|
+
for (
|
|
202
|
+
let attempt = 1;
|
|
203
|
+
attempt <= APPLY_REMINDER_OUTCOME_MAX_ATTEMPTS;
|
|
204
|
+
attempt++
|
|
205
|
+
) {
|
|
206
|
+
try {
|
|
207
|
+
return await run();
|
|
208
|
+
} catch (error) {
|
|
209
|
+
lastError = error;
|
|
210
|
+
if (attempt >= APPLY_REMINDER_OUTCOME_MAX_ATTEMPTS) {
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
await sleepMs(APPLY_REMINDER_OUTCOME_RETRY_DELAY_MS * attempt);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
throw lastError;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function timedWorkJsonFetch(
|
|
220
|
+
options: CursorHostedPlatformTimersOptions,
|
|
221
|
+
method: "GET" | "POST" | "PUT" | "DELETE",
|
|
222
|
+
path: string,
|
|
223
|
+
body?: unknown
|
|
224
|
+
): Promise<Response> {
|
|
225
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
226
|
+
if (typeof fetchImpl !== "function") {
|
|
227
|
+
throw new Error("reminder control plane requires fetch");
|
|
228
|
+
}
|
|
229
|
+
return fetchImpl(`${resolveBaseUrl(options)}${path}`, {
|
|
230
|
+
method,
|
|
231
|
+
headers: {
|
|
232
|
+
Authorization: `Bearer ${resolveApiKey(options)}`,
|
|
233
|
+
...(body === undefined ? {} : { "Content-Type": "application/json" }),
|
|
234
|
+
},
|
|
235
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Cursor-hosted HTTP ReminderControlPlane for architecture v2.
|
|
241
|
+
* Speaks factory `/internal/agentsdk/reminders` (ELB → factory ECS).
|
|
242
|
+
* CP owns ids and time. v1 PUT/DELETE reminders stay on timed-work.
|
|
243
|
+
*/
|
|
244
|
+
export function createCursorHostedReminderControlPlane(
|
|
245
|
+
options: CursorHostedPlatformTimersOptions = {}
|
|
246
|
+
): ReminderControlPlane {
|
|
247
|
+
return {
|
|
248
|
+
protocolVersion: REMINDER_CP_PROTOCOL_VERSION,
|
|
249
|
+
async createReminder(intent: ReminderCreateIntent) {
|
|
250
|
+
const response = await timedWorkJsonFetch(
|
|
251
|
+
options,
|
|
252
|
+
"POST",
|
|
253
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders`,
|
|
254
|
+
intent
|
|
255
|
+
);
|
|
256
|
+
if (!response.ok) {
|
|
257
|
+
const text = await response.text();
|
|
258
|
+
throw new Error(
|
|
259
|
+
`reminder CP create failed: HTTP ${response.status} ${text}`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
const parsed: unknown = await response.json();
|
|
263
|
+
if (!isReminderRecord(parsed)) {
|
|
264
|
+
throw new Error("reminder CP create returned invalid record");
|
|
265
|
+
}
|
|
266
|
+
return parsed;
|
|
267
|
+
},
|
|
268
|
+
async getReminder(id) {
|
|
269
|
+
const response = await timedWorkJsonFetch(
|
|
270
|
+
options,
|
|
271
|
+
"GET",
|
|
272
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders/${encodeURIComponent(id)}`
|
|
273
|
+
);
|
|
274
|
+
if (response.status === 404) {
|
|
275
|
+
return undefined;
|
|
276
|
+
}
|
|
277
|
+
if (!response.ok) {
|
|
278
|
+
throw new Error(`reminder CP get failed: HTTP ${response.status}`);
|
|
279
|
+
}
|
|
280
|
+
const parsed: unknown = await response.json();
|
|
281
|
+
return isReminderRecord(parsed) ? parsed : undefined;
|
|
282
|
+
},
|
|
283
|
+
async listReminders() {
|
|
284
|
+
const response = await timedWorkJsonFetch(
|
|
285
|
+
options,
|
|
286
|
+
"GET",
|
|
287
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders`
|
|
288
|
+
);
|
|
289
|
+
if (!response.ok) {
|
|
290
|
+
throw new Error(`reminder CP list failed: HTTP ${response.status}`);
|
|
291
|
+
}
|
|
292
|
+
const parsed: unknown = await response.json();
|
|
293
|
+
if (
|
|
294
|
+
parsed === null ||
|
|
295
|
+
typeof parsed !== "object" ||
|
|
296
|
+
!("reminders" in parsed) ||
|
|
297
|
+
!Array.isArray((parsed as { reminders: unknown }).reminders)
|
|
298
|
+
) {
|
|
299
|
+
return [];
|
|
300
|
+
}
|
|
301
|
+
return (parsed as { reminders: unknown[] }).reminders.filter(
|
|
302
|
+
isReminderRecord
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
async cancelReminder(id, reason) {
|
|
306
|
+
const response = await timedWorkJsonFetch(
|
|
307
|
+
options,
|
|
308
|
+
"POST",
|
|
309
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders/${encodeURIComponent(id)}/cancel`,
|
|
310
|
+
reason === undefined ? {} : { reason }
|
|
311
|
+
);
|
|
312
|
+
if (response.status === 404) {
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
if (!response.ok) {
|
|
316
|
+
throw new Error(`reminder CP cancel failed: HTTP ${response.status}`);
|
|
317
|
+
}
|
|
318
|
+
const parsed: unknown = await response.json();
|
|
319
|
+
return isReminderRecord(parsed) ? parsed : undefined;
|
|
320
|
+
},
|
|
321
|
+
async rekeyReminders(channelId, oldToken, newToken) {
|
|
322
|
+
const response = await timedWorkJsonFetch(
|
|
323
|
+
options,
|
|
324
|
+
"POST",
|
|
325
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders/rekey`,
|
|
326
|
+
{ channelId, oldToken, newToken }
|
|
327
|
+
);
|
|
328
|
+
if (!response.ok) {
|
|
329
|
+
throw new Error(`reminder CP rekey failed: HTTP ${response.status}`);
|
|
330
|
+
}
|
|
331
|
+
const parsed: unknown = await response.json();
|
|
332
|
+
if (
|
|
333
|
+
parsed === null ||
|
|
334
|
+
typeof parsed !== "object" ||
|
|
335
|
+
typeof (parsed as { count?: unknown }).count !== "number"
|
|
336
|
+
) {
|
|
337
|
+
throw new Error("reminder CP rekey returned invalid body");
|
|
338
|
+
}
|
|
339
|
+
return (parsed as { count: number }).count;
|
|
340
|
+
},
|
|
341
|
+
async applyReminderOutcome(
|
|
342
|
+
claim: ReminderFireClaim,
|
|
343
|
+
outcome: ReminderExecutionOutcome
|
|
344
|
+
): Promise<ReminderOutcomeApplyResult> {
|
|
345
|
+
// Handler side effects may already have run; retry only the idempotent
|
|
346
|
+
// CP write. Exhausted failures stay thrown so Temporal can re-admit
|
|
347
|
+
// (at-least-once — not exactly-once).
|
|
348
|
+
return await withBoundedApplyRetries(async () => {
|
|
349
|
+
const response = await timedWorkJsonFetch(
|
|
350
|
+
options,
|
|
351
|
+
"POST",
|
|
352
|
+
`${AGENTSDK_REMINDERS_PREFIX}/reminders/outcome`,
|
|
353
|
+
{ claim, outcome }
|
|
354
|
+
);
|
|
355
|
+
if (!response.ok) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`reminder CP outcome apply failed: HTTP ${response.status}`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
return (await response.json()) as ReminderOutcomeApplyResult;
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
}
|
|
@@ -27,6 +27,8 @@ import type { ReminderRecord } from "./reminder-store.js";
|
|
|
27
27
|
|
|
28
28
|
/** Wire / persistence version for this control-plane protocol. */
|
|
29
29
|
export const REMINDER_CP_PROTOCOL_VERSION = 1 as const;
|
|
30
|
+
/** Each recurring/retry fire can spend a model turn and create hosted work. */
|
|
31
|
+
export const REMINDER_CP_MIN_RECURRING_INTERVAL_MS = 60_000 as const;
|
|
30
32
|
|
|
31
33
|
export type ReminderCpProtocolVersion = typeof REMINDER_CP_PROTOCOL_VERSION;
|
|
32
34
|
|
|
@@ -293,6 +295,14 @@ export function intentToNewRecord(
|
|
|
293
295
|
scheduleSpecToCreateFields(intent.schedule),
|
|
294
296
|
nowMs
|
|
295
297
|
);
|
|
298
|
+
if (
|
|
299
|
+
resolved.kind === "every" &&
|
|
300
|
+
(resolved.intervalMs ?? 0) < REMINDER_CP_MIN_RECURRING_INTERVAL_MS
|
|
301
|
+
) {
|
|
302
|
+
throw new ReminderValidationError(
|
|
303
|
+
`every must be >= 1m (got ${intent.schedule.kind === "every" ? intent.schedule.every : ""})`
|
|
304
|
+
);
|
|
305
|
+
}
|
|
296
306
|
const nowIso = new Date(nowMs).toISOString();
|
|
297
307
|
return {
|
|
298
308
|
version: 1,
|
|
@@ -429,11 +439,14 @@ export function applyOutcomeToRecord(
|
|
|
429
439
|
const nowIso = new Date(nowMs).toISOString();
|
|
430
440
|
|
|
431
441
|
if (outcome.kind === "retry") {
|
|
432
|
-
const delayMs =
|
|
442
|
+
const delayMs = Math.max(
|
|
443
|
+
REMINDER_CP_MIN_RECURRING_INTERVAL_MS,
|
|
444
|
+
outcome.delayMs ?? REMINDER_CP_MIN_RECURRING_INTERVAL_MS
|
|
445
|
+
);
|
|
433
446
|
const updated: ReminderRecord = {
|
|
434
447
|
...record,
|
|
435
448
|
status: "armed",
|
|
436
|
-
nextFireAtMs: nowMs +
|
|
449
|
+
nextFireAtMs: nowMs + delayMs,
|
|
437
450
|
// Same fireCount; bump generation so a second apply of the old claim is stale.
|
|
438
451
|
generation: recordGeneration(record) + 1,
|
|
439
452
|
updatedAt: nowIso,
|
|
@@ -18,6 +18,11 @@ import type {
|
|
|
18
18
|
ReminderStatus,
|
|
19
19
|
ReminderUntilContext,
|
|
20
20
|
} from "../types.js";
|
|
21
|
+
import {
|
|
22
|
+
channelContinuationIdentity,
|
|
23
|
+
httpContinuationIdentity,
|
|
24
|
+
} from "./continuation-channel.js";
|
|
25
|
+
import { continuationSource } from "./continuation-identity.js";
|
|
21
26
|
import { NAME_PATTERN } from "./discovery/names.js";
|
|
22
27
|
import { parseDuration } from "./duration.js";
|
|
23
28
|
import type { PlatformTimers } from "./platform-timers.js";
|
|
@@ -39,7 +44,12 @@ import {
|
|
|
39
44
|
} from "./reminder-schedule.js";
|
|
40
45
|
import type { ReminderBusyPolicy, ReminderRecord } from "./reminder-store.js";
|
|
41
46
|
import { APP_AUTH } from "./schedule-runner.js";
|
|
42
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
type EngineSendOptions,
|
|
49
|
+
HTTP_CHANNEL_ID,
|
|
50
|
+
SessionBusyError,
|
|
51
|
+
type SessionEngine,
|
|
52
|
+
} from "./session-engine.js";
|
|
43
53
|
|
|
44
54
|
export { ReminderValidationError } from "./reminder-schedule.js";
|
|
45
55
|
|
|
@@ -1040,16 +1050,56 @@ export class ReminderRunner {
|
|
|
1040
1050
|
return this.sendFollowup(record, buildFirePrompt(record), auth);
|
|
1041
1051
|
}
|
|
1042
1052
|
|
|
1053
|
+
/**
|
|
1054
|
+
* Join the reminder's existing session. Control-plane (`/v2`) channels
|
|
1055
|
+
* refuse authored `continuationToken`; pass the stored key as
|
|
1056
|
+
* {@link EngineSendOptions.mailboxContinuation} so the engine reuses the
|
|
1057
|
+
* CP-selected identity without channel admission.
|
|
1058
|
+
*/
|
|
1059
|
+
private followupSendOptions(
|
|
1060
|
+
record: ReminderRecord,
|
|
1061
|
+
auth: AuthContext | null
|
|
1062
|
+
): EngineSendOptions {
|
|
1063
|
+
if (this.channelUsesControlPlaneContinuation(record.channelId)) {
|
|
1064
|
+
return {
|
|
1065
|
+
auth,
|
|
1066
|
+
mailboxContinuation: { key: record.continuationToken },
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
auth,
|
|
1071
|
+
continuationToken: record.continuationToken,
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
private channelUsesControlPlaneContinuation(channelId: string): boolean {
|
|
1076
|
+
const project = this.engine.project;
|
|
1077
|
+
// Test doubles may omit `project`; keep the legacy continuationToken path.
|
|
1078
|
+
if (project === undefined) {
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
const identity =
|
|
1082
|
+
channelId === HTTP_CHANNEL_ID
|
|
1083
|
+
? httpContinuationIdentity(project.httpChannel)
|
|
1084
|
+
: channelContinuationIdentity(
|
|
1085
|
+
project.channels.find((channel) => channel.id === channelId)
|
|
1086
|
+
?.definition
|
|
1087
|
+
);
|
|
1088
|
+
return continuationSource(identity) === "control-plane";
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1043
1091
|
private async sendFollowup(
|
|
1044
1092
|
record: ReminderRecord,
|
|
1045
1093
|
message: string,
|
|
1046
1094
|
auth: AuthContext | null
|
|
1047
1095
|
): Promise<ReminderFireResult> {
|
|
1096
|
+
const sendOptions = this.followupSendOptions(record, auth);
|
|
1048
1097
|
try {
|
|
1049
|
-
const session = await this.engine.send(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1098
|
+
const session = await this.engine.send(
|
|
1099
|
+
record.channelId,
|
|
1100
|
+
message,
|
|
1101
|
+
sendOptions
|
|
1102
|
+
);
|
|
1053
1103
|
this.engine.trackBackground(this.engine.waitForCompletion(session.id));
|
|
1054
1104
|
return { action: "delivered" };
|
|
1055
1105
|
} catch (error) {
|
|
@@ -1059,10 +1109,11 @@ export class ReminderRunner {
|
|
|
1059
1109
|
}
|
|
1060
1110
|
await this.whenSessionIdle(record.channelId, record.continuationToken);
|
|
1061
1111
|
try {
|
|
1062
|
-
const session = await this.engine.send(
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1112
|
+
const session = await this.engine.send(
|
|
1113
|
+
record.channelId,
|
|
1114
|
+
message,
|
|
1115
|
+
sendOptions
|
|
1116
|
+
);
|
|
1066
1117
|
this.engine.trackBackground(
|
|
1067
1118
|
this.engine.waitForCompletion(session.id)
|
|
1068
1119
|
);
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
validateDeploymentSlug,
|
|
17
17
|
} from "./deploy-client.js";
|
|
18
18
|
import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
|
|
19
|
+
import { rejectV2ForLegacyCommand } from "./legacy-command-guard.js";
|
|
19
20
|
import { sleep as defaultSleep } from "./sleep.js";
|
|
20
21
|
import { stderrPalette } from "./terminal-style.js";
|
|
21
22
|
|
|
@@ -38,6 +39,8 @@ export interface ResolveProdTargetOptions {
|
|
|
38
39
|
apiKey?: string;
|
|
39
40
|
fetchImpl?: typeof fetch;
|
|
40
41
|
err?: (text: string) => void;
|
|
42
|
+
/** Skip when the caller already ran {@link rejectV2ForLegacyCommand}. */
|
|
43
|
+
skipLegacyArchitectureCheck?: boolean;
|
|
41
44
|
/** Test seam for reachability-retry backoff. */
|
|
42
45
|
sleep?: (ms: number) => Promise<void>;
|
|
43
46
|
}
|
|
@@ -95,6 +98,13 @@ export async function resolveProdTarget(
|
|
|
95
98
|
`Could not resolve Cursor credentials/team for the hosted deployment (see above).`
|
|
96
99
|
);
|
|
97
100
|
}
|
|
101
|
+
if (options.skipLegacyArchitectureCheck !== true) {
|
|
102
|
+
await rejectV2ForLegacyCommand({
|
|
103
|
+
client: auth.client,
|
|
104
|
+
teamId: auth.teamId,
|
|
105
|
+
slug,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
98
108
|
|
|
99
109
|
// A hibernated engine can briefly omit engineAccess while getPod fails
|
|
100
110
|
// during wake. Retry a few times before surfacing "not reachable yet".
|
|
@@ -198,6 +208,11 @@ export async function resolveProdAliasPlaygroundUrl(
|
|
|
198
208
|
`Could not resolve Cursor credentials/team for the hosted deployment (see above).`
|
|
199
209
|
);
|
|
200
210
|
}
|
|
211
|
+
await rejectV2ForLegacyCommand({
|
|
212
|
+
client: auth.client,
|
|
213
|
+
teamId: auth.teamId,
|
|
214
|
+
slug,
|
|
215
|
+
});
|
|
201
216
|
|
|
202
217
|
let detail: DeploymentDetail;
|
|
203
218
|
try {
|
|
@@ -518,7 +518,7 @@ export class CursorSdkRunner implements AgentRunner {
|
|
|
518
518
|
const resolved = resolveApiKeySync({ apiKey: this.options.apiKey });
|
|
519
519
|
if (resolved === undefined) {
|
|
520
520
|
throw new Error(
|
|
521
|
-
`The Cursor SDK runner requires a Cursor credential: run \`${CLI} login\`, set CURSOR_API_KEY or CURSOR_API_KEY_FILE, or pass serve({ apiKey }).`
|
|
521
|
+
`The Cursor SDK runner requires a Cursor credential: run \`${CLI} login\`, set CURSOR_API_KEY, CURSOR_SERVICE_ACCOUNT_KEY, or CURSOR_API_KEY_FILE, or pass serve({ apiKey }).`
|
|
522
522
|
);
|
|
523
523
|
}
|
|
524
524
|
return resolved.apiKey;
|
|
@@ -805,6 +805,7 @@ function toTurnOutcome(result: RunResult): TurnOutcome {
|
|
|
805
805
|
...(result.status === "error"
|
|
806
806
|
? { errorMessage: runErrorMessage(result) }
|
|
807
807
|
: {}),
|
|
808
|
+
errorCode: result.status === "error" ? result.error?.code : undefined,
|
|
808
809
|
...(usage === undefined ? {} : { usage }),
|
|
809
810
|
};
|
|
810
811
|
}
|
package/src/internal/server.ts
CHANGED
|
@@ -66,6 +66,7 @@ import type {
|
|
|
66
66
|
ReminderInfo,
|
|
67
67
|
ServeOptions,
|
|
68
68
|
} from "../types.js";
|
|
69
|
+
import { invokeChannelRoute } from "./channel-route-dispatch.js";
|
|
69
70
|
import { selectConversationMirror } from "./conversation-mirror.js";
|
|
70
71
|
import {
|
|
71
72
|
type CursorAccountConnectionRef,
|
|
@@ -129,6 +130,7 @@ import {
|
|
|
129
130
|
} from "./handleAgentServeTrigger.js";
|
|
130
131
|
import { createHostPlatforms, warmupHostGitHubAuth } from "./host-platforms.js";
|
|
131
132
|
import { registerHostedDeliveryRoute } from "./hosted-delivery.js";
|
|
133
|
+
import type { HostedReminderFireDescriptorPayload } from "./hosted-delivery-protocol.js";
|
|
132
134
|
import {
|
|
133
135
|
HOSTED_DELIVERY_HEADER,
|
|
134
136
|
HOSTED_GUEST_RETRY_TERMINAL,
|
|
@@ -143,7 +145,6 @@ import {
|
|
|
143
145
|
readJsonObject,
|
|
144
146
|
requireSessionOwner,
|
|
145
147
|
} from "./http-channel.js";
|
|
146
|
-
import { validateToolInput } from "./json-schema.js";
|
|
147
148
|
import { DEFAULT_SERVE_PORT, listenHttpServer } from "./listen-port.js";
|
|
148
149
|
import {
|
|
149
150
|
createLocalControlPlane,
|
|
@@ -176,7 +177,10 @@ import {
|
|
|
176
177
|
PLATFORM_SCHEDULE_CATALOG_FILENAME,
|
|
177
178
|
syncHostedPlatformSchedules,
|
|
178
179
|
} from "./platform-schedule-sync.js";
|
|
179
|
-
import {
|
|
180
|
+
import {
|
|
181
|
+
createCursorHostedPlatformTimers,
|
|
182
|
+
createCursorHostedReminderControlPlane,
|
|
183
|
+
} from "./platform-timers.js";
|
|
180
184
|
import {
|
|
181
185
|
type AgentIndexEntry,
|
|
182
186
|
renderAgentsIndexHtml,
|
|
@@ -193,6 +197,11 @@ import {
|
|
|
193
197
|
collectMemoryHealthFields,
|
|
194
198
|
sumEventLogStats,
|
|
195
199
|
} from "./process-memory-telemetry.js";
|
|
200
|
+
import {
|
|
201
|
+
claimFromDescriptor,
|
|
202
|
+
type ReminderControlPlane,
|
|
203
|
+
type ReminderFireDescriptor,
|
|
204
|
+
} from "./reminder-control-plane-protocol.js";
|
|
196
205
|
import {
|
|
197
206
|
ReminderRunner,
|
|
198
207
|
ReminderValidationError,
|
|
@@ -210,6 +219,7 @@ import { RuntimeDispatchingRunner } from "./runtime-dispatch-runner.js";
|
|
|
210
219
|
import { ScheduleRunner, UnknownScheduleError } from "./schedule-runner.js";
|
|
211
220
|
import { type AgentRunner, CursorSdkRunner } from "./sdk-runner.js";
|
|
212
221
|
import {
|
|
222
|
+
HTTP_CHANNEL_ID,
|
|
213
223
|
InvalidContinuationTokenError,
|
|
214
224
|
SessionEngine,
|
|
215
225
|
ToolInputError,
|
|
@@ -252,6 +262,11 @@ interface MountedRuntime {
|
|
|
252
262
|
scheduleRunner: ScheduleRunner;
|
|
253
263
|
reminderRunner: ReminderRunner;
|
|
254
264
|
controlPlane?: LocalControlPlane;
|
|
265
|
+
/**
|
|
266
|
+
* Hosted (or local) reminder CP client for architecture v2 descriptor
|
|
267
|
+
* execution. Undefined when the project is not on the CP protocol.
|
|
268
|
+
*/
|
|
269
|
+
reminderControlPlane?: ReminderControlPlane;
|
|
255
270
|
/** Playground eval batches (`/v1/dev/evals`); loopbackUrl set after listen. */
|
|
256
271
|
evalRuns: EvalRunStore;
|
|
257
272
|
}
|
|
@@ -325,9 +340,12 @@ export async function startServer(
|
|
|
325
340
|
noControlPlane?: boolean;
|
|
326
341
|
/** `--cursor-scm-proxy`: every cursorAccount SCM pack routes via the backend forwarder. */
|
|
327
342
|
cursorScmProxy?: boolean;
|
|
343
|
+
/** Deprecated alias (`--cursor-github-proxy`); same effect as cursorScmProxy. */
|
|
344
|
+
cursorGithubProxy?: boolean;
|
|
328
345
|
};
|
|
329
346
|
const noControlPlane = hosted.noControlPlane === true;
|
|
330
|
-
const scmProxyRequested =
|
|
347
|
+
const scmProxyRequested =
|
|
348
|
+
hosted.cursorScmProxy === true || hosted.cursorGithubProxy === true;
|
|
331
349
|
const runSchedules = options.schedules ?? !dev;
|
|
332
350
|
const runReminders = options.reminders ?? !dev;
|
|
333
351
|
|
|
@@ -371,13 +389,15 @@ export async function startServer(
|
|
|
371
389
|
// One resolution for every Cursor credential consumer (SDK runner, account
|
|
372
390
|
// MCP bridge, SCM event relay, GitHub credentials): explicit option →
|
|
373
391
|
// CURSOR_API_KEY → CURSOR_API_KEY_FILE (or the hosted default path) →
|
|
374
|
-
// stored `agent-sdk login`. SDK / GitHub /
|
|
375
|
-
// Slack and deployment-event relays capture
|
|
392
|
+
// CURSOR_SERVICE_ACCOUNT_KEY → stored `agent-sdk login`. SDK / GitHub /
|
|
393
|
+
// account MCP re-read later; Slack and deployment-event relays capture
|
|
394
|
+
// the key at start.
|
|
376
395
|
const accountKey = await resolveApiKey(
|
|
377
396
|
options.apiKey === undefined ? {} : { apiKey: options.apiKey }
|
|
378
397
|
);
|
|
379
398
|
// First present source wins (we define the order): option → env → file →
|
|
380
|
-
// stored login. Re-read so bind can drop env or file
|
|
399
|
+
// service account → stored login. Re-read so bind can drop env or file
|
|
400
|
+
// onto a listening process.
|
|
381
401
|
const liveCursorApiKey = (): string | undefined =>
|
|
382
402
|
resolveApiKeySync(
|
|
383
403
|
options.apiKey === undefined ? {} : { apiKey: options.apiKey }
|
|
@@ -850,7 +870,7 @@ export async function startServer(
|
|
|
850
870
|
const scheduleRunner = new ScheduleRunner(engine, project, logger);
|
|
851
871
|
let reminderRunner!: ReminderRunner;
|
|
852
872
|
const architecture = project.agent.architecture;
|
|
853
|
-
const useReminderCpProtocol =
|
|
873
|
+
const useReminderCpProtocol = architecture === "v2";
|
|
854
874
|
const localControlPlane = noControlPlane
|
|
855
875
|
? undefined
|
|
856
876
|
: await createLocalControlPlane({
|
|
@@ -873,6 +893,13 @@ export async function startServer(
|
|
|
873
893
|
const timerClient = noControlPlane
|
|
874
894
|
? createCursorHostedPlatformTimers()
|
|
875
895
|
: localControlPlane;
|
|
896
|
+
const hostedReminderControlPlane =
|
|
897
|
+
noControlPlane && useReminderCpProtocol
|
|
898
|
+
? createCursorHostedReminderControlPlane()
|
|
899
|
+
: undefined;
|
|
900
|
+
const reminderControlPlane = useReminderCpProtocol
|
|
901
|
+
? (localControlPlane ?? hostedReminderControlPlane)
|
|
902
|
+
: undefined;
|
|
876
903
|
reminderRunner = new ReminderRunner(engine, logger, timerClient, {
|
|
877
904
|
namedHandlers: project.reminders ?? [],
|
|
878
905
|
// Architecture v2 rejects anonymous run/until everywhere (local + hosted).
|
|
@@ -881,7 +908,7 @@ export async function startServer(
|
|
|
881
908
|
architecture,
|
|
882
909
|
}),
|
|
883
910
|
architecture,
|
|
884
|
-
controlPlane:
|
|
911
|
+
controlPlane: reminderControlPlane,
|
|
885
912
|
});
|
|
886
913
|
const evalRuns = new EvalRunStore(
|
|
887
914
|
project.rootDir,
|
|
@@ -920,6 +947,7 @@ export async function startServer(
|
|
|
920
947
|
...(localControlPlane === undefined
|
|
921
948
|
? {}
|
|
922
949
|
: { controlPlane: localControlPlane }),
|
|
950
|
+
...(reminderControlPlane === undefined ? {} : { reminderControlPlane }),
|
|
923
951
|
};
|
|
924
952
|
runtimes.push(runtime);
|
|
925
953
|
const accountRefs = accountRefsBySlug.get(slug);
|
|
@@ -1479,7 +1507,14 @@ function mountAgentRoutes(
|
|
|
1479
1507
|
evictSdkSession?: (sessionId: string) => void;
|
|
1480
1508
|
}
|
|
1481
1509
|
): void {
|
|
1482
|
-
const {
|
|
1510
|
+
const {
|
|
1511
|
+
slug,
|
|
1512
|
+
project,
|
|
1513
|
+
engine,
|
|
1514
|
+
scheduleRunner,
|
|
1515
|
+
reminderRunner,
|
|
1516
|
+
reminderControlPlane,
|
|
1517
|
+
} = runtime;
|
|
1483
1518
|
const {
|
|
1484
1519
|
dev,
|
|
1485
1520
|
playground,
|
|
@@ -1538,7 +1573,12 @@ function mountAgentRoutes(
|
|
|
1538
1573
|
registerHostedDeliveryRoute({
|
|
1539
1574
|
router,
|
|
1540
1575
|
base,
|
|
1541
|
-
|
|
1576
|
+
// The built-in http channel is not in `project.channels`; the control
|
|
1577
|
+
// plane's session start (`agentsdk.session`) is delivered to it.
|
|
1578
|
+
channels: [
|
|
1579
|
+
...project.channels,
|
|
1580
|
+
{ id: HTTP_CHANNEL_ID, definition: project.httpChannel },
|
|
1581
|
+
],
|
|
1542
1582
|
buildHandlerArgs: (channelId, hostedSessionId) =>
|
|
1543
1583
|
buildChannelHandlerArgs({
|
|
1544
1584
|
runtime,
|
|
@@ -1547,6 +1587,33 @@ function mountAgentRoutes(
|
|
|
1547
1587
|
}),
|
|
1548
1588
|
fireReminder: (reminderId, fireCount) =>
|
|
1549
1589
|
reminderRunner.dispatch(reminderId, { expectedFireCount: fireCount }),
|
|
1590
|
+
executeReminderDescriptor: async (descriptor) => {
|
|
1591
|
+
if (reminderControlPlane === undefined) {
|
|
1592
|
+
throw new Error("hosted reminder CP client is not configured");
|
|
1593
|
+
}
|
|
1594
|
+
const fireDescriptor = descriptor as unknown as ReminderFireDescriptor;
|
|
1595
|
+
const outcome = await reminderRunner.executeFire(fireDescriptor);
|
|
1596
|
+
// applyReminderOutcome is idempotent and internally retries transient
|
|
1597
|
+
// CP failures. Handler execution is at-least-once across Temporal
|
|
1598
|
+
// re-admission if apply ultimately fails.
|
|
1599
|
+
const apply = await reminderControlPlane.applyReminderOutcome(
|
|
1600
|
+
claimFromDescriptor(fireDescriptor),
|
|
1601
|
+
outcome
|
|
1602
|
+
);
|
|
1603
|
+
// Cancel/rekey winning the race is a successful no-op from the guest's
|
|
1604
|
+
// perspective (fired work may have run; lifecycle authority stayed CP).
|
|
1605
|
+
if (!apply.applied && apply.reason === "missing") {
|
|
1606
|
+
return {
|
|
1607
|
+
reminderId: descriptor.id,
|
|
1608
|
+
outcome: "noop" as const,
|
|
1609
|
+
reason: apply.reason,
|
|
1610
|
+
};
|
|
1611
|
+
}
|
|
1612
|
+
return {
|
|
1613
|
+
reminderId: descriptor.id,
|
|
1614
|
+
outcome: "fired" as const,
|
|
1615
|
+
};
|
|
1616
|
+
},
|
|
1550
1617
|
drainSessionDurability: (sessionId) =>
|
|
1551
1618
|
engine.drainSessionDurability(sessionId),
|
|
1552
1619
|
flushOtel: args.flushOtel,
|
|
@@ -2360,75 +2427,18 @@ function mountAgentRoutes(
|
|
|
2360
2427
|
method: channelRoute.method,
|
|
2361
2428
|
pattern: `${base}/v1/channels/${channel.id}${channelRoute.path}`,
|
|
2362
2429
|
auth: channelAuth,
|
|
2363
|
-
handler:
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
);
|
|
2376
|
-
const queryValidation = validateToolInput(
|
|
2377
|
-
channelRoute.querySchema,
|
|
2378
|
-
rawQuery
|
|
2379
|
-
);
|
|
2380
|
-
if (!queryValidation.ok) {
|
|
2381
|
-
return jsonResponse(
|
|
2382
|
-
{ error: queryValidation.message },
|
|
2383
|
-
{ status: 400 }
|
|
2384
|
-
);
|
|
2385
|
-
}
|
|
2386
|
-
handlerArgs.query = queryValidation.value;
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
|
-
if (channelRoute.bodySchema !== undefined) {
|
|
2390
|
-
// Buffer once so handlers that need the raw body (e.g. GitHub
|
|
2391
|
-
// HMAC) can still read `request.text()` / `request.json()`.
|
|
2392
|
-
let bodyText: string;
|
|
2393
|
-
try {
|
|
2394
|
-
bodyText = await request.text();
|
|
2395
|
-
} catch {
|
|
2396
|
-
return jsonResponse(
|
|
2397
|
-
{ error: "Failed to read request body" },
|
|
2398
|
-
{ status: 400 }
|
|
2399
|
-
);
|
|
2400
|
-
}
|
|
2401
|
-
let rawBody: unknown;
|
|
2402
|
-
try {
|
|
2403
|
-
rawBody = bodyText === "" ? {} : JSON.parse(bodyText);
|
|
2404
|
-
} catch {
|
|
2405
|
-
return jsonResponse(
|
|
2406
|
-
{ error: "Request body must be valid JSON" },
|
|
2407
|
-
{ status: 400 }
|
|
2408
|
-
);
|
|
2409
|
-
}
|
|
2410
|
-
const bodyValidation = validateToolInput(
|
|
2411
|
-
channelRoute.bodySchema,
|
|
2412
|
-
rawBody
|
|
2413
|
-
);
|
|
2414
|
-
if (!bodyValidation.ok) {
|
|
2415
|
-
return jsonResponse(
|
|
2416
|
-
{ error: bodyValidation.message },
|
|
2417
|
-
{ status: 400 }
|
|
2418
|
-
);
|
|
2419
|
-
}
|
|
2420
|
-
handlerArgs.body = bodyValidation.value;
|
|
2421
|
-
request = new Request(request.url, {
|
|
2422
|
-
method: request.method,
|
|
2423
|
-
headers: request.headers,
|
|
2424
|
-
body: bodyText === "" ? undefined : bodyText,
|
|
2425
|
-
// node: undici / fetch may require duplex for streamed bodies;
|
|
2426
|
-
// a string body does not.
|
|
2427
|
-
});
|
|
2428
|
-
}
|
|
2429
|
-
|
|
2430
|
-
return channelRoute.handler(request, handlerArgs);
|
|
2431
|
-
},
|
|
2430
|
+
handler: (request, ctx) =>
|
|
2431
|
+
invokeChannelRoute({
|
|
2432
|
+
route: channelRoute,
|
|
2433
|
+
request,
|
|
2434
|
+
handlerArgs: buildChannelHandlerArgs({
|
|
2435
|
+
runtime,
|
|
2436
|
+
channelId: channel.id,
|
|
2437
|
+
params: ctx.params,
|
|
2438
|
+
requestIp: ctx.requestIp,
|
|
2439
|
+
auth: ctx.auth,
|
|
2440
|
+
}),
|
|
2441
|
+
}),
|
|
2432
2442
|
});
|
|
2433
2443
|
}
|
|
2434
2444
|
}
|