@cursor/july 0.1.105 → 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 +31 -16
- package/dist/channels/bitbucket/types.d.ts +2 -4
- package/dist/channels/bitbucket/types.d.ts.map +1 -1
- package/dist/channels/deployments/types.d.ts +5 -8
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/github/github-channel.js +8 -5
- package/dist/channels/github/types.d.ts +7 -5
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/channels/gitlab/types.d.ts +2 -5
- package/dist/channels/gitlab/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 +7 -4
- package/dist/channels/origin/types.d.ts.map +1 -1
- package/dist/channels/slack/defaults.d.ts.map +1 -1
- package/dist/channels/slack/defaults.js +3 -0
- package/dist/channels/slack/dispatch.d.ts.map +1 -1
- package/dist/channels/slack/dispatch.js +1 -1
- package/dist/channels/slack/live-delivery.d.ts.map +1 -1
- package/dist/channels/slack/live-delivery.js +48 -11
- package/dist/channels/slack/types.d.ts +4 -3
- package/dist/channels/slack/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 +3 -3
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{app.BVwaffdK.js → app.BASKM3M4.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.BHZYsNVi.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.WuWTZ1x8.js → VPLocalSearchBox.BOKxlYGP.js} +2 -2
- package/dist/docs/assets/chunks/{theme.BYdI_6sF.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_opentelemetry.md.COh9jFVg.js → guides_opentelemetry.md.SCRMskno.js} +3 -3
- package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.lean.js → guides_opentelemetry.md.SCRMskno.lean.js} +1 -1
- 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_channels.md.DBGcaB7m.js → reference_channels.md.D-qTqwcq.js} +2 -2
- package/dist/docs/assets/{reference_channels.md.DBGcaB7m.lean.js → reference_channels.md.D-qTqwcq.lean.js} +1 -1
- package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.js → reference_cli.md.Ca26u0Es.js} +6 -6
- package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.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/{skills_otel.md.CqhobH3B.js → skills_otel.md.B5Ddv1xH.js} +1 -1
- package/dist/docs/assets/{style.BRuM8477.css → style.DCD_zyg3.css} +1 -1
- package/dist/docs/assets/troubleshooting.md.CYEAO9bM.js +1 -0
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +9 -9
- package/dist/docs/deployment.md +7 -2
- package/dist/docs/evals.html +8 -8
- package/dist/docs/evals.md +3 -2
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-agents.html +7 -7
- package/dist/docs/guides/cloud-agents.md +4 -3
- package/dist/docs/guides/convert-automation.html +6 -6
- package/dist/docs/guides/github.html +7 -7
- package/dist/docs/guides/github.md +2 -1
- package/dist/docs/guides/grokbot-agents.html +7 -7
- package/dist/docs/guides/grokbot-agents.md +2 -2
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- package/dist/docs/guides/improve.html +40 -0
- package/dist/docs/guides/improve.md +91 -0
- package/dist/docs/guides/mcp-oauth.html +6 -6
- package/dist/docs/guides/opentelemetry.html +8 -8
- package/dist/docs/guides/opentelemetry.md +3 -4
- package/dist/docs/guides/slack.html +8 -8
- package/dist/docs/guides/slack.md +2 -1
- package/dist/docs/guides/webhooks.html +6 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/index.html +8 -8
- package/dist/docs/index.md +4 -0
- package/dist/docs/llms-full.txt +180 -61
- package/dist/docs/llms.txt +1 -0
- package/dist/docs/quickstart.html +6 -6
- package/dist/docs/reference/agent-config.html +7 -7
- package/dist/docs/reference/agent-config.md +5 -2
- package/dist/docs/reference/artifacts.html +6 -6
- package/dist/docs/reference/channels.html +7 -7
- package/dist/docs/reference/channels.md +11 -19
- package/dist/docs/reference/cli.html +11 -11
- package/dist/docs/reference/cli.md +23 -13
- package/dist/docs/reference/connections.html +8 -8
- package/dist/docs/reference/connections.md +2 -1
- package/dist/docs/reference/extensions.html +7 -7
- package/dist/docs/reference/extensions.md +2 -0
- package/dist/docs/reference/hooks.html +6 -6
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/instructions.html +6 -6
- package/dist/docs/reference/playground.html +5 -5
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/prompt.html +6 -6
- package/dist/docs/reference/result.html +6 -6
- package/dist/docs/reference/schedules.html +6 -6
- package/dist/docs/reference/sessions.html +5 -5
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/subagents.html +6 -6
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/scaffolding-agents.html +6 -6
- package/dist/docs/scaffolding-agents.md +2 -1
- package/dist/docs/skills/ab.html +6 -6
- package/dist/docs/skills/create-agent.html +8 -8
- package/dist/docs/skills/create-agent.md +1 -1
- package/dist/docs/skills/debug.html +6 -6
- package/dist/docs/skills/debug.md +1 -1
- package/dist/docs/skills/evals.html +8 -8
- package/dist/docs/skills/evals.md +1 -1
- package/dist/docs/skills/framework-map.html +6 -6
- package/dist/docs/skills/framework-map.md +4 -2
- package/dist/docs/skills/github.html +8 -8
- package/dist/docs/skills/github.md +1 -1
- package/dist/docs/skills/hillclimb.html +6 -6
- package/dist/docs/skills/index.html +5 -5
- package/dist/docs/skills/mcp-auth.html +6 -6
- package/dist/docs/skills/otel.html +8 -8
- package/dist/docs/skills/otel.md +3 -3
- package/dist/docs/skills/setup-slack.html +6 -6
- package/dist/docs/storage.html +6 -6
- package/dist/docs/templates/agentic-owners.html +6 -6
- package/dist/docs/templates/agents-md.html +6 -6
- package/dist/docs/templates/code-wiki.html +6 -6
- package/dist/docs/templates/demo.html +6 -6
- package/dist/docs/templates/grokbot-agents.html +6 -6
- package/dist/docs/templates/pr-autofixer.html +6 -6
- package/dist/docs/templates/security-help.html +6 -6
- package/dist/docs/templates/security-reviewer.html +6 -6
- package/dist/docs/templates/triage.html +6 -6
- package/dist/docs/troubleshooting.html +6 -6
- 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/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- 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 +61 -25
- 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/backend-client.d.ts +25 -0
- package/dist/internal/cursor/backend-client.d.ts.map +1 -1
- package/dist/internal/cursor/backend-client.js +57 -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 +35 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +46 -0
- package/dist/internal/deploy-manifest.d.ts +26 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +99 -13
- 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/discovery/agent.d.ts.map +1 -1
- package/dist/internal/discovery/agent.js +1 -0
- package/dist/internal/discovery/info.d.ts.map +1 -1
- package/dist/internal/discovery/info.js +7 -3
- package/dist/internal/discovery/modules.d.ts +1 -1
- package/dist/internal/discovery/modules.d.ts.map +1 -1
- package/dist/internal/discovery/modules.js +1 -0
- package/dist/internal/discovery/project.d.ts.map +1 -1
- package/dist/internal/discovery/project.js +3 -0
- package/dist/internal/discovery/reminders.d.ts +7 -0
- package/dist/internal/discovery/reminders.d.ts.map +1 -0
- package/dist/internal/discovery/reminders.js +65 -0
- package/dist/internal/discovery/subagent.js +1 -0
- package/dist/internal/distribution.d.ts +5 -0
- package/dist/internal/distribution.d.ts.map +1 -1
- package/dist/internal/distribution.js +17 -0
- package/dist/internal/framework-storage-selection.d.ts +14 -3
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/framework-storage-selection.js +36 -6
- 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 +44 -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 +89 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +309 -0
- package/dist/internal/hosted-delivery.d.ts +20 -1
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +179 -36
- 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/local-control-plane.d.ts +18 -2
- package/dist/internal/local-control-plane.d.ts.map +1 -1
- package/dist/internal/local-control-plane.js +199 -17
- 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 +189 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
- package/dist/internal/reminder-control-plane-protocol.js +251 -0
- package/dist/internal/reminder-runner.d.ts +56 -6
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +372 -57
- package/dist/internal/reminder-schedule.d.ts +5 -2
- package/dist/internal/reminder-schedule.d.ts.map +1 -1
- package/dist/internal/reminder-schedule.js +8 -6
- package/dist/internal/reminder-store.d.ts +24 -7
- package/dist/internal/reminder-store.d.ts.map +1 -1
- package/dist/internal/reminder-store.js +6 -5
- package/dist/internal/request-error-status.d.ts +2 -0
- package/dist/internal/request-error-status.d.ts.map +1 -1
- package/dist/internal/request-error-status.js +22 -3
- 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/scm/pr-url.d.ts +40 -0
- package/dist/internal/scm/pr-url.d.ts.map +1 -1
- package/dist/internal/scm/pr-url.js +93 -14
- 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 +118 -77
- package/dist/internal/session-engine.d.ts +6 -6
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +100 -67
- package/dist/playground/assets/{index-BaQzjaNK.js → index-BMDqAeXu.js} +34 -34
- package/dist/playground/assets/index-LUgJoWdL.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/reminders.d.ts +24 -5
- package/dist/reminders.d.ts.map +1 -1
- package/dist/reminders.js +28 -5
- 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 +122 -25
- 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/opentelemetry.md +3 -4
- package/docs/guides/slack.md +2 -1
- package/docs/reference/agent-config.md +5 -2
- package/docs/reference/channels.md +11 -19
- package/docs/reference/cli.md +23 -13
- 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 +10 -3
- 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/skills/otel/SKILL.md +3 -3
- package/src/bin/agent-serve.ts +28 -11
- package/src/channels/bitbucket/types.ts +2 -4
- package/src/channels/deployments/types.ts +5 -8
- package/src/channels/github/github-channel.ts +8 -8
- package/src/channels/github/types.ts +7 -4
- package/src/channels/gitlab/types.ts +2 -5
- package/src/channels/origin/origin-channel.ts +29 -13
- package/src/channels/origin/types.ts +7 -3
- package/src/channels/slack/defaults.ts +3 -0
- package/src/channels/slack/dispatch.ts +1 -0
- package/src/channels/slack/live-delivery.ts +53 -12
- package/src/channels/slack/types.ts +4 -3
- 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/index.ts +6 -3
- 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 +80 -22
- 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/backend-client.ts +90 -2
- package/src/internal/cursor/credentials.ts +35 -7
- package/src/internal/deploy-client.ts +70 -0
- package/src/internal/deploy-manifest.ts +131 -12
- package/src/internal/describe-error.ts +28 -0
- package/src/internal/discovery/agent.ts +1 -0
- package/src/internal/discovery/info.ts +6 -0
- package/src/internal/discovery/modules.ts +2 -4
- package/src/internal/discovery/project.ts +3 -0
- package/src/internal/discovery/reminders.ts +65 -0
- package/src/internal/discovery/subagent.ts +1 -0
- package/src/internal/distribution.ts +20 -0
- package/src/internal/framework-storage-selection.ts +43 -6
- package/src/internal/guest-network.ts +46 -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 +483 -0
- package/src/internal/hosted-delivery.ts +303 -43
- 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/local-control-plane.ts +281 -22
- package/src/internal/platform-timers.ts +191 -0
- package/src/internal/reminder-control-plane-protocol.ts +512 -0
- package/src/internal/reminder-runner.ts +481 -57
- package/src/internal/reminder-schedule.ts +11 -6
- package/src/internal/reminder-store.ts +27 -7
- package/src/internal/request-error-status.ts +27 -3
- package/src/internal/resolve-prod-target.ts +15 -0
- package/src/internal/scm/pr-url.ts +167 -20
- package/src/internal/sdk-runner.ts +2 -1
- package/src/internal/server.ts +151 -88
- package/src/internal/session-engine.ts +146 -83
- package/src/reminders.ts +35 -4
- package/src/storage-backends/cursor-hosted.ts +40 -1
- package/src/types.ts +134 -26
- package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.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-CceqnLTX.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/{skills_otel.md.CqhobH3B.lean.js → skills_otel.md.B5Ddv1xH.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
|
+
}
|
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Versioned internal reminder control-plane protocol.
|
|
3
|
+
*
|
|
4
|
+
* CP owns reminder id, status, fireCount, nextFireAt, claim, and
|
|
5
|
+
* completion/rearm. The guest validates create input and executes an
|
|
6
|
+
* immutable fire descriptor, returning a structured outcome. Cursor-hosted
|
|
7
|
+
* HTTP CP is not implemented here; {@link ReminderControlPlane} is the
|
|
8
|
+
* surface the next backend PR will speak.
|
|
9
|
+
*
|
|
10
|
+
* Not part of the public package API — keep behind the internal boundary.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { randomBytes } from "node:crypto";
|
|
14
|
+
import type {
|
|
15
|
+
JsonObject,
|
|
16
|
+
ReminderBusyPolicy,
|
|
17
|
+
ReminderScheduleKind,
|
|
18
|
+
} from "../types.js";
|
|
19
|
+
import { parseDuration } from "./duration.js";
|
|
20
|
+
import {
|
|
21
|
+
nextCronFireMs,
|
|
22
|
+
ReminderValidationError,
|
|
23
|
+
type ResolvedSchedule,
|
|
24
|
+
resolveSchedule,
|
|
25
|
+
} from "./reminder-schedule.js";
|
|
26
|
+
import type { ReminderRecord } from "./reminder-store.js";
|
|
27
|
+
|
|
28
|
+
/** Wire / persistence version for this control-plane protocol. */
|
|
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;
|
|
32
|
+
|
|
33
|
+
export type ReminderCpProtocolVersion = typeof REMINDER_CP_PROTOCOL_VERSION;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Normalized raw schedule for create intents (JSON-safe).
|
|
37
|
+
* CP resolves this into nextFireAtMs / intervalMs — guests must not send
|
|
38
|
+
* authoritative fire times.
|
|
39
|
+
*/
|
|
40
|
+
export type ReminderScheduleSpec =
|
|
41
|
+
| { kind: "every"; every: string }
|
|
42
|
+
| { kind: "cron"; cron: string }
|
|
43
|
+
| { kind: "delay"; delay: string | number }
|
|
44
|
+
| { kind: "at"; at: string };
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Guest → CP create intent. No id/status/fireCount/nextFireAt — CP assigns
|
|
48
|
+
* those after resolving {@link ReminderScheduleSpec}.
|
|
49
|
+
*/
|
|
50
|
+
export interface ReminderCreateIntent {
|
|
51
|
+
protocolVersion: ReminderCpProtocolVersion;
|
|
52
|
+
purpose: string;
|
|
53
|
+
channelId: string;
|
|
54
|
+
continuationToken: string;
|
|
55
|
+
target?: Record<string, unknown>;
|
|
56
|
+
schedule: ReminderScheduleSpec;
|
|
57
|
+
prompt?: string;
|
|
58
|
+
handler?: string;
|
|
59
|
+
args?: JsonObject;
|
|
60
|
+
hasRunHandler: boolean;
|
|
61
|
+
hasUntilFn: boolean;
|
|
62
|
+
untilPrompt?: string;
|
|
63
|
+
busyPolicy: ReminderBusyPolicy;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Immutable fire descriptor issued after CP claim. Guest execution must not
|
|
68
|
+
* mutate authoritative status/fireCount; it returns {@link ReminderExecutionOutcome}.
|
|
69
|
+
*/
|
|
70
|
+
export interface ReminderFireDescriptor {
|
|
71
|
+
protocolVersion: ReminderCpProtocolVersion;
|
|
72
|
+
id: string;
|
|
73
|
+
/** Fire index being executed (equals record.fireCount at claim time). */
|
|
74
|
+
fireCount: number;
|
|
75
|
+
/** CAS generation captured at claim; cancel/rekey bumps it. */
|
|
76
|
+
generation: number;
|
|
77
|
+
purpose: string;
|
|
78
|
+
channelId: string;
|
|
79
|
+
continuationToken: string;
|
|
80
|
+
target?: Record<string, unknown>;
|
|
81
|
+
scheduleKind: ReminderScheduleKind;
|
|
82
|
+
scheduleValue: string;
|
|
83
|
+
intervalMs?: number;
|
|
84
|
+
prompt?: string;
|
|
85
|
+
handler?: string;
|
|
86
|
+
args?: JsonObject;
|
|
87
|
+
hasRunHandler: boolean;
|
|
88
|
+
hasUntilFn: boolean;
|
|
89
|
+
untilPrompt?: string;
|
|
90
|
+
busyPolicy: ReminderBusyPolicy;
|
|
91
|
+
/** Due time that triggered this claim. */
|
|
92
|
+
nextFireAtMs: number;
|
|
93
|
+
claimedAtMs: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Claim token used to apply an outcome exactly once for (id, fireCount, generation). */
|
|
97
|
+
export interface ReminderFireClaim {
|
|
98
|
+
id: string;
|
|
99
|
+
fireCount: number;
|
|
100
|
+
generation: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Wire-JSON-safe guest execution outcome. Retry is explicit and keyed by
|
|
105
|
+
* (id, fireCount) — `"retry"` does not increment fireCount.
|
|
106
|
+
* `reschedule.at` is an ISO-8601 string (never a Date).
|
|
107
|
+
*/
|
|
108
|
+
export type ReminderExecutionOutcome =
|
|
109
|
+
| {
|
|
110
|
+
kind: "delivered";
|
|
111
|
+
/** Optional one-shot reschedule override (same as handler reschedule). */
|
|
112
|
+
reschedule?: ReminderWireReschedule;
|
|
113
|
+
}
|
|
114
|
+
| {
|
|
115
|
+
kind: "skipped";
|
|
116
|
+
reschedule?: ReminderWireReschedule;
|
|
117
|
+
}
|
|
118
|
+
| { kind: "stop"; reason: string }
|
|
119
|
+
| {
|
|
120
|
+
/** Re-arm the same fireCount after an explicit, idempotent retry. */
|
|
121
|
+
kind: "retry";
|
|
122
|
+
delayMs?: number;
|
|
123
|
+
reason?: string;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/** JSON-safe reschedule override for outcomes / hosted wire. */
|
|
127
|
+
export interface ReminderWireReschedule {
|
|
128
|
+
delay?: string | number;
|
|
129
|
+
/** ISO-8601 timestamp — never a Date instance. */
|
|
130
|
+
at?: string;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export type ReminderOutcomeApplyResult =
|
|
134
|
+
| { applied: true; record: ReminderRecord }
|
|
135
|
+
| {
|
|
136
|
+
applied: false;
|
|
137
|
+
reason:
|
|
138
|
+
| "missing"
|
|
139
|
+
| "stale_generation"
|
|
140
|
+
| "stale_fire_count"
|
|
141
|
+
| "not_claimed"
|
|
142
|
+
| "duplicate";
|
|
143
|
+
record?: ReminderRecord;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
/** Guest executor callback invoked by the CP after a successful claim. */
|
|
147
|
+
export type ReminderGuestExecutor = (
|
|
148
|
+
descriptor: ReminderFireDescriptor
|
|
149
|
+
) => Promise<ReminderExecutionOutcome>;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Control-plane reminder API (local disk today; Cursor-hosted HTTP later).
|
|
153
|
+
* Extends document CRUD with create-intent, cancel/rekey CAS, and outcome apply.
|
|
154
|
+
*/
|
|
155
|
+
export interface ReminderControlPlane {
|
|
156
|
+
readonly protocolVersion: ReminderCpProtocolVersion;
|
|
157
|
+
createReminder(intent: ReminderCreateIntent): Promise<ReminderRecord>;
|
|
158
|
+
getReminder(id: string): Promise<ReminderRecord | undefined>;
|
|
159
|
+
listReminders(): Promise<ReminderRecord[]>;
|
|
160
|
+
cancelReminder(
|
|
161
|
+
id: string,
|
|
162
|
+
reason?: string
|
|
163
|
+
): Promise<ReminderRecord | undefined>;
|
|
164
|
+
/**
|
|
165
|
+
* Re-key armed (and in-flight) reminders for a continuation rotation.
|
|
166
|
+
* Bumps generation so an in-flight outcome cannot land on the old token.
|
|
167
|
+
*/
|
|
168
|
+
rekeyReminders(
|
|
169
|
+
channelId: string,
|
|
170
|
+
oldToken: string,
|
|
171
|
+
newToken: string
|
|
172
|
+
): Promise<number>;
|
|
173
|
+
applyReminderOutcome(
|
|
174
|
+
claim: ReminderFireClaim,
|
|
175
|
+
outcome: ReminderExecutionOutcome
|
|
176
|
+
): Promise<ReminderOutcomeApplyResult>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function newReminderId(): string {
|
|
180
|
+
return `rem_${randomBytes(8).toString("hex")}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function recordGeneration(record: ReminderRecord): number {
|
|
184
|
+
return record.generation ?? 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** Map a JSON-safe schedule spec into resolveSchedule create fields. */
|
|
188
|
+
export function scheduleSpecToCreateFields(schedule: ReminderScheduleSpec): {
|
|
189
|
+
every?: string;
|
|
190
|
+
cron?: string;
|
|
191
|
+
delay?: string | number;
|
|
192
|
+
at?: string;
|
|
193
|
+
} {
|
|
194
|
+
switch (schedule.kind) {
|
|
195
|
+
case "every":
|
|
196
|
+
return { every: schedule.every };
|
|
197
|
+
case "cron":
|
|
198
|
+
return { cron: schedule.cron };
|
|
199
|
+
case "delay":
|
|
200
|
+
return { delay: schedule.delay };
|
|
201
|
+
case "at":
|
|
202
|
+
return { at: schedule.at };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Build a wire-safe schedule spec from guest create input.
|
|
208
|
+
* Normalizes `at` Dates/numbers to ISO strings.
|
|
209
|
+
*/
|
|
210
|
+
export function scheduleSpecFromCreateInput(input: {
|
|
211
|
+
every?: string;
|
|
212
|
+
cron?: string;
|
|
213
|
+
delay?: string | number;
|
|
214
|
+
at?: Date | string | number;
|
|
215
|
+
}): ReminderScheduleSpec {
|
|
216
|
+
const shapes = [
|
|
217
|
+
input.every !== undefined,
|
|
218
|
+
input.cron !== undefined,
|
|
219
|
+
input.delay !== undefined,
|
|
220
|
+
input.at !== undefined,
|
|
221
|
+
].filter(Boolean);
|
|
222
|
+
if (shapes.length !== 1) {
|
|
223
|
+
throw new ReminderValidationError(
|
|
224
|
+
"reminders.create requires exactly one of every, cron, delay, or at"
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
if (input.every !== undefined) {
|
|
228
|
+
return { kind: "every", every: input.every };
|
|
229
|
+
}
|
|
230
|
+
if (input.cron !== undefined) {
|
|
231
|
+
return { kind: "cron", cron: input.cron };
|
|
232
|
+
}
|
|
233
|
+
if (input.delay !== undefined) {
|
|
234
|
+
return { kind: "delay", delay: input.delay };
|
|
235
|
+
}
|
|
236
|
+
return { kind: "at", at: normalizeScheduleAt(input.at!) };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Normalize a guest-facing `at` value to an ISO string for the wire intent.
|
|
241
|
+
*/
|
|
242
|
+
export function normalizeScheduleAt(at: Date | string | number): string {
|
|
243
|
+
if (at instanceof Date) {
|
|
244
|
+
if (!Number.isFinite(at.getTime())) {
|
|
245
|
+
throw new ReminderValidationError(`Invalid at time: ${String(at)}`);
|
|
246
|
+
}
|
|
247
|
+
return at.toISOString();
|
|
248
|
+
}
|
|
249
|
+
if (typeof at === "number") {
|
|
250
|
+
if (!Number.isFinite(at)) {
|
|
251
|
+
throw new ReminderValidationError(`Invalid at time: ${String(at)}`);
|
|
252
|
+
}
|
|
253
|
+
return new Date(at).toISOString();
|
|
254
|
+
}
|
|
255
|
+
const ms = Date.parse(at);
|
|
256
|
+
if (!Number.isFinite(ms)) {
|
|
257
|
+
throw new ReminderValidationError(`Invalid at time: ${String(at)}`);
|
|
258
|
+
}
|
|
259
|
+
// Preserve caller string when it already parses; still wire-safe.
|
|
260
|
+
return at;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/** Normalize handler reschedule into a wire-JSON-safe shape. */
|
|
264
|
+
export function toWireReschedule(reschedule: {
|
|
265
|
+
delay?: string | number;
|
|
266
|
+
at?: Date | string;
|
|
267
|
+
}): ReminderWireReschedule {
|
|
268
|
+
const out: ReminderWireReschedule = {};
|
|
269
|
+
if (reschedule.delay !== undefined) {
|
|
270
|
+
out.delay = reschedule.delay;
|
|
271
|
+
}
|
|
272
|
+
if (reschedule.at !== undefined) {
|
|
273
|
+
out.at =
|
|
274
|
+
reschedule.at instanceof Date
|
|
275
|
+
? reschedule.at.toISOString()
|
|
276
|
+
: String(reschedule.at);
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Build a durable record from a create intent. CP owns schedule resolution
|
|
283
|
+
* via {@link resolveSchedule}; pass `nowMs` for deterministic tests.
|
|
284
|
+
*/
|
|
285
|
+
export function intentToNewRecord(
|
|
286
|
+
intent: ReminderCreateIntent,
|
|
287
|
+
nowMs: number = Date.now()
|
|
288
|
+
): ReminderRecord {
|
|
289
|
+
if (intent.protocolVersion !== REMINDER_CP_PROTOCOL_VERSION) {
|
|
290
|
+
throw new ReminderValidationError(
|
|
291
|
+
`Unsupported reminder CP protocol version ${String(intent.protocolVersion)}`
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const resolved: ResolvedSchedule = resolveSchedule(
|
|
295
|
+
scheduleSpecToCreateFields(intent.schedule),
|
|
296
|
+
nowMs
|
|
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
|
+
}
|
|
306
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
307
|
+
return {
|
|
308
|
+
version: 1,
|
|
309
|
+
id: newReminderId(),
|
|
310
|
+
purpose: intent.purpose,
|
|
311
|
+
channelId: intent.channelId,
|
|
312
|
+
continuationToken: intent.continuationToken,
|
|
313
|
+
target: intent.target,
|
|
314
|
+
scheduleKind: resolved.kind,
|
|
315
|
+
scheduleValue: resolved.value,
|
|
316
|
+
intervalMs: resolved.intervalMs,
|
|
317
|
+
prompt: intent.prompt,
|
|
318
|
+
handler: intent.handler,
|
|
319
|
+
args: intent.args,
|
|
320
|
+
hasRunHandler: intent.hasRunHandler,
|
|
321
|
+
hasUntilFn: intent.hasUntilFn,
|
|
322
|
+
untilPrompt: intent.untilPrompt,
|
|
323
|
+
busyPolicy: intent.busyPolicy,
|
|
324
|
+
status: "armed",
|
|
325
|
+
fireCount: 0,
|
|
326
|
+
nextFireAtMs: resolved.nextFireAtMs,
|
|
327
|
+
generation: 0,
|
|
328
|
+
createdAt: nowIso,
|
|
329
|
+
updatedAt: nowIso,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export function toFireDescriptor(
|
|
334
|
+
record: ReminderRecord,
|
|
335
|
+
claimedAtMs: number
|
|
336
|
+
): ReminderFireDescriptor {
|
|
337
|
+
return {
|
|
338
|
+
protocolVersion: REMINDER_CP_PROTOCOL_VERSION,
|
|
339
|
+
id: record.id,
|
|
340
|
+
fireCount: record.fireCount,
|
|
341
|
+
generation: recordGeneration(record),
|
|
342
|
+
purpose: record.purpose,
|
|
343
|
+
channelId: record.channelId,
|
|
344
|
+
continuationToken: record.continuationToken,
|
|
345
|
+
target: record.target,
|
|
346
|
+
scheduleKind: record.scheduleKind,
|
|
347
|
+
scheduleValue: record.scheduleValue,
|
|
348
|
+
intervalMs: record.intervalMs,
|
|
349
|
+
prompt: record.prompt,
|
|
350
|
+
handler: record.handler,
|
|
351
|
+
args: record.args,
|
|
352
|
+
hasRunHandler: record.hasRunHandler,
|
|
353
|
+
hasUntilFn: record.hasUntilFn,
|
|
354
|
+
untilPrompt: record.untilPrompt,
|
|
355
|
+
busyPolicy: record.busyPolicy,
|
|
356
|
+
nextFireAtMs: record.nextFireAtMs,
|
|
357
|
+
claimedAtMs,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export function claimFromDescriptor(
|
|
362
|
+
descriptor: ReminderFireDescriptor
|
|
363
|
+
): ReminderFireClaim {
|
|
364
|
+
return {
|
|
365
|
+
id: descriptor.id,
|
|
366
|
+
fireCount: descriptor.fireCount,
|
|
367
|
+
generation: descriptor.generation,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function resolveRescheduleMs(
|
|
372
|
+
reschedule: ReminderWireReschedule,
|
|
373
|
+
nowMs: number
|
|
374
|
+
): number {
|
|
375
|
+
if (reschedule.delay !== undefined) {
|
|
376
|
+
return nowMs + parseDuration(reschedule.delay);
|
|
377
|
+
}
|
|
378
|
+
if (reschedule.at !== undefined) {
|
|
379
|
+
const atMs = Date.parse(reschedule.at);
|
|
380
|
+
if (!Number.isFinite(atMs)) {
|
|
381
|
+
throw new ReminderValidationError("Invalid reminder reschedule.at");
|
|
382
|
+
}
|
|
383
|
+
return atMs;
|
|
384
|
+
}
|
|
385
|
+
throw new ReminderValidationError("reminder reschedule requires delay or at");
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** Next fire time from the record's recurring schedule (CP-owned). */
|
|
389
|
+
export function nextScheduleFireAtMs(
|
|
390
|
+
record: ReminderRecord,
|
|
391
|
+
afterMs: number
|
|
392
|
+
): number {
|
|
393
|
+
if (record.scheduleKind === "every" && record.intervalMs !== undefined) {
|
|
394
|
+
return afterMs + record.intervalMs;
|
|
395
|
+
}
|
|
396
|
+
if (record.scheduleKind === "cron") {
|
|
397
|
+
return nextCronFireMs(record.scheduleValue, afterMs);
|
|
398
|
+
}
|
|
399
|
+
throw new ReminderValidationError(
|
|
400
|
+
`Cannot advance schedule for reminder "${record.id}" (kind=${record.scheduleKind})`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function isOneShotSchedule(kind: ReminderScheduleKind): boolean {
|
|
405
|
+
return kind === "delay" || kind === "at";
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Pure CAS apply of a guest outcome onto a reminder record.
|
|
410
|
+
* Callers persist the returned record when `applied: true`.
|
|
411
|
+
* Requires `status === "firing"` — an armed record was never claimed.
|
|
412
|
+
*/
|
|
413
|
+
export function applyOutcomeToRecord(
|
|
414
|
+
record: ReminderRecord,
|
|
415
|
+
claim: ReminderFireClaim,
|
|
416
|
+
outcome: ReminderExecutionOutcome,
|
|
417
|
+
nowMs: number = Date.now()
|
|
418
|
+
): ReminderOutcomeApplyResult {
|
|
419
|
+
if (record.id !== claim.id) {
|
|
420
|
+
return { applied: false, reason: "missing", record };
|
|
421
|
+
}
|
|
422
|
+
// Prefer duplicate over generation mismatch: a successful prior apply
|
|
423
|
+
// bumps both fireCount and generation, and retries of (id, fireCount)
|
|
424
|
+
// must stay idempotent no-ops.
|
|
425
|
+
if (record.fireCount > claim.fireCount) {
|
|
426
|
+
return { applied: false, reason: "duplicate", record };
|
|
427
|
+
}
|
|
428
|
+
if (record.fireCount < claim.fireCount) {
|
|
429
|
+
return { applied: false, reason: "stale_fire_count", record };
|
|
430
|
+
}
|
|
431
|
+
if (recordGeneration(record) !== claim.generation) {
|
|
432
|
+
return { applied: false, reason: "stale_generation", record };
|
|
433
|
+
}
|
|
434
|
+
// Only a claimed (firing) reminder can accept an outcome.
|
|
435
|
+
if (record.status !== "firing") {
|
|
436
|
+
return { applied: false, reason: "not_claimed", record };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const nowIso = new Date(nowMs).toISOString();
|
|
440
|
+
|
|
441
|
+
if (outcome.kind === "retry") {
|
|
442
|
+
const delayMs = Math.max(
|
|
443
|
+
REMINDER_CP_MIN_RECURRING_INTERVAL_MS,
|
|
444
|
+
outcome.delayMs ?? REMINDER_CP_MIN_RECURRING_INTERVAL_MS
|
|
445
|
+
);
|
|
446
|
+
const updated: ReminderRecord = {
|
|
447
|
+
...record,
|
|
448
|
+
status: "armed",
|
|
449
|
+
nextFireAtMs: nowMs + delayMs,
|
|
450
|
+
// Same fireCount; bump generation so a second apply of the old claim is stale.
|
|
451
|
+
generation: recordGeneration(record) + 1,
|
|
452
|
+
updatedAt: nowIso,
|
|
453
|
+
};
|
|
454
|
+
return { applied: true, record: updated };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (outcome.kind === "stop") {
|
|
458
|
+
const updated: ReminderRecord = {
|
|
459
|
+
...record,
|
|
460
|
+
status: "completed",
|
|
461
|
+
disarmReason: outcome.reason,
|
|
462
|
+
fireCount: record.fireCount + 1,
|
|
463
|
+
generation: recordGeneration(record) + 1,
|
|
464
|
+
updatedAt: nowIso,
|
|
465
|
+
};
|
|
466
|
+
return { applied: true, record: updated };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const reschedule = outcome.reschedule;
|
|
470
|
+
if (reschedule !== undefined) {
|
|
471
|
+
const nextFireAtMs = resolveRescheduleMs(reschedule, nowMs);
|
|
472
|
+
const updated: ReminderRecord = {
|
|
473
|
+
...record,
|
|
474
|
+
status: "armed",
|
|
475
|
+
fireCount: record.fireCount + 1,
|
|
476
|
+
nextFireAtMs,
|
|
477
|
+
scheduleKind: reschedule.delay !== undefined ? "delay" : "at",
|
|
478
|
+
scheduleValue:
|
|
479
|
+
reschedule.delay !== undefined
|
|
480
|
+
? String(reschedule.delay)
|
|
481
|
+
: String(reschedule.at),
|
|
482
|
+
intervalMs: undefined,
|
|
483
|
+
generation: recordGeneration(record) + 1,
|
|
484
|
+
updatedAt: nowIso,
|
|
485
|
+
};
|
|
486
|
+
return { applied: true, record: updated };
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (isOneShotSchedule(record.scheduleKind)) {
|
|
490
|
+
const updated: ReminderRecord = {
|
|
491
|
+
...record,
|
|
492
|
+
status: "completed",
|
|
493
|
+
disarmReason:
|
|
494
|
+
outcome.kind === "skipped" ? "skipped_one_shot" : "one_shot_complete",
|
|
495
|
+
fireCount: record.fireCount + 1,
|
|
496
|
+
generation: recordGeneration(record) + 1,
|
|
497
|
+
updatedAt: nowIso,
|
|
498
|
+
};
|
|
499
|
+
return { applied: true, record: updated };
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const nextFireAtMs = nextScheduleFireAtMs(record, nowMs);
|
|
503
|
+
const updated: ReminderRecord = {
|
|
504
|
+
...record,
|
|
505
|
+
status: "armed",
|
|
506
|
+
fireCount: record.fireCount + 1,
|
|
507
|
+
nextFireAtMs,
|
|
508
|
+
generation: recordGeneration(record) + 1,
|
|
509
|
+
updatedAt: nowIso,
|
|
510
|
+
};
|
|
511
|
+
return { applied: true, record: updated };
|
|
512
|
+
}
|