@cursor/july 0.1.105 → 0.1.107

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.
Files changed (217) hide show
  1. package/dist/bin/agent-serve.js +1 -8
  2. package/dist/channels/bitbucket/types.d.ts +2 -4
  3. package/dist/channels/bitbucket/types.d.ts.map +1 -1
  4. package/dist/channels/deployments/types.d.ts +5 -8
  5. package/dist/channels/deployments/types.d.ts.map +1 -1
  6. package/dist/channels/github/types.d.ts +2 -4
  7. package/dist/channels/github/types.d.ts.map +1 -1
  8. package/dist/channels/gitlab/types.d.ts +2 -5
  9. package/dist/channels/gitlab/types.d.ts.map +1 -1
  10. package/dist/channels/origin/types.d.ts +2 -3
  11. package/dist/channels/origin/types.d.ts.map +1 -1
  12. package/dist/channels/slack/defaults.d.ts.map +1 -1
  13. package/dist/channels/slack/defaults.js +3 -0
  14. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  15. package/dist/channels/slack/dispatch.js +1 -1
  16. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  17. package/dist/channels/slack/live-delivery.js +48 -11
  18. package/dist/channels/slack/types.d.ts +4 -3
  19. package/dist/channels/slack/types.d.ts.map +1 -1
  20. package/dist/docs/404.html +3 -3
  21. package/dist/docs/ab.html +6 -6
  22. package/dist/docs/assets/{app.BVwaffdK.js → app.CtuckIiL.js} +1 -1
  23. package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +1 -0
  24. package/dist/docs/assets/chunks/{VPLocalSearchBox.WuWTZ1x8.js → VPLocalSearchBox.Bkkhnu8K.js} +2 -2
  25. package/dist/docs/assets/chunks/{theme.BYdI_6sF.js → theme.DhpIe0Pa.js} +2 -2
  26. package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.js → guides_opentelemetry.md.SCRMskno.js} +3 -3
  27. package/dist/docs/assets/{guides_opentelemetry.md.COh9jFVg.lean.js → guides_opentelemetry.md.SCRMskno.lean.js} +1 -1
  28. package/dist/docs/assets/{reference_channels.md.DBGcaB7m.js → reference_channels.md.D-qTqwcq.js} +2 -2
  29. package/dist/docs/assets/{reference_channels.md.DBGcaB7m.lean.js → reference_channels.md.D-qTqwcq.lean.js} +1 -1
  30. package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.js → reference_cli.md.Dm67hd2D.js} +1 -1
  31. package/dist/docs/assets/{skills_otel.md.CqhobH3B.js → skills_otel.md.B5Ddv1xH.js} +1 -1
  32. package/dist/docs/assets/{style.BRuM8477.css → style.DCD_zyg3.css} +1 -1
  33. package/dist/docs/building-with-agents.html +6 -6
  34. package/dist/docs/concepts.html +5 -5
  35. package/dist/docs/deployment.html +6 -6
  36. package/dist/docs/evals.html +6 -6
  37. package/dist/docs/guides/agent-to-agent.html +6 -6
  38. package/dist/docs/guides/cloud-agents.html +6 -6
  39. package/dist/docs/guides/convert-automation.html +6 -6
  40. package/dist/docs/guides/github.html +6 -6
  41. package/dist/docs/guides/grokbot-agents.html +6 -6
  42. package/dist/docs/guides/human-in-the-loop.html +6 -6
  43. package/dist/docs/guides/mcp-oauth.html +6 -6
  44. package/dist/docs/guides/opentelemetry.html +8 -8
  45. package/dist/docs/guides/opentelemetry.md +3 -4
  46. package/dist/docs/guides/slack.html +6 -6
  47. package/dist/docs/guides/webhooks.html +6 -6
  48. package/dist/docs/hashmap.json +1 -1
  49. package/dist/docs/hillclimbing.html +6 -6
  50. package/dist/docs/index.html +6 -6
  51. package/dist/docs/llms-full.txt +22 -28
  52. package/dist/docs/quickstart.html +6 -6
  53. package/dist/docs/reference/agent-config.html +6 -6
  54. package/dist/docs/reference/artifacts.html +6 -6
  55. package/dist/docs/reference/channels.html +7 -7
  56. package/dist/docs/reference/channels.md +11 -19
  57. package/dist/docs/reference/cli.html +8 -8
  58. package/dist/docs/reference/cli.md +5 -2
  59. package/dist/docs/reference/connections.html +6 -6
  60. package/dist/docs/reference/extensions.html +6 -6
  61. package/dist/docs/reference/hooks.html +6 -6
  62. package/dist/docs/reference/http-api.html +6 -6
  63. package/dist/docs/reference/instructions.html +6 -6
  64. package/dist/docs/reference/playground.html +5 -5
  65. package/dist/docs/reference/project-layout.html +6 -6
  66. package/dist/docs/reference/prompt.html +6 -6
  67. package/dist/docs/reference/result.html +6 -6
  68. package/dist/docs/reference/schedules.html +6 -6
  69. package/dist/docs/reference/sessions.html +5 -5
  70. package/dist/docs/reference/skills.html +6 -6
  71. package/dist/docs/reference/subagents.html +6 -6
  72. package/dist/docs/reference/tools.html +6 -6
  73. package/dist/docs/scaffolding-agents.html +5 -5
  74. package/dist/docs/skills/ab.html +6 -6
  75. package/dist/docs/skills/create-agent.html +6 -6
  76. package/dist/docs/skills/debug.html +5 -5
  77. package/dist/docs/skills/evals.html +6 -6
  78. package/dist/docs/skills/framework-map.html +5 -5
  79. package/dist/docs/skills/github.html +6 -6
  80. package/dist/docs/skills/hillclimb.html +6 -6
  81. package/dist/docs/skills/index.html +5 -5
  82. package/dist/docs/skills/mcp-auth.html +6 -6
  83. package/dist/docs/skills/otel.html +8 -8
  84. package/dist/docs/skills/otel.md +3 -3
  85. package/dist/docs/skills/setup-slack.html +6 -6
  86. package/dist/docs/storage.html +6 -6
  87. package/dist/docs/templates/agentic-owners.html +6 -6
  88. package/dist/docs/templates/agents-md.html +6 -6
  89. package/dist/docs/templates/code-wiki.html +6 -6
  90. package/dist/docs/templates/demo.html +6 -6
  91. package/dist/docs/templates/grokbot-agents.html +6 -6
  92. package/dist/docs/templates/pr-autofixer.html +6 -6
  93. package/dist/docs/templates/security-help.html +6 -6
  94. package/dist/docs/templates/security-reviewer.html +6 -6
  95. package/dist/docs/templates/triage.html +6 -6
  96. package/dist/docs/troubleshooting.html +5 -5
  97. package/dist/index.d.ts +4 -5
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3 -4
  100. package/dist/internal/cli-deploy.d.ts.map +1 -1
  101. package/dist/internal/cli-deploy.js +23 -11
  102. package/dist/internal/cursor/backend-client.d.ts +25 -0
  103. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  104. package/dist/internal/cursor/backend-client.js +57 -2
  105. package/dist/internal/deploy-client.d.ts +8 -0
  106. package/dist/internal/deploy-client.d.ts.map +1 -1
  107. package/dist/internal/deploy-client.js +14 -0
  108. package/dist/internal/deploy-manifest.d.ts +3 -0
  109. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  110. package/dist/internal/deploy-manifest.js +15 -12
  111. package/dist/internal/discovery/agent.d.ts.map +1 -1
  112. package/dist/internal/discovery/agent.js +1 -0
  113. package/dist/internal/discovery/info.d.ts.map +1 -1
  114. package/dist/internal/discovery/info.js +7 -3
  115. package/dist/internal/discovery/modules.d.ts +1 -1
  116. package/dist/internal/discovery/modules.d.ts.map +1 -1
  117. package/dist/internal/discovery/modules.js +1 -0
  118. package/dist/internal/discovery/project.d.ts.map +1 -1
  119. package/dist/internal/discovery/project.js +3 -0
  120. package/dist/internal/discovery/reminders.d.ts +7 -0
  121. package/dist/internal/discovery/reminders.d.ts.map +1 -0
  122. package/dist/internal/discovery/reminders.js +65 -0
  123. package/dist/internal/discovery/subagent.js +1 -0
  124. package/dist/internal/distribution.d.ts +5 -0
  125. package/dist/internal/distribution.d.ts.map +1 -1
  126. package/dist/internal/distribution.js +17 -0
  127. package/dist/internal/framework-storage-selection.d.ts +14 -3
  128. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  129. package/dist/internal/framework-storage-selection.js +36 -6
  130. package/dist/internal/guest-network.d.ts.map +1 -1
  131. package/dist/internal/guest-network.js +2 -0
  132. package/dist/internal/hosted-delivery-protocol.d.ts +48 -0
  133. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  134. package/dist/internal/hosted-delivery-protocol.js +60 -0
  135. package/dist/internal/hosted-delivery.d.ts +10 -0
  136. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  137. package/dist/internal/hosted-delivery.js +83 -18
  138. package/dist/internal/local-control-plane.d.ts +18 -2
  139. package/dist/internal/local-control-plane.d.ts.map +1 -1
  140. package/dist/internal/local-control-plane.js +199 -17
  141. package/dist/internal/reminder-control-plane-protocol.d.ts +187 -0
  142. package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
  143. package/dist/internal/reminder-control-plane-protocol.js +244 -0
  144. package/dist/internal/reminder-runner.d.ts +48 -6
  145. package/dist/internal/reminder-runner.d.ts.map +1 -1
  146. package/dist/internal/reminder-runner.js +336 -48
  147. package/dist/internal/reminder-schedule.d.ts +5 -2
  148. package/dist/internal/reminder-schedule.d.ts.map +1 -1
  149. package/dist/internal/reminder-schedule.js +8 -6
  150. package/dist/internal/reminder-store.d.ts +24 -7
  151. package/dist/internal/reminder-store.d.ts.map +1 -1
  152. package/dist/internal/reminder-store.js +6 -5
  153. package/dist/internal/request-error-status.d.ts +2 -0
  154. package/dist/internal/request-error-status.d.ts.map +1 -1
  155. package/dist/internal/request-error-status.js +22 -3
  156. package/dist/internal/scm/pr-url.d.ts +40 -0
  157. package/dist/internal/scm/pr-url.d.ts.map +1 -1
  158. package/dist/internal/scm/pr-url.js +93 -14
  159. package/dist/internal/server.d.ts.map +1 -1
  160. package/dist/internal/server.js +67 -27
  161. package/dist/internal/session-engine.d.ts +4 -0
  162. package/dist/internal/session-engine.d.ts.map +1 -1
  163. package/dist/internal/session-engine.js +5 -0
  164. package/dist/playground/assets/{index-BaQzjaNK.js → index-Bq2HpEQB.js} +23 -23
  165. package/dist/playground/assets/index-CZKKNlmb.css +1 -0
  166. package/dist/playground/index.html +2 -2
  167. package/dist/reminders.d.ts +24 -5
  168. package/dist/reminders.d.ts.map +1 -1
  169. package/dist/reminders.js +28 -5
  170. package/dist/types.d.ts +89 -20
  171. package/dist/types.d.ts.map +1 -1
  172. package/docs/guides/opentelemetry.md +3 -4
  173. package/docs/reference/channels.md +11 -19
  174. package/docs/reference/cli.md +5 -2
  175. package/package.json +3 -3
  176. package/skills/otel/SKILL.md +3 -3
  177. package/src/bin/agent-serve.ts +1 -9
  178. package/src/channels/bitbucket/types.ts +2 -4
  179. package/src/channels/deployments/types.ts +5 -8
  180. package/src/channels/github/types.ts +2 -4
  181. package/src/channels/gitlab/types.ts +2 -5
  182. package/src/channels/origin/types.ts +2 -3
  183. package/src/channels/slack/defaults.ts +3 -0
  184. package/src/channels/slack/dispatch.ts +1 -0
  185. package/src/channels/slack/live-delivery.ts +53 -12
  186. package/src/channels/slack/types.ts +4 -3
  187. package/src/index.ts +6 -3
  188. package/src/internal/cli-deploy.ts +30 -12
  189. package/src/internal/cursor/backend-client.ts +90 -2
  190. package/src/internal/deploy-client.ts +16 -0
  191. package/src/internal/deploy-manifest.ts +16 -11
  192. package/src/internal/discovery/agent.ts +1 -0
  193. package/src/internal/discovery/info.ts +6 -0
  194. package/src/internal/discovery/modules.ts +2 -4
  195. package/src/internal/discovery/project.ts +3 -0
  196. package/src/internal/discovery/reminders.ts +65 -0
  197. package/src/internal/discovery/subagent.ts +1 -0
  198. package/src/internal/distribution.ts +20 -0
  199. package/src/internal/framework-storage-selection.ts +43 -6
  200. package/src/internal/guest-network.ts +3 -0
  201. package/src/internal/hosted-delivery-protocol.ts +96 -0
  202. package/src/internal/hosted-delivery.ts +154 -27
  203. package/src/internal/local-control-plane.ts +281 -22
  204. package/src/internal/reminder-control-plane-protocol.ts +499 -0
  205. package/src/internal/reminder-runner.ts +421 -48
  206. package/src/internal/reminder-schedule.ts +11 -6
  207. package/src/internal/reminder-store.ts +27 -7
  208. package/src/internal/request-error-status.ts +27 -3
  209. package/src/internal/scm/pr-url.ts +167 -20
  210. package/src/internal/server.ts +69 -16
  211. package/src/internal/session-engine.ts +5 -0
  212. package/src/reminders.ts +35 -4
  213. package/src/types.ts +95 -20
  214. package/dist/docs/assets/chunks/@localSearchIndexroot.CJxMklAy.js +0 -1
  215. package/dist/playground/assets/index-CceqnLTX.css +0 -1
  216. /package/dist/docs/assets/{reference_cli.md.C6Lo8SEd.lean.js → reference_cli.md.Dm67hd2D.lean.js} +0 -0
  217. /package/dist/docs/assets/{skills_otel.md.CqhobH3B.lean.js → skills_otel.md.B5Ddv1xH.lean.js} +0 -0
