@cursor/july 0.1.104 → 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.
- package/dist/bin/agent-serve.js +1 -8
- package/dist/channels/bitbucket/types.d.ts +2 -4
- package/dist/channels/bitbucket/types.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.d.ts.map +1 -1
- package/dist/channels/deployments/deployments-channel.js +1 -0
- package/dist/channels/deployments/types.d.ts +7 -8
- package/dist/channels/deployments/types.d.ts.map +1 -1
- package/dist/channels/github/types.d.ts +2 -4
- 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/types.d.ts +2 -3
- 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/docs/404.html +3 -3
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{app.DbMWUyGb.js → app.CtuckIiL.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DcNeTh9o.js → VPLocalSearchBox.Bkkhnu8K.js} +2 -2
- package/dist/docs/assets/chunks/{theme.DMjhY3gl.js → theme.DhpIe0Pa.js} +2 -2
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.js +9 -0
- package/dist/docs/assets/guides_grokbot-agents.md.DVqdhBKb.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/index.md.BW_6tOgR.js +5 -0
- package/dist/docs/assets/index.md.BW_6tOgR.lean.js +1 -0
- 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.CJNc_xYK.js → reference_cli.md.Dm67hd2D.js} +3 -2
- package/dist/docs/assets/{reference_extensions.md.DykHW3qL.js → reference_extensions.md.CGmMLblt.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/templates_grokbot-agents.md.c1s6Flp9.js +3 -0
- package/dist/docs/assets/templates_grokbot-agents.md.c1s6Flp9.lean.js +1 -0
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/concepts.html +5 -5
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +6 -6
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-agents.html +6 -6
- package/dist/docs/guides/convert-automation.html +6 -6
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/grokbot-agents.html +35 -0
- package/dist/docs/guides/grokbot-agents.md +88 -0
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- 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 +6 -6
- 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 +6 -1
- package/dist/docs/llms-full.txt +178 -30
- package/dist/docs/llms.txt +2 -0
- package/dist/docs/quickstart.html +6 -6
- package/dist/docs/reference/agent-config.html +6 -6
- 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 +10 -9
- package/dist/docs/reference/cli.md +7 -3
- package/dist/docs/reference/connections.html +6 -6
- 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 +5 -5
- package/dist/docs/skills/ab.html +6 -6
- package/dist/docs/skills/create-agent.html +6 -6
- package/dist/docs/skills/debug.html +5 -5
- package/dist/docs/skills/evals.html +6 -6
- package/dist/docs/skills/framework-map.html +5 -5
- package/dist/docs/skills/github.html +6 -6
- 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 +29 -0
- package/dist/docs/templates/grokbot-agents.md +48 -0
- 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 +5 -5
- package/dist/files.d.ts +7 -2
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +7 -2
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +23 -11
- 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-agent-template.d.ts +1 -1
- package/dist/internal/cursor-agent-template.d.ts.map +1 -1
- package/dist/internal/cursor-agent-template.js +1 -0
- package/dist/internal/deploy-client.d.ts +8 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +14 -0
- package/dist/internal/deploy-manifest.d.ts +3 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +15 -12
- 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-file-storage.d.ts +3 -1
- package/dist/internal/framework-file-storage.d.ts.map +1 -1
- package/dist/internal/framework-file-storage.js +3 -1
- 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.map +1 -1
- package/dist/internal/guest-network.js +2 -0
- package/dist/internal/hosted-delivery-protocol.d.ts +48 -0
- package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
- package/dist/internal/hosted-delivery-protocol.js +60 -0
- package/dist/internal/hosted-delivery.d.ts +17 -0
- package/dist/internal/hosted-delivery.d.ts.map +1 -1
- package/dist/internal/hosted-delivery.js +112 -19
- package/dist/internal/hosted-managed-automation.d.ts.map +1 -1
- package/dist/internal/hosted-managed-automation.js +27 -2
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +4 -1
- 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/reminder-control-plane-protocol.d.ts +187 -0
- package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -0
- package/dist/internal/reminder-control-plane-protocol.js +244 -0
- package/dist/internal/reminder-runner.d.ts +48 -6
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +336 -48
- 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/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 +12 -0
- package/dist/internal/sdk-runner.d.ts.map +1 -1
- package/dist/internal/sdk-runner.js +18 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +78 -30
- package/dist/internal/session-engine.d.ts +12 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +137 -52
- package/dist/internal/store-mirror.d.ts +2 -2
- package/dist/internal/store-mirror.js +2 -2
- package/dist/internal/tenant-slack/relay.d.ts +2 -0
- package/dist/internal/tenant-slack/relay.d.ts.map +1 -1
- package/dist/internal/tenant-slack/relay.js +18 -8
- package/dist/internal/workspace-sync.d.ts +31 -6
- package/dist/internal/workspace-sync.d.ts.map +1 -1
- package/dist/internal/workspace-sync.js +67 -12
- package/dist/internal/workspace.d.ts +9 -0
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +16 -0
- package/dist/playground/assets/index-Bq2HpEQB.js +67 -0
- package/dist/playground/assets/index-CZKKNlmb.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/types.d.ts +98 -28
- package/dist/types.d.ts.map +1 -1
- package/docs/README.md +6 -1
- package/docs/guides/grokbot-agents.md +93 -0
- package/docs/guides/opentelemetry.md +3 -4
- package/docs/reference/channels.md +11 -19
- package/docs/reference/cli.md +7 -3
- package/docs/reference/extensions.md +2 -0
- package/docs/templates/grokbot-agents.md +53 -0
- package/package.json +12 -3
- package/skills/otel/SKILL.md +3 -3
- package/src/bin/agent-serve.ts +1 -9
- package/src/channels/bitbucket/types.ts +2 -4
- package/src/channels/deployments/deployments-channel.ts +1 -0
- package/src/channels/deployments/types.ts +7 -8
- package/src/channels/github/types.ts +2 -4
- package/src/channels/gitlab/types.ts +2 -5
- package/src/channels/origin/types.ts +2 -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/files.ts +9 -2
- package/src/index.ts +6 -3
- package/src/internal/cli-deploy.ts +30 -12
- package/src/internal/cursor/backend-client.ts +90 -2
- package/src/internal/cursor-agent-template.ts +1 -0
- package/src/internal/deploy-client.ts +16 -0
- package/src/internal/deploy-manifest.ts +16 -11
- 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-file-storage.ts +3 -1
- package/src/internal/framework-storage-selection.ts +43 -6
- package/src/internal/guest-network.ts +3 -0
- package/src/internal/hosted-delivery-protocol.ts +96 -0
- package/src/internal/hosted-delivery.ts +207 -28
- package/src/internal/hosted-managed-automation.ts +49 -5
- package/src/internal/init-project.ts +4 -1
- package/src/internal/local-control-plane.ts +281 -22
- package/src/internal/reminder-control-plane-protocol.ts +499 -0
- package/src/internal/reminder-runner.ts +421 -48
- 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/scm/pr-url.ts +167 -20
- package/src/internal/sdk-runner.ts +19 -0
- package/src/internal/server.ts +85 -16
- package/src/internal/session-engine.ts +173 -56
- package/src/internal/store-mirror.ts +2 -2
- package/src/internal/tenant-slack/relay.ts +28 -4
- package/src/internal/workspace-sync.ts +84 -12
- package/src/internal/workspace.ts +20 -0
- package/src/reminders.ts +35 -4
- package/src/types.ts +104 -28
- package/templates/grokbot-agents/README.md +26 -0
- package/templates/grokbot-agents/agent/agent.ts +6 -0
- package/templates/grokbot-agents/agent/extensions/grokbot.ts +5 -0
- package/templates/grokbot-agents/agent/instructions.md +7 -0
- package/templates/grokbot-agents/evals/evals.config.ts +5 -0
- package/templates/grokbot-agents/evals/grokbot-agents.eval.ts +18 -0
- package/templates/grokbot-agents/init.json +18 -0
- package/templates/grokbot-agents/package.json +17 -0
- package/templates/grokbot-agents/tsconfig.json +12 -0
- package/dist/docs/assets/chunks/@localSearchIndexroot.Bqsulb9K.js +0 -1
- package/dist/docs/assets/index.md.D75GWhPl.js +0 -5
- package/dist/docs/assets/index.md.D75GWhPl.lean.js +0 -1
- package/dist/playground/assets/index-8YiozU89.js +0 -67
- package/dist/playground/assets/index-MbMxXg-y.css +0 -1
- /package/dist/docs/assets/{reference_cli.md.CJNc_xYK.lean.js → reference_cli.md.Dm67hd2D.lean.js} +0 -0
- /package/dist/docs/assets/{reference_extensions.md.DykHW3qL.lean.js → reference_extensions.md.CGmMLblt.lean.js} +0 -0
- /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.
|
|
@@ -17,10 +23,26 @@ import {
|
|
|
17
23
|
routeLocalIngressContinuation,
|
|
18
24
|
} from "./local-ingress.js";
|
|
19
25
|
import type { PlatformTimers } from "./platform-timers.js";
|
|
26
|
+
import {
|
|
27
|
+
applyOutcomeToRecord,
|
|
28
|
+
claimFromDescriptor,
|
|
29
|
+
intentToNewRecord,
|
|
30
|
+
REMINDER_CP_PROTOCOL_VERSION,
|
|
31
|
+
type ReminderControlPlane,
|
|
32
|
+
type ReminderCreateIntent,
|
|
33
|
+
type ReminderExecutionOutcome,
|
|
34
|
+
type ReminderFireClaim,
|
|
35
|
+
type ReminderGuestExecutor,
|
|
36
|
+
type ReminderOutcomeApplyResult,
|
|
37
|
+
recordGeneration,
|
|
38
|
+
toFireDescriptor,
|
|
39
|
+
} from "./reminder-control-plane-protocol.js";
|
|
20
40
|
import { MAX_TIMER_MS } from "./reminder-schedule.js";
|
|
21
41
|
import { type ReminderRecord, ReminderStore } from "./reminder-store.js";
|
|
22
42
|
|
|
23
|
-
export interface LocalControlPlane
|
|
43
|
+
export interface LocalControlPlane
|
|
44
|
+
extends PlatformTimers,
|
|
45
|
+
ReminderControlPlane {
|
|
24
46
|
/**
|
|
25
47
|
* Stamp a v2 mailbox from the channel selector + live payload. Same
|
|
26
48
|
* contract as hosted verified ingress. `/v1` is a no-op.
|
|
@@ -40,7 +62,16 @@ export interface LocalControlPlane extends PlatformTimers {
|
|
|
40
62
|
|
|
41
63
|
export async function createLocalControlPlane(opts: {
|
|
42
64
|
fireSchedule: (name: string) => Promise<unknown>;
|
|
43
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Legacy v1 path: CP calls this with (id, fireCount) and the runner mutates
|
|
67
|
+
* authoritative state. Prefer {@link executeReminder} for architecture v2.
|
|
68
|
+
*/
|
|
69
|
+
fireReminder?: (id: string, fireCount: number) => Promise<unknown>;
|
|
70
|
+
/**
|
|
71
|
+
* Protocol guest executor. When set, due timers claim once, invoke this
|
|
72
|
+
* with an immutable descriptor, and apply the returned outcome via CAS.
|
|
73
|
+
*/
|
|
74
|
+
executeReminder?: ReminderGuestExecutor;
|
|
44
75
|
/**
|
|
45
76
|
* Persist reminder documents under `<stateDir>/reminders`. Local serve
|
|
46
77
|
* passes `<stateRoot>/control-plane`.
|
|
@@ -50,6 +81,12 @@ export async function createLocalControlPlane(opts: {
|
|
|
50
81
|
armReminderClocks?: boolean;
|
|
51
82
|
logger?: (line: string) => void;
|
|
52
83
|
}): Promise<LocalControlPlane> {
|
|
84
|
+
if (opts.fireReminder === undefined && opts.executeReminder === undefined) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
"createLocalControlPlane requires fireReminder and/or executeReminder"
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
53
90
|
const logger = opts.logger ?? (() => {});
|
|
54
91
|
const armReminderClocks = opts.armReminderClocks !== false;
|
|
55
92
|
const store =
|
|
@@ -59,12 +96,22 @@ export async function createLocalControlPlane(opts: {
|
|
|
59
96
|
const schedules = new Map<string, CronExpression>();
|
|
60
97
|
const reminderTimers = new Map<string, NodeJS.Timeout>();
|
|
61
98
|
const reminders = new Map<string, ReminderRecord>(
|
|
62
|
-
((await store?.list()) ?? []).map((record) => [
|
|
99
|
+
((await store?.list()) ?? []).map((record) => [
|
|
100
|
+
record.id,
|
|
101
|
+
{ ...record, generation: record.generation ?? 0 },
|
|
102
|
+
])
|
|
63
103
|
);
|
|
104
|
+
/** In-flight claims keyed by reminder id (single-flight per id). */
|
|
105
|
+
const inflight = new Map<string, ReminderFireClaim>();
|
|
64
106
|
let scheduleTimer: NodeJS.Timeout | undefined;
|
|
65
107
|
let lastFiredMinute = -1;
|
|
66
108
|
let scmRelay: { start(): void; stop(): Promise<void> } | undefined;
|
|
67
109
|
|
|
110
|
+
async function persist(record: ReminderRecord): Promise<void> {
|
|
111
|
+
reminders.set(record.id, record);
|
|
112
|
+
await store?.save(record);
|
|
113
|
+
}
|
|
114
|
+
|
|
68
115
|
function armScheduleTicker(): void {
|
|
69
116
|
if (scheduleTimer !== undefined || schedules.size === 0) {
|
|
70
117
|
return;
|
|
@@ -115,23 +162,18 @@ export async function createLocalControlPlane(opts: {
|
|
|
115
162
|
|
|
116
163
|
function armReminder(record: ReminderRecord): void {
|
|
117
164
|
clearReminderTimer(record.id);
|
|
165
|
+
if (record.status !== "armed") {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
118
168
|
const delay = Math.min(
|
|
119
169
|
MAX_TIMER_MS,
|
|
120
170
|
Math.max(0, record.nextFireAtMs - Date.now())
|
|
121
171
|
);
|
|
122
172
|
const timer = setTimeout(() => {
|
|
123
173
|
reminderTimers.delete(record.id);
|
|
124
|
-
|
|
125
|
-
if (current === undefined || current.status !== "armed") {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
if (current.nextFireAtMs > Date.now() + 50) {
|
|
129
|
-
armReminder(current);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
void opts.fireReminder(current.id, current.fireCount).catch((error) => {
|
|
174
|
+
void onReminderDue(record.id).catch((error) => {
|
|
133
175
|
logger(
|
|
134
|
-
`[agent-sdk] reminder "${
|
|
176
|
+
`[agent-sdk] reminder "${record.id}" dispatch failed: ${
|
|
135
177
|
error instanceof Error ? error.message : String(error)
|
|
136
178
|
}`
|
|
137
179
|
);
|
|
@@ -141,58 +183,275 @@ export async function createLocalControlPlane(opts: {
|
|
|
141
183
|
reminderTimers.set(record.id, timer);
|
|
142
184
|
}
|
|
143
185
|
|
|
186
|
+
async function claimDue(id: string): Promise<ReminderRecord | undefined> {
|
|
187
|
+
const current = reminders.get(id);
|
|
188
|
+
if (current === undefined || current.status !== "armed") {
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
if (current.nextFireAtMs > Date.now() + 50) {
|
|
192
|
+
armReminder(current);
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
if (inflight.has(id)) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
const claimed: ReminderRecord = {
|
|
199
|
+
...current,
|
|
200
|
+
status: "firing",
|
|
201
|
+
generation: recordGeneration(current),
|
|
202
|
+
updatedAt: new Date().toISOString(),
|
|
203
|
+
};
|
|
204
|
+
await persist(claimed);
|
|
205
|
+
inflight.set(id, {
|
|
206
|
+
id: claimed.id,
|
|
207
|
+
fireCount: claimed.fireCount,
|
|
208
|
+
generation: recordGeneration(claimed),
|
|
209
|
+
});
|
|
210
|
+
return claimed;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function applyReminderOutcome(
|
|
214
|
+
claim: ReminderFireClaim,
|
|
215
|
+
outcome: ReminderExecutionOutcome
|
|
216
|
+
): Promise<ReminderOutcomeApplyResult> {
|
|
217
|
+
const current = reminders.get(claim.id);
|
|
218
|
+
if (current === undefined) {
|
|
219
|
+
inflight.delete(claim.id);
|
|
220
|
+
return { applied: false, reason: "missing" };
|
|
221
|
+
}
|
|
222
|
+
const result = applyOutcomeToRecord(current, claim, outcome);
|
|
223
|
+
inflight.delete(claim.id);
|
|
224
|
+
if (!result.applied) {
|
|
225
|
+
if (current.status === "armed" && armReminderClocks) {
|
|
226
|
+
armReminder(current);
|
|
227
|
+
}
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
await persist(result.record);
|
|
231
|
+
if (armReminderClocks && result.record.status === "armed") {
|
|
232
|
+
armReminder(result.record);
|
|
233
|
+
} else {
|
|
234
|
+
clearReminderTimer(result.record.id);
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function onReminderDue(id: string): Promise<void> {
|
|
240
|
+
// Protocol path: claim → guest execute → CAS apply.
|
|
241
|
+
if (opts.executeReminder !== undefined) {
|
|
242
|
+
const claimed = await claimDue(id);
|
|
243
|
+
if (claimed === undefined) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const descriptor = toFireDescriptor(claimed, Date.now());
|
|
247
|
+
let outcome: ReminderExecutionOutcome;
|
|
248
|
+
try {
|
|
249
|
+
outcome = await opts.executeReminder(descriptor);
|
|
250
|
+
} catch (error) {
|
|
251
|
+
logger(
|
|
252
|
+
`[agent-sdk] reminder "${id}" guest execution failed: ${
|
|
253
|
+
error instanceof Error ? error.message : String(error)
|
|
254
|
+
}`
|
|
255
|
+
);
|
|
256
|
+
outcome = {
|
|
257
|
+
kind: "retry",
|
|
258
|
+
delayMs: 1_000,
|
|
259
|
+
reason: "guest_error",
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const result = await applyReminderOutcome(
|
|
263
|
+
claimFromDescriptor(descriptor),
|
|
264
|
+
outcome
|
|
265
|
+
);
|
|
266
|
+
if (!result.applied) {
|
|
267
|
+
logger(
|
|
268
|
+
`[agent-sdk] reminder "${id}" outcome not applied: ${result.reason}`
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Legacy v1: do not claim/firing — runner mutates via putReminder after fire.
|
|
275
|
+
const current = reminders.get(id);
|
|
276
|
+
if (current === undefined || current.status !== "armed") {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (current.nextFireAtMs > Date.now() + 50) {
|
|
280
|
+
armReminder(current);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
await opts.fireReminder!(current.id, current.fireCount);
|
|
285
|
+
} catch (error) {
|
|
286
|
+
logger(
|
|
287
|
+
`[agent-sdk] reminder "${id}" dispatch failed: ${
|
|
288
|
+
error instanceof Error ? error.message : String(error)
|
|
289
|
+
}`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
144
294
|
return {
|
|
295
|
+
protocolVersion: REMINDER_CP_PROTOCOL_VERSION,
|
|
296
|
+
|
|
145
297
|
routeContinuation: routeLocalIngressContinuation,
|
|
298
|
+
|
|
146
299
|
attachScmEvents(relay) {
|
|
147
300
|
scmRelay = relay;
|
|
148
301
|
relay.start();
|
|
149
302
|
logger("[agent-sdk] cursor-events: control-plane ingest started");
|
|
150
303
|
},
|
|
304
|
+
|
|
151
305
|
async upsertSchedule(input) {
|
|
152
306
|
schedules.set(input.name, parseCron(input.cron));
|
|
153
307
|
armScheduleTicker();
|
|
154
308
|
},
|
|
309
|
+
|
|
155
310
|
async deleteSchedule(name) {
|
|
156
311
|
schedules.delete(name);
|
|
157
312
|
if (schedules.size === 0) {
|
|
158
313
|
stopScheduleTicker();
|
|
159
314
|
}
|
|
160
315
|
},
|
|
316
|
+
|
|
161
317
|
async putReminder(record) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
318
|
+
const normalized: ReminderRecord = {
|
|
319
|
+
...record,
|
|
320
|
+
generation: record.generation ?? 0,
|
|
321
|
+
};
|
|
322
|
+
await persist(normalized);
|
|
323
|
+
if (armReminderClocks && normalized.status === "armed") {
|
|
324
|
+
armReminder(normalized);
|
|
166
325
|
return;
|
|
167
326
|
}
|
|
168
|
-
clearReminderTimer(
|
|
327
|
+
clearReminderTimer(normalized.id);
|
|
328
|
+
if (normalized.status !== "firing") {
|
|
329
|
+
inflight.delete(normalized.id);
|
|
330
|
+
}
|
|
169
331
|
},
|
|
332
|
+
|
|
170
333
|
async getReminder(id) {
|
|
171
334
|
return reminders.get(id);
|
|
172
335
|
},
|
|
336
|
+
|
|
173
337
|
async listReminders() {
|
|
174
338
|
return [...reminders.values()];
|
|
175
339
|
},
|
|
340
|
+
|
|
176
341
|
async deleteReminder(id) {
|
|
177
342
|
clearReminderTimer(id);
|
|
343
|
+
inflight.delete(id);
|
|
178
344
|
reminders.delete(id);
|
|
179
345
|
await store?.delete(id);
|
|
180
346
|
},
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
347
|
+
|
|
348
|
+
async createReminder(intent: ReminderCreateIntent) {
|
|
349
|
+
if (intent.protocolVersion !== REMINDER_CP_PROTOCOL_VERSION) {
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Unsupported reminder CP protocol version ${String(intent.protocolVersion)}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
const record = intentToNewRecord(intent);
|
|
355
|
+
await persist(record);
|
|
356
|
+
if (armReminderClocks && record.status === "armed") {
|
|
357
|
+
armReminder(record);
|
|
358
|
+
}
|
|
359
|
+
return record;
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
async cancelReminder(id, reason = "explicit_cancel") {
|
|
363
|
+
const current = reminders.get(id);
|
|
364
|
+
if (current === undefined) {
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
367
|
+
if (
|
|
368
|
+
current.status === "completed" ||
|
|
369
|
+
current.status === "cancelled" ||
|
|
370
|
+
current.status === "disarmed"
|
|
371
|
+
) {
|
|
372
|
+
return current;
|
|
373
|
+
}
|
|
374
|
+
clearReminderTimer(id);
|
|
375
|
+
inflight.delete(id);
|
|
376
|
+
const updated: ReminderRecord = {
|
|
377
|
+
...current,
|
|
378
|
+
status: "cancelled",
|
|
379
|
+
disarmReason: reason,
|
|
380
|
+
generation: recordGeneration(current) + 1,
|
|
381
|
+
updatedAt: new Date().toISOString(),
|
|
382
|
+
};
|
|
383
|
+
await persist(updated);
|
|
384
|
+
return updated;
|
|
385
|
+
},
|
|
386
|
+
|
|
387
|
+
async rekeyReminders(channelId, oldToken, newToken) {
|
|
388
|
+
let n = 0;
|
|
389
|
+
for (const record of [...reminders.values()]) {
|
|
390
|
+
if (
|
|
391
|
+
record.channelId !== channelId ||
|
|
392
|
+
record.continuationToken !== oldToken
|
|
393
|
+
) {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (record.status !== "armed" && record.status !== "firing") {
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const wasInflight = inflight.has(record.id);
|
|
400
|
+
inflight.delete(record.id);
|
|
401
|
+
let updated: ReminderRecord = {
|
|
402
|
+
...record,
|
|
403
|
+
continuationToken: newToken,
|
|
404
|
+
status: "armed",
|
|
405
|
+
generation: recordGeneration(record) + 1,
|
|
406
|
+
updatedAt: new Date().toISOString(),
|
|
407
|
+
};
|
|
408
|
+
// Interrupted in-flight fire: retry the same fireCount soon.
|
|
409
|
+
if (wasInflight) {
|
|
410
|
+
updated = {
|
|
411
|
+
...updated,
|
|
412
|
+
nextFireAtMs: Math.min(updated.nextFireAtMs, Date.now()),
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
await persist(updated);
|
|
416
|
+
if (armReminderClocks) {
|
|
417
|
+
armReminder(updated);
|
|
418
|
+
}
|
|
419
|
+
n += 1;
|
|
184
420
|
}
|
|
421
|
+
return n;
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
applyReminderOutcome,
|
|
425
|
+
|
|
426
|
+
startClocks() {
|
|
185
427
|
for (const record of reminders.values()) {
|
|
186
|
-
if (record.status === "
|
|
428
|
+
if (record.status === "firing") {
|
|
429
|
+
// Restart hydration: treat interrupted fires as armed for retry
|
|
430
|
+
// even when clocks are not armed (status must still recover).
|
|
431
|
+
const restored: ReminderRecord = {
|
|
432
|
+
...record,
|
|
433
|
+
status: "armed",
|
|
434
|
+
updatedAt: new Date().toISOString(),
|
|
435
|
+
};
|
|
436
|
+
void persist(restored).then(() => {
|
|
437
|
+
if (armReminderClocks) {
|
|
438
|
+
armReminder(restored);
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (armReminderClocks && record.status === "armed") {
|
|
187
444
|
armReminder(record);
|
|
188
445
|
}
|
|
189
446
|
}
|
|
190
447
|
},
|
|
448
|
+
|
|
191
449
|
stop() {
|
|
192
450
|
stopScheduleTicker();
|
|
193
451
|
for (const id of [...reminderTimers.keys()]) {
|
|
194
452
|
clearReminderTimer(id);
|
|
195
453
|
}
|
|
454
|
+
inflight.clear();
|
|
196
455
|
if (scmRelay !== undefined) {
|
|
197
456
|
void scmRelay.stop();
|
|
198
457
|
scmRelay = undefined;
|