@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
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Per-session reminder runner: durable wake subscriptions with
3
- * prompt XOR run, every/cron/delay/at schedules, and until as
4
- * function XOR prompt.
3
+ * prompt XOR named handler XOR run, every/cron/delay/at schedules, and
4
+ * until as function XOR prompt.
5
5
  */
6
6
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
7
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
@@ -13,11 +13,15 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
13
13
  });
14
14
  };
15
15
  import { randomBytes } from "node:crypto";
16
+ import { NAME_PATTERN } from "./discovery/names.js";
16
17
  import { parseDuration } from "./duration.js";
18
+ import { REMINDER_CP_PROTOCOL_VERSION, scheduleSpecFromCreateInput, toWireReschedule, } from "./reminder-control-plane-protocol.js";
17
19
  import { defaultBusyPolicy, MAX_TIMER_MS, nextCronFireMs, ReminderValidationError, resolveSchedule, } from "./reminder-schedule.js";
18
20
  import { APP_AUTH } from "./schedule-runner.js";
19
21
  import { SessionBusyError } from "./session-engine.js";
20
22
  export { ReminderValidationError } from "./reminder-schedule.js";
23
+ /** Max UTF-8 byte length of `JSON.stringify(args)` on named-handler create. */
24
+ export const REMINDER_ARGS_MAX_BYTES = 32 * 1024;
21
25
  export class UnknownReminderError extends Error {
22
26
  constructor(reminderId, availableReminderIds) {
23
27
  super(`Unknown reminder "${reminderId}"`);
@@ -25,9 +29,46 @@ export class UnknownReminderError extends Error {
25
29
  this.name = "UnknownReminderError";
26
30
  }
27
31
  }
32
+ /**
33
+ * Server wiring for {@link ReminderRunnerOptions.rejectAnonymousHandlers}:
34
+ * architecture v2 always rejects anonymous run/until (local or hosted).
35
+ */
36
+ export function rejectAnonymousReminderHandlers(options) {
37
+ return options.architecture === "v2";
38
+ }
28
39
  function newReminderId() {
29
40
  return `rem_${randomBytes(8).toString("hex")}`;
30
41
  }
42
+ function isJsonObject(value) {
43
+ return (typeof value === "object" &&
44
+ value !== null &&
45
+ !Array.isArray(value) &&
46
+ Object.values(value).every(isJsonValue));
47
+ }
48
+ function isJsonValue(value) {
49
+ if (value === null ||
50
+ typeof value === "string" ||
51
+ typeof value === "boolean") {
52
+ return true;
53
+ }
54
+ if (typeof value === "number") {
55
+ return Number.isFinite(value);
56
+ }
57
+ if (Array.isArray(value)) {
58
+ return value.every(isJsonValue);
59
+ }
60
+ return isJsonObject(value);
61
+ }
62
+ /** Same stem rules as `agent/reminders/<name>.ts` discovery. */
63
+ function isValidReminderHandlerName(name) {
64
+ return NAME_PATTERN.test(name) && !name.includes("__");
65
+ }
66
+ function assertReminderArgsSize(args) {
67
+ const bytes = Buffer.byteLength(JSON.stringify(args), "utf8");
68
+ if (bytes > REMINDER_ARGS_MAX_BYTES) {
69
+ throw new ReminderValidationError(`reminders.create args exceed ${REMINDER_ARGS_MAX_BYTES} bytes (got ${bytes})`);
70
+ }
71
+ }
31
72
  function toInfo(record) {
32
73
  return {
33
74
  id: record.id,
@@ -38,9 +79,11 @@ function toInfo(record) {
38
79
  scheduleKind: record.scheduleKind,
39
80
  scheduleValue: record.scheduleValue,
40
81
  prompt: record.prompt,
82
+ handler: record.handler,
83
+ args: record.args,
41
84
  untilPrompt: record.untilPrompt,
42
85
  busyPolicy: record.busyPolicy,
43
- status: record.status,
86
+ status: record.status === "firing" ? "armed" : record.status,
44
87
  disarmReason: record.disarmReason,
45
88
  fireCount: record.fireCount,
46
89
  nextFireAt: new Date(record.nextFireAtMs).toISOString(),
@@ -69,17 +112,31 @@ function buildFirePrompt(record) {
69
112
  return lines.join("\n");
70
113
  }
71
114
  export class ReminderRunner {
72
- constructor(engine, logger = () => { }, platformTimers) {
115
+ constructor(engine, logger = () => { }, platformTimers, options = {}) {
116
+ var _a;
73
117
  this.engine = engine;
74
118
  this.logger = logger;
75
119
  this.platformTimers = platformTimers;
76
120
  this.records = new Map();
77
121
  this.handlers = new Map();
122
+ this.namedHandlers = new Map();
123
+ this.namedHandlerNames = new Map();
78
124
  this.timers = new Map();
79
125
  this.firing = new Set();
80
126
  /** Single-flight idle gates keyed by `channelId:continuationToken`. */
81
127
  this.idleGates = new Map();
82
128
  this.started = false;
129
+ this.rejectAnonymousHandlers = options.rejectAnonymousHandlers === true;
130
+ this.controlPlane = options.controlPlane;
131
+ this.architecture = options.architecture;
132
+ for (const reminder of (_a = options.namedHandlers) !== null && _a !== void 0 ? _a : []) {
133
+ this.namedHandlers.set(reminder.name, reminder.definition);
134
+ this.namedHandlerNames.set(reminder.definition, reminder.name);
135
+ }
136
+ }
137
+ /** True when create/cancel/rekey/fire go through the CP protocol. */
138
+ get usesControlPlaneProtocol() {
139
+ return this.controlPlane !== undefined && this.architecture === "v2";
83
140
  }
84
141
  /**
85
142
  * Load reminder documents from the control plane into memory. Does not
@@ -107,6 +164,13 @@ export class ReminderRunner {
107
164
  if (record.status !== "armed") {
108
165
  continue;
109
166
  }
167
+ if (record.handler !== undefined) {
168
+ if (this.namedHandlers.get(record.handler) === undefined) {
169
+ yield this.disarm(record, "handler_missing");
170
+ disarmedOrphan += 1;
171
+ }
172
+ continue;
173
+ }
110
174
  if ((record.hasRunHandler === true || record.hasUntilFn === true) &&
111
175
  this.handlers.get(record.id) === undefined) {
112
176
  yield this.disarm(record, "handler_lost_on_restart");
@@ -135,6 +199,16 @@ export class ReminderRunner {
135
199
  let restored = 0;
136
200
  let disarmedOrphan = 0;
137
201
  for (const record of armed) {
202
+ if (record.handler !== undefined) {
203
+ if (this.namedHandlers.get(record.handler) === undefined) {
204
+ yield this.disarm(record, "handler_missing");
205
+ disarmedOrphan += 1;
206
+ continue;
207
+ }
208
+ this.armTimer(record);
209
+ restored += 1;
210
+ continue;
211
+ }
138
212
  if ((record.hasRunHandler === true || record.hasUntilFn === true) &&
139
213
  this.handlers.get(record.id) === undefined) {
140
214
  yield this.disarm(record, "handler_lost_on_restart");
@@ -207,11 +281,13 @@ export class ReminderRunner {
207
281
  }
208
282
  create(input) {
209
283
  return __awaiter(this, void 0, void 0, function* () {
210
- var _a;
284
+ var _a, _b;
211
285
  const hasPrompt = input.prompt !== undefined;
212
286
  const hasRun = input.run !== undefined;
213
- if (hasPrompt === hasRun) {
214
- throw new ReminderValidationError("reminders.create requires exactly one of prompt or run");
287
+ const hasHandler = input.handler !== undefined;
288
+ const formCount = [hasPrompt, hasRun, hasHandler].filter(Boolean).length;
289
+ if (formCount !== 1) {
290
+ throw new ReminderValidationError("reminders.create requires exactly one of prompt, handler, or run");
215
291
  }
216
292
  if (input.continuationToken.trim() === "") {
217
293
  throw new ReminderValidationError("continuationToken is required");
@@ -222,41 +298,111 @@ export class ReminderRunner {
222
298
  if (input.purpose.trim() === "") {
223
299
  throw new ReminderValidationError("purpose is required");
224
300
  }
225
- const schedule = resolveSchedule(input);
226
- const busyPolicy = (_a = input.busyPolicy) !== null && _a !== void 0 ? _a : defaultBusyPolicy(schedule.kind);
227
- const now = new Date().toISOString();
228
- const id = newReminderId();
229
301
  const untilPrompt = typeof input.until === "string" ? input.until : undefined;
230
302
  const untilFn = typeof input.until === "function" ? input.until : undefined;
303
+ if (hasHandler && input.until !== undefined) {
304
+ throw new ReminderValidationError("reminders.create: until is not valid with handler; encode the stop condition in args and the handler body");
305
+ }
306
+ if (this.rejectAnonymousHandlers) {
307
+ if (hasRun) {
308
+ throw new ReminderValidationError("reminders.create: anonymous run handlers are not durable on architecture v2; author agent/reminders/<name>.ts and pass handler + args");
309
+ }
310
+ if (untilFn !== undefined) {
311
+ throw new ReminderValidationError("reminders.create: function until is not durable on architecture v2; use a string until prompt");
312
+ }
313
+ }
314
+ let handlerName;
315
+ let handlerArgs;
316
+ if (hasHandler) {
317
+ const handler = input.handler;
318
+ const name = typeof handler === "string"
319
+ ? handler.trim()
320
+ : this.namedHandlerNames.get(handler);
321
+ if (name === undefined) {
322
+ throw new ReminderValidationError("Unknown reminder handler definition (expected a default export discovered under agent/reminders/)");
323
+ }
324
+ if (name === "") {
325
+ throw new ReminderValidationError("handler name is required");
326
+ }
327
+ if (!isValidReminderHandlerName(name)) {
328
+ throw new ReminderValidationError(name.includes("__")
329
+ ? `Invalid reminder handler name "${name}": may not contain "__"`
330
+ : `Invalid reminder handler name "${name}": must match ${NAME_PATTERN}`);
331
+ }
332
+ if (this.namedHandlers.get(name) === undefined) {
333
+ throw new ReminderValidationError(`Unknown reminder handler "${name}" (expected agent/reminders/${name}.ts)`);
334
+ }
335
+ if (input.args !== undefined && !isJsonObject(input.args)) {
336
+ throw new ReminderValidationError("reminders.create args must be a JSON object");
337
+ }
338
+ handlerName = name;
339
+ handlerArgs = (_a = input.args) !== null && _a !== void 0 ? _a : {};
340
+ assertReminderArgsSize(handlerArgs);
341
+ }
342
+ else if (input.args !== undefined) {
343
+ throw new ReminderValidationError("reminders.create args is only valid with handler");
344
+ }
345
+ const schedule = resolveSchedule(input);
346
+ const busyPolicy = (_b = input.busyPolicy) !== null && _b !== void 0 ? _b : defaultBusyPolicy(schedule.kind);
231
347
  const hasRunHandler = input.run !== undefined;
232
348
  const hasUntilFn = untilFn !== undefined;
233
- const record = {
234
- version: 1,
235
- id,
236
- purpose: input.purpose,
237
- channelId: input.channelId,
238
- continuationToken: input.continuationToken,
239
- target: input.target,
240
- scheduleKind: schedule.kind,
241
- scheduleValue: schedule.value,
242
- intervalMs: schedule.intervalMs,
243
- prompt: input.prompt,
244
- hasRunHandler,
245
- hasUntilFn,
246
- untilPrompt,
247
- busyPolicy,
248
- status: "armed",
249
- fireCount: 0,
250
- nextFireAtMs: schedule.nextFireAtMs,
251
- createdAt: now,
252
- updatedAt: now,
253
- };
254
- yield this.persist(record);
255
- this.handlers.set(id, {
349
+ let record;
350
+ if (this.usesControlPlaneProtocol) {
351
+ const intent = {
352
+ protocolVersion: REMINDER_CP_PROTOCOL_VERSION,
353
+ purpose: input.purpose,
354
+ channelId: input.channelId,
355
+ continuationToken: input.continuationToken,
356
+ target: input.target,
357
+ // CP recalculates nextFireAtMs / intervalMs from the raw schedule.
358
+ schedule: scheduleSpecFromCreateInput(input),
359
+ prompt: input.prompt,
360
+ handler: handlerName,
361
+ args: handlerArgs,
362
+ hasRunHandler,
363
+ hasUntilFn,
364
+ untilPrompt,
365
+ busyPolicy,
366
+ };
367
+ record = yield this.controlPlane.createReminder(intent);
368
+ this.records.set(record.id, record);
369
+ }
370
+ else {
371
+ const now = new Date().toISOString();
372
+ const id = newReminderId();
373
+ record = {
374
+ version: 1,
375
+ id,
376
+ purpose: input.purpose,
377
+ channelId: input.channelId,
378
+ continuationToken: input.continuationToken,
379
+ target: input.target,
380
+ scheduleKind: schedule.kind,
381
+ scheduleValue: schedule.value,
382
+ intervalMs: schedule.intervalMs,
383
+ prompt: input.prompt,
384
+ handler: handlerName,
385
+ args: handlerArgs,
386
+ hasRunHandler,
387
+ hasUntilFn,
388
+ untilPrompt,
389
+ busyPolicy,
390
+ status: "armed",
391
+ fireCount: 0,
392
+ nextFireAtMs: schedule.nextFireAtMs,
393
+ generation: 0,
394
+ createdAt: now,
395
+ updatedAt: now,
396
+ };
397
+ yield this.persist(record);
398
+ }
399
+ this.handlers.set(record.id, {
256
400
  run: input.run,
257
401
  untilFn,
258
402
  });
259
- if (this.started && this.platformTimers === undefined) {
403
+ if (this.started &&
404
+ this.platformTimers === undefined &&
405
+ !this.usesControlPlaneProtocol) {
260
406
  this.armTimer(record);
261
407
  }
262
408
  return toInfo(record);
@@ -267,8 +413,11 @@ export class ReminderRunner {
267
413
  const all = yield this.allRecords();
268
414
  return all
269
415
  .filter((r) => {
270
- if ((filter === null || filter === void 0 ? void 0 : filter.status) !== undefined && r.status !== filter.status) {
271
- return false;
416
+ if ((filter === null || filter === void 0 ? void 0 : filter.status) !== undefined) {
417
+ const publicStatus = r.status === "firing" ? "armed" : r.status;
418
+ if (publicStatus !== filter.status) {
419
+ return false;
420
+ }
272
421
  }
273
422
  if ((filter === null || filter === void 0 ? void 0 : filter.purpose) !== undefined && r.purpose !== filter.purpose) {
274
423
  return false;
@@ -290,15 +439,27 @@ export class ReminderRunner {
290
439
  }
291
440
  cancel(id_1) {
292
441
  return __awaiter(this, arguments, void 0, function* (id, reason = "explicit_cancel") {
442
+ var _a;
443
+ if (this.usesControlPlaneProtocol) {
444
+ const updated = yield this.controlPlane.cancelReminder(id, reason);
445
+ if (updated === undefined) {
446
+ this.records.delete(id);
447
+ this.handlers.delete(id);
448
+ return undefined;
449
+ }
450
+ this.records.set(id, updated);
451
+ this.handlers.delete(id);
452
+ return toInfo(updated);
453
+ }
293
454
  const record = yield this.getRecord(id);
294
455
  if (record === undefined) {
295
456
  return undefined;
296
457
  }
297
- if (record.status !== "armed") {
458
+ if (record.status !== "armed" && record.status !== "firing") {
298
459
  return toInfo(record);
299
460
  }
300
461
  this.clearTimer(id);
301
- const updated = Object.assign(Object.assign({}, record), { status: "cancelled", disarmReason: reason, updatedAt: new Date().toISOString() });
462
+ const updated = Object.assign(Object.assign({}, record), { status: "cancelled", disarmReason: reason, generation: ((_a = record.generation) !== null && _a !== void 0 ? _a : 0) + 1, updatedAt: new Date().toISOString() });
302
463
  yield this.persist(updated);
303
464
  this.handlers.delete(id);
304
465
  return toInfo(updated);
@@ -326,14 +487,18 @@ export class ReminderRunner {
326
487
  */
327
488
  rekeyContinuation(channelId, oldToken, newToken) {
328
489
  return __awaiter(this, void 0, void 0, function* () {
329
- const armed = (yield this.allRecords()).filter((r) => r.status === "armed");
490
+ var _a;
491
+ if (this.usesControlPlaneProtocol) {
492
+ return this.controlPlane.rekeyReminders(channelId, oldToken, newToken);
493
+ }
494
+ const armed = (yield this.allRecords()).filter((r) => r.status === "armed" || r.status === "firing");
330
495
  let n = 0;
331
496
  for (const record of armed) {
332
497
  if (record.channelId !== channelId ||
333
498
  record.continuationToken !== oldToken) {
334
499
  continue;
335
500
  }
336
- yield this.persist(Object.assign(Object.assign({}, record), { continuationToken: newToken, updatedAt: new Date().toISOString() }));
501
+ yield this.persist(Object.assign(Object.assign({}, record), { continuationToken: newToken, generation: ((_a = record.generation) !== null && _a !== void 0 ? _a : 0) + 1, updatedAt: new Date().toISOString() }));
337
502
  n += 1;
338
503
  }
339
504
  return n;
@@ -359,6 +524,108 @@ export class ReminderRunner {
359
524
  return Object.assign({ reminderId }, result);
360
525
  });
361
526
  }
527
+ /**
528
+ * Architecture v2 / CP protocol guest entrypoint: execute an immutable fire
529
+ * descriptor and return an outcome. Does not mint ids or mutate
530
+ * authoritative status/fireCount — the control plane applies the outcome.
531
+ */
532
+ executeFire(descriptor) {
533
+ return __awaiter(this, void 0, void 0, function* () {
534
+ var _a;
535
+ const record = this.recordFromDescriptor(descriptor);
536
+ this.records.set(record.id, record);
537
+ const live = (_a = this.handlers.get(record.id)) !== null && _a !== void 0 ? _a : {};
538
+ if (record.handler !== undefined) {
539
+ if (this.resolveNamedHandler(record.handler) === undefined) {
540
+ return { kind: "stop", reason: "handler_missing" };
541
+ }
542
+ }
543
+ else if ((record.hasRunHandler === true || record.hasUntilFn === true) &&
544
+ live.run === undefined &&
545
+ live.untilFn === undefined) {
546
+ return { kind: "stop", reason: "handler_lost_on_restart" };
547
+ }
548
+ if (live.untilFn !== undefined) {
549
+ try {
550
+ const done = yield live.untilFn(this.buildUntilContext(record));
551
+ if (done) {
552
+ return { kind: "stop", reason: "until_fn" };
553
+ }
554
+ }
555
+ catch (error) {
556
+ this.logger(`[agent-sdk] reminder "${record.id}" until() failed: ${error instanceof Error ? error.message : String(error)}`);
557
+ }
558
+ }
559
+ if (yield this.isSessionBusy(record.channelId, record.continuationToken)) {
560
+ if (record.busyPolicy === "skipIfBusy") {
561
+ this.logger(`[agent-sdk] reminder "${record.id}" (${record.purpose}) skipped: session busy`);
562
+ return { kind: "skipped" };
563
+ }
564
+ yield this.whenSessionIdle(record.channelId, record.continuationToken);
565
+ }
566
+ let result;
567
+ if (record.handler !== undefined) {
568
+ const named = this.resolveNamedHandler(record.handler);
569
+ if (named === undefined) {
570
+ return { kind: "stop", reason: "handler_missing" };
571
+ }
572
+ result = yield named.run(this.buildFireContext(record));
573
+ }
574
+ else if (live.run !== undefined) {
575
+ result = yield live.run(this.buildFireContext(record));
576
+ }
577
+ else if (record.prompt !== undefined) {
578
+ result = yield this.deliverPrompt(record);
579
+ }
580
+ else {
581
+ return { kind: "stop", reason: "missing_handler" };
582
+ }
583
+ if (result.action === "stop") {
584
+ return { kind: "stop", reason: "run_stop" };
585
+ }
586
+ if (result.action === "skip") {
587
+ return {
588
+ kind: "skipped",
589
+ reschedule: result.reschedule !== undefined
590
+ ? toWireReschedule(result.reschedule)
591
+ : undefined,
592
+ };
593
+ }
594
+ return {
595
+ kind: "delivered",
596
+ reschedule: result.reschedule !== undefined
597
+ ? toWireReschedule(result.reschedule)
598
+ : undefined,
599
+ };
600
+ });
601
+ }
602
+ recordFromDescriptor(descriptor) {
603
+ const now = new Date().toISOString();
604
+ return {
605
+ version: 1,
606
+ id: descriptor.id,
607
+ purpose: descriptor.purpose,
608
+ channelId: descriptor.channelId,
609
+ continuationToken: descriptor.continuationToken,
610
+ target: descriptor.target,
611
+ scheduleKind: descriptor.scheduleKind,
612
+ scheduleValue: descriptor.scheduleValue,
613
+ intervalMs: descriptor.intervalMs,
614
+ prompt: descriptor.prompt,
615
+ handler: descriptor.handler,
616
+ args: descriptor.args,
617
+ hasRunHandler: descriptor.hasRunHandler,
618
+ hasUntilFn: descriptor.hasUntilFn,
619
+ untilPrompt: descriptor.untilPrompt,
620
+ busyPolicy: descriptor.busyPolicy,
621
+ status: "firing",
622
+ fireCount: descriptor.fireCount,
623
+ nextFireAtMs: descriptor.nextFireAtMs,
624
+ generation: descriptor.generation,
625
+ createdAt: now,
626
+ updatedAt: now,
627
+ };
628
+ }
362
629
  armTimer(record) {
363
630
  var _a;
364
631
  this.clearTimer(record.id);
@@ -401,6 +668,9 @@ export class ReminderRunner {
401
668
  this.timers.delete(id);
402
669
  }
403
670
  }
671
+ resolveNamedHandler(name) {
672
+ return this.namedHandlers.get(name);
673
+ }
404
674
  fire(id_1) {
405
675
  return __awaiter(this, arguments, void 0, function* (id, opts = {}) {
406
676
  var _a, _b;
@@ -420,7 +690,13 @@ export class ReminderRunner {
420
690
  return { outcome: "noop", reason: "not_due" };
421
691
  }
422
692
  const live = (_a = this.handlers.get(id)) !== null && _a !== void 0 ? _a : {};
423
- if ((record.hasRunHandler === true || record.hasUntilFn === true) &&
693
+ if (record.handler !== undefined) {
694
+ if (this.resolveNamedHandler(record.handler) === undefined) {
695
+ yield this.disarm(record, "handler_missing");
696
+ return { outcome: "noop", reason: "handler_missing" };
697
+ }
698
+ }
699
+ else if ((record.hasRunHandler === true || record.hasUntilFn === true) &&
424
700
  live.run === undefined &&
425
701
  live.untilFn === undefined) {
426
702
  yield this.disarm(record, "handler_lost_on_restart");
@@ -452,9 +728,16 @@ export class ReminderRunner {
452
728
  }
453
729
  }
454
730
  let result;
455
- if (live.run !== undefined) {
456
- const fireCtx = this.buildFireContext(record);
457
- result = yield live.run(fireCtx);
731
+ if (record.handler !== undefined) {
732
+ const named = this.resolveNamedHandler(record.handler);
733
+ if (named === undefined) {
734
+ yield this.disarm(record, "handler_missing");
735
+ return { outcome: "noop", reason: "handler_missing" };
736
+ }
737
+ result = yield named.run(this.buildFireContext(record));
738
+ }
739
+ else if (live.run !== undefined) {
740
+ result = yield live.run(this.buildFireContext(record));
458
741
  }
459
742
  else if (record.prompt !== undefined) {
460
743
  result = yield this.deliverPrompt(record);
@@ -485,7 +768,7 @@ export class ReminderRunner {
485
768
  return { outcome: "fired", reason: "skipped" };
486
769
  }
487
770
  yield this.afterFire(latest, "delivered", result.reschedule);
488
- this.logger(`[agent-sdk] reminder "${id}" (${record.purpose}) delivered`);
771
+ this.logger(`[agent-sdk] reminder "${id}" (${record.purpose}) fired`);
489
772
  return { outcome: "fired", reason: "delivered" };
490
773
  }
491
774
  finally {
@@ -534,7 +817,11 @@ export class ReminderRunner {
534
817
  return __awaiter(this, void 0, void 0, function* () {
535
818
  const auth = yield this.resolveFollowupAuth(record.channelId, record.continuationToken);
536
819
  if (auth === undefined) {
537
- yield this.disarm(record, "session_gone");
820
+ // Under CP protocol, return stop and let the CP apply/CAS; do not
821
+ // mutate authoritative status from the guest mid-fire.
822
+ if (!this.usesControlPlaneProtocol) {
823
+ yield this.disarm(record, "session_gone");
824
+ }
538
825
  return { action: "stop" };
539
826
  }
540
827
  return this.sendFollowup(record, buildFirePrompt(record), auth);
@@ -576,13 +863,14 @@ export class ReminderRunner {
576
863
  });
577
864
  }
578
865
  buildFireContext(record) {
579
- var _a;
866
+ var _a, _b;
580
867
  const self = this;
581
868
  return {
582
869
  id: record.id,
583
870
  purpose: record.purpose,
584
871
  fireCount: record.fireCount,
585
872
  target: Object.assign({ channelId: record.channelId, continuationToken: record.continuationToken }, ((_a = record.target) !== null && _a !== void 0 ? _a : {})),
873
+ args: (_b = record.args) !== null && _b !== void 0 ? _b : {},
586
874
  host: this.engine.host,
587
875
  mcp: this.engine.host.mcp,
588
876
  appAuth: APP_AUTH,
@@ -13,8 +13,11 @@ export interface ResolvedSchedule {
13
13
  intervalMs?: number;
14
14
  nextFireAtMs: number;
15
15
  }
16
- /** Resolve create-input schedule fields into a concrete next-fire plan. */
17
- export declare function resolveSchedule(input: ReminderCreateInput): ResolvedSchedule;
16
+ /**
17
+ * Resolve create-input schedule fields into a concrete next-fire plan.
18
+ * Pass `nowMs` for deterministic CP / test resolution; CP owns this call.
19
+ */
20
+ export declare function resolveSchedule(input: Pick<ReminderCreateInput, "every" | "cron" | "delay" | "at">, nowMs?: number): ResolvedSchedule;
18
21
  /** Next UTC fire strictly after `afterMs` for a 5-field cron. */
19
22
  export declare function nextCronFireMs(cronSource: string, afterMs: number): number;
20
23
  /** Default busy policy: recurring may skip; one-shots queue behind the turn. */
@@ -1 +1 @@
1
- {"version":3,"file":"reminder-schedule.d.ts","sourceRoot":"","sources":["../../src/internal/reminder-schedule.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7E,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,4EAA4E;AAC5E,eAAO,MAAM,YAAY,aAAgB,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,GAAG,gBAAgB,CA2D5E;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAU1E;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,oBAAoB,GACzB,YAAY,GAAG,iBAAiB,CAElC"}
1
+ {"version":3,"file":"reminder-schedule.d.ts","sourceRoot":"","sources":["../../src/internal/reminder-schedule.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAI7E,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,4EAA4E;AAC5E,eAAO,MAAM,YAAY,aAAgB,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,EACnE,KAAK,GAAE,MAAmB,GACzB,gBAAgB,CA0DlB;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAU1E;AAED,gFAAgF;AAChF,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,oBAAoB,GACzB,YAAY,GAAG,iBAAiB,CAElC"}
@@ -11,8 +11,11 @@ export class ReminderValidationError extends Error {
11
11
  }
12
12
  /** Node setTimeout overflows past ~2^31-1 ms (~24.8d); chunk long waits. */
13
13
  export const MAX_TIMER_MS = 2147483647;
14
- /** Resolve create-input schedule fields into a concrete next-fire plan. */
15
- export function resolveSchedule(input) {
14
+ /**
15
+ * Resolve create-input schedule fields into a concrete next-fire plan.
16
+ * Pass `nowMs` for deterministic CP / test resolution; CP owns this call.
17
+ */
18
+ export function resolveSchedule(input, nowMs = Date.now()) {
16
19
  const shapes = [
17
20
  input.every !== undefined,
18
21
  input.cron !== undefined,
@@ -22,7 +25,6 @@ export function resolveSchedule(input) {
22
25
  if (shapes.length !== 1) {
23
26
  throw new ReminderValidationError("reminders.create requires exactly one of every, cron, delay, or at");
24
27
  }
25
- const now = Date.now();
26
28
  if (input.every !== undefined) {
27
29
  const intervalMs = parseDuration(input.every);
28
30
  if (intervalMs <= 0) {
@@ -32,7 +34,7 @@ export function resolveSchedule(input) {
32
34
  kind: "every",
33
35
  value: String(input.every),
34
36
  intervalMs,
35
- nextFireAtMs: now + intervalMs,
37
+ nextFireAtMs: nowMs + intervalMs,
36
38
  };
37
39
  }
38
40
  if (input.cron !== undefined) {
@@ -40,7 +42,7 @@ export function resolveSchedule(input) {
40
42
  return {
41
43
  kind: "cron",
42
44
  value: input.cron,
43
- nextFireAtMs: nextCronFireMs(input.cron, now),
45
+ nextFireAtMs: nextCronFireMs(input.cron, nowMs),
44
46
  };
45
47
  }
46
48
  if (input.delay !== undefined) {
@@ -48,7 +50,7 @@ export function resolveSchedule(input) {
48
50
  return {
49
51
  kind: "delay",
50
52
  value: String(input.delay),
51
- nextFireAtMs: now + delayMs,
53
+ nextFireAtMs: nowMs + delayMs,
52
54
  };
53
55
  }
54
56
  const atMs = typeof input.at === "number"