@@ -3,6 +3,12 @@
3
3
  * and reminder documents + clocks, then fires into the kit the same way the
4
4
  * Cursor backend does over HTTP when `--no-control-plane` is set.
5
5
  *
6
+ * Reminder lifecycle (protocol v1): CP assigns id, arms timers, claims a due
7
+ * fire once, invokes a guest executor with an immutable descriptor, and
8
+ * applies the outcome with generation/fireCount CAS. Cancel/rekey racing an
9
+ * in-flight execution bumps generation so a stale outcome cannot resurrect
10
+ * the reminder. Legacy `fireReminder(id, fireCount)` remains for v1 runners.
11
+ *
6
12
  * v2 ingest lives here: `routeContinuation` stamps the mailbox (July twin
7
13
  * of hosted `routeVerifiedIngressContinuation`), and `attachScmEvents`
8
14
  * owns the `/v0/scm-events` long poll. v1 keeps engine-side scm-events.
@@ -20,11 +26,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
20
26
  import { join } from "node:path";
21
27
  import { parseCron } from "./cron.js";
22
28
  import { routeLocalIngressContinuation, } from "./local-ingress.js";
29
+ import { applyOutcomeToRecord, claimFromDescriptor, intentToNewRecord, REMINDER_CP_PROTOCOL_VERSION, recordGeneration, toFireDescriptor, } from "./reminder-control-plane-protocol.js";
23
30
  import { MAX_TIMER_MS } from "./reminder-schedule.js";
24
31
  import { ReminderStore } from "./reminder-store.js";
25
32
  export function createLocalControlPlane(opts) {
26
33
  return __awaiter(this, void 0, void 0, function* () {
27
34
  var _a, _b;
35
+ if (opts.fireReminder === undefined && opts.executeReminder === undefined) {
36
+ throw new Error("createLocalControlPlane requires fireReminder and/or executeReminder");
37
+ }
28
38
  const logger = (_a = opts.logger) !== null && _a !== void 0 ? _a : (() => { });
29
39
  const armReminderClocks = opts.armReminderClocks !== false;
30
40
  const store = opts.stateDir === undefined
@@ -32,10 +42,21 @@ export function createLocalControlPlane(opts) {
32
42
  : new ReminderStore(join(opts.stateDir, "reminders"));
33
43
  const schedules = new Map();
34
44
  const reminderTimers = new Map();
35
- const reminders = new Map(((_b = (yield store === null || store === void 0 ? void 0 : store.list())) !== null && _b !== void 0 ? _b : []).map((record) => [record.id, record]));
45
+ const reminders = new Map(((_b = (yield store === null || store === void 0 ? void 0 : store.list())) !== null && _b !== void 0 ? _b : []).map((record) => { var _a; return [
46
+ record.id,
47
+ Object.assign(Object.assign({}, record), { generation: (_a = record.generation) !== null && _a !== void 0 ? _a : 0 }),
48
+ ]; }));
49
+ /** In-flight claims keyed by reminder id (single-flight per id). */
50
+ const inflight = new Map();
36
51
  let scheduleTimer;
37
52
  let lastFiredMinute = -1;
38
53
  let scmRelay;
54
+ function persist(record) {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ reminders.set(record.id, record);
57
+ yield store === null || store === void 0 ? void 0 : store.save(record);
58
+ });
59
+ }
39
60
  function armScheduleTicker() {
40
61
  var _a;
41
62
  if (scheduleTimer !== undefined || schedules.size === 0) {
@@ -80,10 +101,96 @@ export function createLocalControlPlane(opts) {
80
101
  function armReminder(record) {
81
102
  var _a;
82
103
  clearReminderTimer(record.id);
104
+ if (record.status !== "armed") {
105
+ return;
106
+ }
83
107
  const delay = Math.min(MAX_TIMER_MS, Math.max(0, record.nextFireAtMs - Date.now()));
84
108
  const timer = setTimeout(() => {
85
109
  reminderTimers.delete(record.id);
86
- const current = reminders.get(record.id);
110
+ void onReminderDue(record.id).catch((error) => {
111
+ logger(`[agent-sdk] reminder "${record.id}" dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
112
+ });
113
+ }, delay);
114
+ (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
115
+ reminderTimers.set(record.id, timer);
116
+ }
117
+ function claimDue(id) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ const current = reminders.get(id);
120
+ if (current === undefined || current.status !== "armed") {
121
+ return undefined;
122
+ }
123
+ if (current.nextFireAtMs > Date.now() + 50) {
124
+ armReminder(current);
125
+ return undefined;
126
+ }
127
+ if (inflight.has(id)) {
128
+ return undefined;
129
+ }
130
+ const claimed = Object.assign(Object.assign({}, current), { status: "firing", generation: recordGeneration(current), updatedAt: new Date().toISOString() });
131
+ yield persist(claimed);
132
+ inflight.set(id, {
133
+ id: claimed.id,
134
+ fireCount: claimed.fireCount,
135
+ generation: recordGeneration(claimed),
136
+ });
137
+ return claimed;
138
+ });
139
+ }
140
+ function applyReminderOutcome(claim, outcome) {
141
+ return __awaiter(this, void 0, void 0, function* () {
142
+ const current = reminders.get(claim.id);
143
+ if (current === undefined) {
144
+ inflight.delete(claim.id);
145
+ return { applied: false, reason: "missing" };
146
+ }
147
+ const result = applyOutcomeToRecord(current, claim, outcome);
148
+ inflight.delete(claim.id);
149
+ if (!result.applied) {
150
+ if (current.status === "armed" && armReminderClocks) {
151
+ armReminder(current);
152
+ }
153
+ return result;
154
+ }
155
+ yield persist(result.record);
156
+ if (armReminderClocks && result.record.status === "armed") {
157
+ armReminder(result.record);
158
+ }
159
+ else {
160
+ clearReminderTimer(result.record.id);
161
+ }
162
+ return result;
163
+ });
164
+ }
165
+ function onReminderDue(id) {
166
+ return __awaiter(this, void 0, void 0, function* () {
167
+ // Protocol path: claim → guest execute → CAS apply.
168
+ if (opts.executeReminder !== undefined) {
169
+ const claimed = yield claimDue(id);
170
+ if (claimed === undefined) {
171
+ return;
172
+ }
173
+ const descriptor = toFireDescriptor(claimed, Date.now());
174
+ let outcome;
175
+ try {
176
+ outcome = yield opts.executeReminder(descriptor);
177
+ }
178
+ catch (error) {
179
+ logger(`[agent-sdk] reminder "${id}" guest execution failed: ${error instanceof Error ? error.message : String(error)}`);
180
+ outcome = {
181
+ kind: "retry",
182
+ delayMs: 1000,
183
+ reason: "guest_error",
184
+ };
185
+ }
186
+ const result = yield applyReminderOutcome(claimFromDescriptor(descriptor), outcome);
187
+ if (!result.applied) {
188
+ logger(`[agent-sdk] reminder "${id}" outcome not applied: ${result.reason}`);
189
+ }
190
+ return;
191
+ }
192
+ // Legacy v1: do not claim/firing — runner mutates via putReminder after fire.
193
+ const current = reminders.get(id);
87
194
  if (current === undefined || current.status !== "armed") {
88
195
  return;
89
196
  }
@@ -91,14 +198,16 @@ export function createLocalControlPlane(opts) {
91
198
  armReminder(current);
92
199
  return;
93
200
  }
94
- void opts.fireReminder(current.id, current.fireCount).catch((error) => {
95
- logger(`[agent-sdk] reminder "${current.id}" dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
96
- });
97
- }, delay);
98
- (_a = timer.unref) === null || _a === void 0 ? void 0 : _a.call(timer);
99
- reminderTimers.set(record.id, timer);
201
+ try {
202
+ yield opts.fireReminder(current.id, current.fireCount);
203
+ }
204
+ catch (error) {
205
+ logger(`[agent-sdk] reminder "${id}" dispatch failed: ${error instanceof Error ? error.message : String(error)}`);
206
+ }
207
+ });
100
208
  }
101
209
  return {
210
+ protocolVersion: REMINDER_CP_PROTOCOL_VERSION,
102
211
  routeContinuation: routeLocalIngressContinuation,
103
212
  attachScmEvents(relay) {
104
213
  scmRelay = relay;
@@ -121,13 +230,17 @@ export function createLocalControlPlane(opts) {
121
230
  },
122
231
  putReminder(record) {
123
232
  return __awaiter(this, void 0, void 0, function* () {
124
- reminders.set(record.id, record);
125
- yield store === null || store === void 0 ? void 0 : store.save(record);
126
- if (armReminderClocks && record.status === "armed") {
127
- armReminder(record);
233
+ var _a;
234
+ const normalized = Object.assign(Object.assign({}, record), { generation: (_a = record.generation) !== null && _a !== void 0 ? _a : 0 });
235
+ yield persist(normalized);
236
+ if (armReminderClocks && normalized.status === "armed") {
237
+ armReminder(normalized);
128
238
  return;
129
239
  }
130
- clearReminderTimer(record.id);
240
+ clearReminderTimer(normalized.id);
241
+ if (normalized.status !== "firing") {
242
+ inflight.delete(normalized.id);
243
+ }
131
244
  });
132
245
  },
133
246
  getReminder(id) {
@@ -143,16 +256,84 @@ export function createLocalControlPlane(opts) {
143
256
  deleteReminder(id) {
144
257
  return __awaiter(this, void 0, void 0, function* () {
145
258
  clearReminderTimer(id);
259
+ inflight.delete(id);
146
260
  reminders.delete(id);
147
261
  yield store === null || store === void 0 ? void 0 : store.delete(id);
148
262
  });
149
263
  },
264
+ createReminder(intent) {
265
+ return __awaiter(this, void 0, void 0, function* () {
266
+ if (intent.protocolVersion !== REMINDER_CP_PROTOCOL_VERSION) {
267
+ throw new Error(`Unsupported reminder CP protocol version ${String(intent.protocolVersion)}`);
268
+ }
269
+ const record = intentToNewRecord(intent);
270
+ yield persist(record);
271
+ if (armReminderClocks && record.status === "armed") {
272
+ armReminder(record);
273
+ }
274
+ return record;
275
+ });
276
+ },
277
+ cancelReminder(id_1) {
278
+ return __awaiter(this, arguments, void 0, function* (id, reason = "explicit_cancel") {
279
+ const current = reminders.get(id);
280
+ if (current === undefined) {
281
+ return undefined;
282
+ }
283
+ if (current.status === "completed" ||
284
+ current.status === "cancelled" ||
285
+ current.status === "disarmed") {
286
+ return current;
287
+ }
288
+ clearReminderTimer(id);
289
+ inflight.delete(id);
290
+ const updated = Object.assign(Object.assign({}, current), { status: "cancelled", disarmReason: reason, generation: recordGeneration(current) + 1, updatedAt: new Date().toISOString() });
291
+ yield persist(updated);
292
+ return updated;
293
+ });
294
+ },
295
+ rekeyReminders(channelId, oldToken, newToken) {
296
+ return __awaiter(this, void 0, void 0, function* () {
297
+ let n = 0;
298
+ for (const record of [...reminders.values()]) {
299
+ if (record.channelId !== channelId ||
300
+ record.continuationToken !== oldToken) {
301
+ continue;
302
+ }
303
+ if (record.status !== "armed" && record.status !== "firing") {
304
+ continue;
305
+ }
306
+ const wasInflight = inflight.has(record.id);
307
+ inflight.delete(record.id);
308
+ let updated = Object.assign(Object.assign({}, record), { continuationToken: newToken, status: "armed", generation: recordGeneration(record) + 1, updatedAt: new Date().toISOString() });
309
+ // Interrupted in-flight fire: retry the same fireCount soon.
310
+ if (wasInflight) {
311
+ updated = Object.assign(Object.assign({}, updated), { nextFireAtMs: Math.min(updated.nextFireAtMs, Date.now()) });
312
+ }
313
+ yield persist(updated);
314
+ if (armReminderClocks) {
315
+ armReminder(updated);
316
+ }
317
+ n += 1;
318
+ }
319
+ return n;
320
+ });
321
+ },
322
+ applyReminderOutcome,
150
323
  startClocks() {
151
- if (!armReminderClocks) {
152
- return;
153
- }
154
324
  for (const record of reminders.values()) {
155
- if (record.status === "armed") {
325
+ if (record.status === "firing") {
326
+ // Restart hydration: treat interrupted fires as armed for retry
327
+ // even when clocks are not armed (status must still recover).
328
+ const restored = Object.assign(Object.assign({}, record), { status: "armed", updatedAt: new Date().toISOString() });
329
+ void persist(restored).then(() => {
330
+ if (armReminderClocks) {
331
+ armReminder(restored);
332
+ }
333
+ });
334
+ continue;
335
+ }
336
+ if (armReminderClocks && record.status === "armed") {
156
337
  armReminder(record);
157
338
  }
158
339
  }
@@ -162,6 +343,7 @@ export function createLocalControlPlane(opts) {
162
343
  for (const id of [...reminderTimers.keys()]) {
163
344
  clearReminderTimer(id);
164
345
  }
346
+ inflight.clear();
165
347
  if (scmRelay !== undefined) {
166
348
  void scmRelay.stop();
167
349
  scmRelay = undefined;
@@ -0,0 +1,187 @@
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
+ import type { JsonObject, ReminderBusyPolicy, ReminderScheduleKind } from "../types.js";
13
+ import type { ReminderRecord } from "./reminder-store.js";
14
+ /** Wire / persistence version for this control-plane protocol. */
15
+ export declare const REMINDER_CP_PROTOCOL_VERSION: 1;
16
+ export type ReminderCpProtocolVersion = typeof REMINDER_CP_PROTOCOL_VERSION;
17
+ /**
18
+ * Normalized raw schedule for create intents (JSON-safe).
19
+ * CP resolves this into nextFireAtMs / intervalMs — guests must not send
20
+ * authoritative fire times.
21
+ */
22
+ export type ReminderScheduleSpec = {
23
+ kind: "every";
24
+ every: string;
25
+ } | {
26
+ kind: "cron";
27
+ cron: string;
28
+ } | {
29
+ kind: "delay";
30
+ delay: string | number;
31
+ } | {
32
+ kind: "at";
33
+ at: string;
34
+ };
35
+ /**
36
+ * Guest → CP create intent. No id/status/fireCount/nextFireAt — CP assigns
37
+ * those after resolving {@link ReminderScheduleSpec}.
38
+ */
39
+ export interface ReminderCreateIntent {
40
+ protocolVersion: ReminderCpProtocolVersion;
41
+ purpose: string;
42
+ channelId: string;
43
+ continuationToken: string;
44
+ target?: Record<string, unknown>;
45
+ schedule: ReminderScheduleSpec;
46
+ prompt?: string;
47
+ handler?: string;
48
+ args?: JsonObject;
49
+ hasRunHandler: boolean;
50
+ hasUntilFn: boolean;
51
+ untilPrompt?: string;
52
+ busyPolicy: ReminderBusyPolicy;
53
+ }
54
+ /**
55
+ * Immutable fire descriptor issued after CP claim. Guest execution must not
56
+ * mutate authoritative status/fireCount; it returns {@link ReminderExecutionOutcome}.
57
+ */
58
+ export interface ReminderFireDescriptor {
59
+ protocolVersion: ReminderCpProtocolVersion;
60
+ id: string;
61
+ /** Fire index being executed (equals record.fireCount at claim time). */
62
+ fireCount: number;
63
+ /** CAS generation captured at claim; cancel/rekey bumps it. */
64
+ generation: number;
65
+ purpose: string;
66
+ channelId: string;
67
+ continuationToken: string;
68
+ target?: Record<string, unknown>;
69
+ scheduleKind: ReminderScheduleKind;
70
+ scheduleValue: string;
71
+ intervalMs?: number;
72
+ prompt?: string;
73
+ handler?: string;
74
+ args?: JsonObject;
75
+ hasRunHandler: boolean;
76
+ hasUntilFn: boolean;
77
+ untilPrompt?: string;
78
+ busyPolicy: ReminderBusyPolicy;
79
+ /** Due time that triggered this claim. */
80
+ nextFireAtMs: number;
81
+ claimedAtMs: number;
82
+ }
83
+ /** Claim token used to apply an outcome exactly once for (id, fireCount, generation). */
84
+ export interface ReminderFireClaim {
85
+ id: string;
86
+ fireCount: number;
87
+ generation: number;
88
+ }
89
+ /**
90
+ * Wire-JSON-safe guest execution outcome. Retry is explicit and keyed by
91
+ * (id, fireCount) — `"retry"` does not increment fireCount.
92
+ * `reschedule.at` is an ISO-8601 string (never a Date).
93
+ */
94
+ export type ReminderExecutionOutcome = {
95
+ kind: "delivered";
96
+ /** Optional one-shot reschedule override (same as handler reschedule). */
97
+ reschedule?: ReminderWireReschedule;
98
+ } | {
99
+ kind: "skipped";
100
+ reschedule?: ReminderWireReschedule;
101
+ } | {
102
+ kind: "stop";
103
+ reason: string;
104
+ } | {
105
+ /** Re-arm the same fireCount after an explicit, idempotent retry. */
106
+ kind: "retry";
107
+ delayMs?: number;
108
+ reason?: string;
109
+ };
110
+ /** JSON-safe reschedule override for outcomes / hosted wire. */
111
+ export interface ReminderWireReschedule {
112
+ delay?: string | number;
113
+ /** ISO-8601 timestamp — never a Date instance. */
114
+ at?: string;
115
+ }
116
+ export type ReminderOutcomeApplyResult = {
117
+ applied: true;
118
+ record: ReminderRecord;
119
+ } | {
120
+ applied: false;
121
+ reason: "missing" | "stale_generation" | "stale_fire_count" | "not_claimed" | "duplicate";
122
+ record?: ReminderRecord;
123
+ };
124
+ /** Guest executor callback invoked by the CP after a successful claim. */
125
+ export type ReminderGuestExecutor = (descriptor: ReminderFireDescriptor) => Promise<ReminderExecutionOutcome>;
126
+ /**
127
+ * Control-plane reminder API (local disk today; Cursor-hosted HTTP later).
128
+ * Extends document CRUD with create-intent, cancel/rekey CAS, and outcome apply.
129
+ */
130
+ export interface ReminderControlPlane {
131
+ readonly protocolVersion: ReminderCpProtocolVersion;
132
+ createReminder(intent: ReminderCreateIntent): Promise<ReminderRecord>;
133
+ getReminder(id: string): Promise<ReminderRecord | undefined>;
134
+ listReminders(): Promise<ReminderRecord[]>;
135
+ cancelReminder(id: string, reason?: string): Promise<ReminderRecord | undefined>;
136
+ /**
137
+ * Re-key armed (and in-flight) reminders for a continuation rotation.
138
+ * Bumps generation so an in-flight outcome cannot land on the old token.
139
+ */
140
+ rekeyReminders(channelId: string, oldToken: string, newToken: string): Promise<number>;
141
+ applyReminderOutcome(claim: ReminderFireClaim, outcome: ReminderExecutionOutcome): Promise<ReminderOutcomeApplyResult>;
142
+ }
143
+ export declare function newReminderId(): string;
144
+ export declare function recordGeneration(record: ReminderRecord): number;
145
+ /** Map a JSON-safe schedule spec into resolveSchedule create fields. */
146
+ export declare function scheduleSpecToCreateFields(schedule: ReminderScheduleSpec): {
147
+ every?: string;
148
+ cron?: string;
149
+ delay?: string | number;
150
+ at?: string;
151
+ };
152
+ /**
153
+ * Build a wire-safe schedule spec from guest create input.
154
+ * Normalizes `at` Dates/numbers to ISO strings.
155
+ */
156
+ export declare function scheduleSpecFromCreateInput(input: {
157
+ every?: string;
158
+ cron?: string;
159
+ delay?: string | number;
160
+ at?: Date | string | number;
161
+ }): ReminderScheduleSpec;
162
+ /**
163
+ * Normalize a guest-facing `at` value to an ISO string for the wire intent.
164
+ */
165
+ export declare function normalizeScheduleAt(at: Date | string | number): string;
166
+ /** Normalize handler reschedule into a wire-JSON-safe shape. */
167
+ export declare function toWireReschedule(reschedule: {
168
+ delay?: string | number;
169
+ at?: Date | string;
170
+ }): ReminderWireReschedule;
171
+ /**
172
+ * Build a durable record from a create intent. CP owns schedule resolution
173
+ * via {@link resolveSchedule}; pass `nowMs` for deterministic tests.
174
+ */
175
+ export declare function intentToNewRecord(intent: ReminderCreateIntent, nowMs?: number): ReminderRecord;
176
+ export declare function toFireDescriptor(record: ReminderRecord, claimedAtMs: number): ReminderFireDescriptor;
177
+ export declare function claimFromDescriptor(descriptor: ReminderFireDescriptor): ReminderFireClaim;
178
+ /** Next fire time from the record's recurring schedule (CP-owned). */
179
+ export declare function nextScheduleFireAtMs(record: ReminderRecord, afterMs: number): number;
180
+ export declare function isOneShotSchedule(kind: ReminderScheduleKind): boolean;
181
+ /**
182
+ * Pure CAS apply of a guest outcome onto a reminder record.
183
+ * Callers persist the returned record when `applied: true`.
184
+ * Requires `status === "firing"` — an armed record was never claimed.
185
+ */
186
+ export declare function applyOutcomeToRecord(record: ReminderRecord, claim: ReminderFireClaim, outcome: ReminderExecutionOutcome, nowMs?: number): ReminderOutcomeApplyResult;
187
+ //# sourceMappingURL=reminder-control-plane-protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reminder-control-plane-protocol.d.ts","sourceRoot":"","sources":["../../src/internal/reminder-control-plane-protocol.ts"],"names":[],"mappings":"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"}