@minpeter/pss-runtime 0.1.0-next.2 → 0.1.0-next.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +166 -78
- package/dist/agent-host-session-store.js +2 -4
- package/dist/agent-host-session-store.js.map +1 -1
- package/dist/agent-loop.js +2 -3
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent-namespace.js +3 -7
- package/dist/agent-namespace.js.map +1 -1
- package/dist/agent-options.d.ts +2 -8
- package/dist/agent-options.js +4 -4
- package/dist/agent-options.js.map +1 -1
- package/dist/agent-resume.js +2 -82
- package/dist/agent-resume.js.map +1 -1
- package/dist/agent-session-entry.d.ts +1 -1
- package/dist/agent.d.ts +4 -4
- package/dist/agent.js +16 -70
- package/dist/agent.js.map +1 -1
- package/dist/cloudflare/cloudflare-agent-context.d.ts +40 -0
- package/dist/cloudflare/cloudflare-agent-context.js +37 -0
- package/dist/cloudflare/cloudflare-agent-context.js.map +1 -0
- package/dist/cloudflare/cloudflare-alarm-budget.d.ts +18 -0
- package/dist/cloudflare/cloudflare-alarm-budget.js +77 -0
- package/dist/cloudflare/cloudflare-alarm-budget.js.map +1 -0
- package/dist/cloudflare/cloudflare-alarm-drainer.d.ts +45 -0
- package/dist/cloudflare/cloudflare-alarm-drainer.js +103 -0
- package/dist/cloudflare/cloudflare-alarm-drainer.js.map +1 -0
- package/dist/cloudflare/cloudflare-alarm-run-drain.d.ts +13 -0
- package/dist/cloudflare/cloudflare-alarm-run-drain.js +81 -0
- package/dist/cloudflare/cloudflare-alarm-run-drain.js.map +1 -0
- package/dist/cloudflare/cloudflare-alarm-work.js +110 -0
- package/dist/cloudflare/cloudflare-alarm-work.js.map +1 -0
- package/dist/cloudflare/cloudflare-checkpoint-store.js +39 -0
- package/dist/cloudflare/cloudflare-checkpoint-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-durable-object-fetch.d.ts +21 -0
- package/dist/cloudflare/cloudflare-durable-object-fetch.js +11 -0
- package/dist/cloudflare/cloudflare-durable-object-fetch.js.map +1 -0
- package/dist/cloudflare/cloudflare-event-store.js +33 -0
- package/dist/cloudflare/cloudflare-event-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-execution-session-store.js +40 -0
- package/dist/cloudflare/cloudflare-execution-session-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-execution-store.js +35 -0
- package/dist/cloudflare/cloudflare-execution-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-host.d.ts +61 -0
- package/dist/cloudflare/cloudflare-host.js +113 -0
- package/dist/cloudflare/cloudflare-host.js.map +1 -0
- package/dist/cloudflare/cloudflare-notification-store.js +59 -0
- package/dist/cloudflare/cloudflare-notification-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-run-store.js +81 -0
- package/dist/cloudflare/cloudflare-run-store.js.map +1 -0
- package/dist/cloudflare/cloudflare-store-utils.js +43 -0
- package/dist/cloudflare/cloudflare-store-utils.js.map +1 -0
- package/dist/cloudflare/durable-object-storage.d.ts +20 -0
- package/dist/cloudflare/durable-object-storage.js +76 -0
- package/dist/cloudflare/durable-object-storage.js.map +1 -0
- package/dist/cloudflare/index.d.ts +7 -0
- package/dist/cloudflare/index.js +6 -0
- package/dist/execution/capabilities.d.ts +40 -0
- package/dist/execution/host.d.ts +9 -0
- package/dist/execution/host.js +49 -1
- package/dist/execution/host.js.map +1 -1
- package/dist/execution/index.d.ts +3 -1
- package/dist/execution/index.js +2 -1
- package/dist/execution/memory.js +1 -1
- package/dist/execution/memory.js.map +1 -1
- package/dist/execution/types.d.ts +5 -10
- package/dist/index.d.ts +8 -4
- package/dist/index.js +6 -1
- package/dist/plugins.d.ts +25 -3
- package/dist/plugins.js +35 -6
- package/dist/plugins.js.map +1 -1
- package/dist/session/delegate-input.d.ts +9 -0
- package/dist/session/delegate-input.js +16 -0
- package/dist/session/delegate-input.js.map +1 -0
- package/dist/session/events.d.ts +43 -25
- package/dist/session/events.js +41 -0
- package/dist/session/events.js.map +1 -0
- package/dist/session/input-meta-types.d.ts +10 -0
- package/dist/session/input-meta.d.ts +13 -0
- package/dist/session/input-meta.js +45 -0
- package/dist/session/input-meta.js.map +1 -0
- package/dist/session/input.d.ts +4 -0
- package/dist/session/mapping.js +4 -2
- package/dist/session/mapping.js.map +1 -1
- package/dist/session/runtime-input-emit.js +41 -0
- package/dist/session/runtime-input-emit.js.map +1 -0
- package/dist/session/runtime-input.js +5 -1
- package/dist/session/runtime-input.js.map +1 -1
- package/dist/session/session-events.js +20 -6
- package/dist/session/session-events.js.map +1 -1
- package/dist/session/session-notification.js +3 -2
- package/dist/session/session-notification.js.map +1 -1
- package/dist/session/session-runtime-drain.js +3 -9
- package/dist/session/session-runtime-drain.js.map +1 -1
- package/dist/session/session-turn-processor.js +7 -17
- package/dist/session/session-turn-processor.js.map +1 -1
- package/dist/session/session.js +11 -4
- package/dist/session/session.js.map +1 -1
- package/package.json +6 -1
- package/dist/agent-child-runs.js +0 -16
- package/dist/agent-child-runs.js.map +0 -1
- package/dist/agent-host-capabilities.js +0 -9
- package/dist/agent-host-capabilities.js.map +0 -1
- package/dist/agent-validation.js +0 -35
- package/dist/agent-validation.js.map +0 -1
- package/dist/child-session-cleanups.js +0 -61
- package/dist/child-session-cleanups.js.map +0 -1
- package/dist/execution/run.js +0 -55
- package/dist/execution/run.js.map +0 -1
- package/dist/subagent-background-child-run-state.js +0 -51
- package/dist/subagent-background-child-run-state.js.map +0 -1
- package/dist/subagent-background-child-run.js +0 -103
- package/dist/subagent-background-child-run.js.map +0 -1
- package/dist/subagent-background-in-process.js +0 -98
- package/dist/subagent-background-in-process.js.map +0 -1
- package/dist/subagent-background-notification-inbox.js +0 -106
- package/dist/subagent-background-notification-inbox.js.map +0 -1
- package/dist/subagent-background-notify.js +0 -136
- package/dist/subagent-background-notify.js.map +0 -1
- package/dist/subagent-background-resume-group.js +0 -99
- package/dist/subagent-background-resume-group.js.map +0 -1
- package/dist/subagent-background-runner.js +0 -115
- package/dist/subagent-background-runner.js.map +0 -1
- package/dist/subagent-background-schedule.js +0 -43
- package/dist/subagent-background-schedule.js.map +0 -1
- package/dist/subagent-child-run.js +0 -68
- package/dist/subagent-child-run.js.map +0 -1
- package/dist/subagent-job-cancel.js +0 -84
- package/dist/subagent-job-cancel.js.map +0 -1
- package/dist/subagent-job-observer.js +0 -19
- package/dist/subagent-job-observer.js.map +0 -1
- package/dist/subagent-job-output.js +0 -87
- package/dist/subagent-job-output.js.map +0 -1
- package/dist/subagent-job-state.js +0 -66
- package/dist/subagent-job-state.js.map +0 -1
- package/dist/subagent-jobs.js +0 -96
- package/dist/subagent-jobs.js.map +0 -1
- package/dist/subagent-prompt-schema.js +0 -114
- package/dist/subagent-prompt-schema.js.map +0 -1
- package/dist/subagent-run.js +0 -111
- package/dist/subagent-run.js.map +0 -1
- package/dist/subagents.js +0 -125
- package/dist/subagents.js.map +0 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AgentEvent } from "../session/events.js";
|
|
2
|
+
import { AgentRun } from "../session/run.js";
|
|
3
|
+
//#region src/cloudflare/cloudflare-alarm-run-drain.d.ts
|
|
4
|
+
interface CloudflareAgentRunDrainOptions {
|
|
5
|
+
readonly deadlineMs?: number;
|
|
6
|
+
readonly maxEvents?: number;
|
|
7
|
+
readonly onEvent?: (event: AgentEvent) => Promise<void> | void;
|
|
8
|
+
readonly startedAt?: number;
|
|
9
|
+
}
|
|
10
|
+
declare function drainAgentRun(run: AgentRun, options?: CloudflareAgentRunDrainOptions): Promise<AgentEvent[]>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { CloudflareAgentRunDrainOptions, drainAgentRun };
|
|
13
|
+
//# sourceMappingURL=cloudflare-alarm-run-drain.d.ts.map
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
//#region src/cloudflare/cloudflare-alarm-run-drain.ts
|
|
2
|
+
async function drainAgentRun(run, options = {}) {
|
|
3
|
+
return [...(await drainAgentRunWithBudget(run, {
|
|
4
|
+
deadlineMs: options.deadlineMs,
|
|
5
|
+
maxEvents: options.maxEvents,
|
|
6
|
+
onEvent: options.onEvent,
|
|
7
|
+
startedAt: options.startedAt
|
|
8
|
+
})).events];
|
|
9
|
+
}
|
|
10
|
+
async function drainAgentRunWithBudget(run, options = {}) {
|
|
11
|
+
const events = [];
|
|
12
|
+
let droppedEvents = 0;
|
|
13
|
+
const deadlineAt = options.deadlineMs === void 0 ? void 0 : (options.startedAt ?? Date.now()) + options.deadlineMs;
|
|
14
|
+
const iterator = run.events()[Symbol.asyncIterator]();
|
|
15
|
+
let stoppedReason;
|
|
16
|
+
try {
|
|
17
|
+
while (true) {
|
|
18
|
+
const eventBudgetFull = options.maxEvents !== void 0 && events.length >= options.maxEvents;
|
|
19
|
+
const nextEvent = await readNextEvent(iterator, deadlineAt);
|
|
20
|
+
if (nextEvent.deadlineExpired) {
|
|
21
|
+
stoppedReason = "deadline";
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
if (nextEvent.result.done) break;
|
|
25
|
+
if (deadlineExpired(deadlineAt)) {
|
|
26
|
+
droppedEvents += 1;
|
|
27
|
+
stoppedReason = "deadline";
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
if (eventBudgetFull) {
|
|
31
|
+
droppedEvents += 1;
|
|
32
|
+
stoppedReason = "event-budget";
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
events.push(nextEvent.result.value);
|
|
36
|
+
await options.onEvent?.(nextEvent.result.value);
|
|
37
|
+
}
|
|
38
|
+
} finally {
|
|
39
|
+
if (stoppedReason) stopIterator(iterator);
|
|
40
|
+
}
|
|
41
|
+
return stoppedReason ? {
|
|
42
|
+
droppedEvents,
|
|
43
|
+
events,
|
|
44
|
+
stoppedReason
|
|
45
|
+
} : {
|
|
46
|
+
droppedEvents,
|
|
47
|
+
events
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function readNextEvent(iterator, deadlineAt) {
|
|
51
|
+
if (deadlineExpired(deadlineAt)) return { deadlineExpired: true };
|
|
52
|
+
if (deadlineAt === void 0) return {
|
|
53
|
+
deadlineExpired: false,
|
|
54
|
+
result: await iterator.next()
|
|
55
|
+
};
|
|
56
|
+
const remainingMs = Math.max(0, deadlineAt - Date.now());
|
|
57
|
+
let timeout;
|
|
58
|
+
const nextEvent = iterator.next().then((result) => ({
|
|
59
|
+
deadlineExpired: false,
|
|
60
|
+
result
|
|
61
|
+
}));
|
|
62
|
+
const deadline = new Promise((resolve) => {
|
|
63
|
+
timeout = setTimeout(() => resolve({ deadlineExpired: true }), remainingMs);
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
return await Promise.race([nextEvent, deadline]);
|
|
67
|
+
} finally {
|
|
68
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function deadlineExpired(deadlineAt) {
|
|
72
|
+
return deadlineAt !== void 0 && Date.now() >= deadlineAt;
|
|
73
|
+
}
|
|
74
|
+
function stopIterator(iterator) {
|
|
75
|
+
const returned = iterator.return?.();
|
|
76
|
+
if (returned) returned.catch(() => void 0);
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
export { drainAgentRun, drainAgentRunWithBudget };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=cloudflare-alarm-run-drain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-alarm-run-drain.js","names":[],"sources":["../../src/cloudflare/cloudflare-alarm-run-drain.ts"],"sourcesContent":["import type { AgentEvent, AgentRun } from \"../index\";\n\nexport type AgentRunDrainStopReason = \"deadline\" | \"event-budget\";\n\nexport interface CloudflareAgentRunDrainOptions {\n readonly deadlineMs?: number;\n readonly maxEvents?: number;\n readonly onEvent?: (event: AgentEvent) => Promise<void> | void;\n readonly startedAt?: number;\n}\n\nexport interface AgentRunDrainResult {\n readonly droppedEvents: number;\n readonly events: readonly AgentEvent[];\n readonly stoppedReason?: AgentRunDrainStopReason;\n}\n\nexport async function drainAgentRun(\n run: AgentRun,\n options: CloudflareAgentRunDrainOptions = {}\n): Promise<AgentEvent[]> {\n return [\n ...(\n await drainAgentRunWithBudget(run, {\n deadlineMs: options.deadlineMs,\n maxEvents: options.maxEvents,\n onEvent: options.onEvent,\n startedAt: options.startedAt,\n })\n ).events,\n ];\n}\n\nexport async function drainAgentRunWithBudget(\n run: AgentRun,\n options: CloudflareAgentRunDrainOptions = {}\n): Promise<AgentRunDrainResult> {\n const events: AgentEvent[] = [];\n let droppedEvents = 0;\n const deadlineAt =\n options.deadlineMs === undefined\n ? undefined\n : (options.startedAt ?? Date.now()) + options.deadlineMs;\n const iterator = run.events()[Symbol.asyncIterator]();\n let stoppedReason: AgentRunDrainStopReason | undefined;\n\n try {\n while (true) {\n const eventBudgetFull =\n options.maxEvents !== undefined && events.length >= options.maxEvents;\n const nextEvent = await readNextEvent(iterator, deadlineAt);\n if (nextEvent.deadlineExpired) {\n stoppedReason = \"deadline\";\n break;\n }\n if (nextEvent.result.done) {\n break;\n }\n if (deadlineExpired(deadlineAt)) {\n droppedEvents += 1;\n stoppedReason = \"deadline\";\n break;\n }\n if (eventBudgetFull) {\n droppedEvents += 1;\n stoppedReason = \"event-budget\";\n break;\n }\n events.push(nextEvent.result.value);\n await options.onEvent?.(nextEvent.result.value);\n }\n } finally {\n if (stoppedReason) {\n stopIterator(iterator);\n }\n }\n\n return stoppedReason\n ? { droppedEvents, events, stoppedReason }\n : { droppedEvents, events };\n}\n\ntype NextEventResult =\n | {\n readonly deadlineExpired: false;\n readonly result: IteratorResult<AgentEvent>;\n }\n | { readonly deadlineExpired: true };\n\nasync function readNextEvent(\n iterator: AsyncIterator<AgentEvent>,\n deadlineAt: number | undefined\n): Promise<NextEventResult> {\n if (deadlineExpired(deadlineAt)) {\n return { deadlineExpired: true };\n }\n if (deadlineAt === undefined) {\n return { deadlineExpired: false, result: await iterator.next() };\n }\n\n const remainingMs = Math.max(0, deadlineAt - Date.now());\n let timeout: ReturnType<typeof setTimeout> | undefined;\n const nextEvent = iterator.next().then(\n (result) =>\n ({\n deadlineExpired: false,\n result,\n }) satisfies NextEventResult\n );\n const deadline = new Promise<NextEventResult>((resolve) => {\n timeout = setTimeout(() => resolve({ deadlineExpired: true }), remainingMs);\n });\n\n try {\n return await Promise.race([nextEvent, deadline]);\n } finally {\n if (timeout !== undefined) {\n clearTimeout(timeout);\n }\n }\n}\n\nfunction deadlineExpired(deadlineAt: number | undefined): boolean {\n return deadlineAt !== undefined && Date.now() >= deadlineAt;\n}\n\nfunction stopIterator(iterator: AsyncIterator<AgentEvent>): void {\n const returned = iterator.return?.();\n if (returned) {\n returned.catch(() => undefined);\n }\n}\n"],"mappings":";AAiBA,eAAsB,cACpB,KACA,UAA0C,CAAC,GACpB;CACvB,OAAO,CACL,IACE,MAAM,wBAAwB,KAAK;EACjC,YAAY,QAAQ;EACpB,WAAW,QAAQ;EACnB,SAAS,QAAQ;EACjB,WAAW,QAAQ;CACrB,CAAC,GACD,MACJ;AACF;AAEA,eAAsB,wBACpB,KACA,UAA0C,CAAC,GACb;CAC9B,MAAM,SAAuB,CAAC;CAC9B,IAAI,gBAAgB;CACpB,MAAM,aACJ,QAAQ,eAAe,KAAA,IACnB,KAAA,KACC,QAAQ,aAAa,KAAK,IAAI,KAAK,QAAQ;CAClD,MAAM,WAAW,IAAI,OAAO,EAAE,OAAO,eAAe;CACpD,IAAI;CAEJ,IAAI;EACF,OAAO,MAAM;GACX,MAAM,kBACJ,QAAQ,cAAc,KAAA,KAAa,OAAO,UAAU,QAAQ;GAC9D,MAAM,YAAY,MAAM,cAAc,UAAU,UAAU;GAC1D,IAAI,UAAU,iBAAiB;IAC7B,gBAAgB;IAChB;GACF;GACA,IAAI,UAAU,OAAO,MACnB;GAEF,IAAI,gBAAgB,UAAU,GAAG;IAC/B,iBAAiB;IACjB,gBAAgB;IAChB;GACF;GACA,IAAI,iBAAiB;IACnB,iBAAiB;IACjB,gBAAgB;IAChB;GACF;GACA,OAAO,KAAK,UAAU,OAAO,KAAK;GAClC,MAAM,QAAQ,UAAU,UAAU,OAAO,KAAK;EAChD;CACF,UAAU;EACR,IAAI,eACF,aAAa,QAAQ;CAEzB;CAEA,OAAO,gBACH;EAAE;EAAe;EAAQ;CAAc,IACvC;EAAE;EAAe;CAAO;AAC9B;AASA,eAAe,cACb,UACA,YAC0B;CAC1B,IAAI,gBAAgB,UAAU,GAC5B,OAAO,EAAE,iBAAiB,KAAK;CAEjC,IAAI,eAAe,KAAA,GACjB,OAAO;EAAE,iBAAiB;EAAO,QAAQ,MAAM,SAAS,KAAK;CAAE;CAGjE,MAAM,cAAc,KAAK,IAAI,GAAG,aAAa,KAAK,IAAI,CAAC;CACvD,IAAI;CACJ,MAAM,YAAY,SAAS,KAAK,EAAE,MAC/B,YACE;EACC,iBAAiB;EACjB;CACF,EACJ;CACA,MAAM,WAAW,IAAI,SAA0B,YAAY;EACzD,UAAU,iBAAiB,QAAQ,EAAE,iBAAiB,KAAK,CAAC,GAAG,WAAW;CAC5E,CAAC;CAED,IAAI;EACF,OAAO,MAAM,QAAQ,KAAK,CAAC,WAAW,QAAQ,CAAC;CACjD,UAAU;EACR,IAAI,YAAY,KAAA,GACd,aAAa,OAAO;CAExB;AACF;AAEA,SAAS,gBAAgB,YAAyC;CAChE,OAAO,eAAe,KAAA,KAAa,KAAK,IAAI,KAAK;AACnD;AAEA,SAAS,aAAa,UAA2C;CAC/D,MAAM,WAAW,SAAS,SAAS;CACnC,IAAI,UACF,SAAS,YAAY,KAAA,CAAS;AAElC"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { eventSlotsRemaining } from "./cloudflare-alarm-budget.js";
|
|
2
|
+
import { drainAgentRunWithBudget } from "./cloudflare-alarm-run-drain.js";
|
|
3
|
+
import { ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareDurableObjectHost } from "./cloudflare-host.js";
|
|
4
|
+
//#region src/cloudflare/cloudflare-alarm-work.ts
|
|
5
|
+
async function resumeScheduledRun({ agent, budget, prefix, runId, state, storage }) {
|
|
6
|
+
try {
|
|
7
|
+
const run = await agent.resume(runId);
|
|
8
|
+
if (!run) {
|
|
9
|
+
if (await shouldRetryScheduledRun(storage, prefix, runId)) {
|
|
10
|
+
state.failedRuns.push({
|
|
11
|
+
error: "Run was not claimable during this alarm.",
|
|
12
|
+
id: runId
|
|
13
|
+
});
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
await ackScheduledCloudflareRun(storage, runId, { prefix });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
state.resumedRuns.push(runId);
|
|
20
|
+
appendRunDrainEvents(state, await drainAgentRunWithBudget(run, {
|
|
21
|
+
deadlineMs: budget.deadlineMs,
|
|
22
|
+
maxEvents: eventSlotsRemaining(state, budget),
|
|
23
|
+
startedAt: budget.startedAt
|
|
24
|
+
}));
|
|
25
|
+
if (shouldContinueRunDrain(state)) return;
|
|
26
|
+
await ackScheduledCloudflareRun(storage, runId, { prefix });
|
|
27
|
+
} catch (error) {
|
|
28
|
+
state.failedRuns.push({
|
|
29
|
+
error: describeError(error),
|
|
30
|
+
id: runId
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function resumeScheduledSessionPrompt({ agent, budget, prefix, prompt, state, storage }) {
|
|
35
|
+
try {
|
|
36
|
+
const runId = await scheduledSessionPromptRunId(storage, prefix, prompt);
|
|
37
|
+
if (!runId) {
|
|
38
|
+
state.failedSessionPrompts.push({
|
|
39
|
+
error: "Session prompt did not include or resolve to a run id.",
|
|
40
|
+
id: sessionPromptId(prompt)
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const run = await agent.resume(runId);
|
|
45
|
+
if (!run) {
|
|
46
|
+
if (await shouldRetryScheduledSessionPrompt(storage, prefix, prompt)) {
|
|
47
|
+
state.failedSessionPrompts.push({
|
|
48
|
+
error: "Session prompt was not claimable during this alarm.",
|
|
49
|
+
id: sessionPromptId(prompt)
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
await ackScheduledCloudflareSessionPrompt(storage, prompt, { prefix });
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
appendRunDrainEvents(state, await drainAgentRunWithBudget(run, {
|
|
57
|
+
deadlineMs: budget.deadlineMs,
|
|
58
|
+
maxEvents: eventSlotsRemaining(state, budget),
|
|
59
|
+
startedAt: budget.startedAt
|
|
60
|
+
}));
|
|
61
|
+
if (shouldContinueRunDrain(state)) return;
|
|
62
|
+
state.consumedSessionPrompts.push(sessionPromptId(prompt));
|
|
63
|
+
await ackScheduledCloudflareSessionPrompt(storage, prompt, { prefix });
|
|
64
|
+
} catch (error) {
|
|
65
|
+
state.failedSessionPrompts.push({
|
|
66
|
+
error: describeError(error),
|
|
67
|
+
id: sessionPromptId(prompt)
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function shouldRetryScheduledRun(storage, prefix, runId) {
|
|
72
|
+
const run = await createCloudflareDurableObjectHost({
|
|
73
|
+
prefix,
|
|
74
|
+
storage
|
|
75
|
+
}).store.runs.get(runId);
|
|
76
|
+
return run ? isRetryableRunStatus(run.status) : true;
|
|
77
|
+
}
|
|
78
|
+
async function shouldRetryScheduledSessionPrompt(storage, prefix, prompt) {
|
|
79
|
+
const runId = await scheduledSessionPromptRunId(storage, prefix, prompt);
|
|
80
|
+
return runId ? await shouldRetryScheduledRun(storage, prefix, runId) : prompt.idempotencyKey !== void 0;
|
|
81
|
+
}
|
|
82
|
+
async function scheduledSessionPromptRunId(storage, prefix, prompt) {
|
|
83
|
+
if (prompt.runId) return prompt.runId;
|
|
84
|
+
if (!prompt.idempotencyKey) return;
|
|
85
|
+
return (await createCloudflareDurableObjectHost({
|
|
86
|
+
prefix,
|
|
87
|
+
storage
|
|
88
|
+
}).store.notifications.getByIdempotencyKey(prompt.idempotencyKey))?.runId;
|
|
89
|
+
}
|
|
90
|
+
function sessionPromptId(prompt) {
|
|
91
|
+
return prompt.idempotencyKey ?? prompt.runId ?? "<missing-run-id>";
|
|
92
|
+
}
|
|
93
|
+
function isRetryableRunStatus(status) {
|
|
94
|
+
return status === "leased" || status === "queued" || status === "running" || status === "suspended";
|
|
95
|
+
}
|
|
96
|
+
function describeError(error) {
|
|
97
|
+
return error instanceof Error ? error.message : String(error);
|
|
98
|
+
}
|
|
99
|
+
function appendRunDrainEvents(state, result) {
|
|
100
|
+
state.events.push(...result.events);
|
|
101
|
+
state.droppedEvents += result.droppedEvents;
|
|
102
|
+
if (result.stoppedReason) state.reasons.add(result.stoppedReason);
|
|
103
|
+
}
|
|
104
|
+
function shouldContinueRunDrain(state) {
|
|
105
|
+
return state.reasons.has("deadline") || state.reasons.has("event-budget");
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
export { resumeScheduledRun, resumeScheduledSessionPrompt };
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=cloudflare-alarm-work.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-alarm-work.js","names":[],"sources":["../../src/cloudflare/cloudflare-alarm-work.ts"],"sourcesContent":["import type { RunStatus } from \"../execution\";\nimport {\n type AlarmDrainState,\n eventSlotsRemaining,\n type NormalizedAlarmDrainBudget,\n} from \"./cloudflare-alarm-budget\";\nimport type { CloudflareAlarmAgent } from \"./cloudflare-alarm-drainer\";\nimport {\n type AgentRunDrainResult,\n drainAgentRunWithBudget,\n} from \"./cloudflare-alarm-run-drain\";\nimport {\n ackScheduledCloudflareRun,\n ackScheduledCloudflareSessionPrompt,\n type CloudflareDurableObjectStorage,\n type CloudflareScheduledSessionPrompt,\n createCloudflareDurableObjectHost,\n} from \"./cloudflare-host\";\n\nexport async function resumeScheduledRun({\n agent,\n budget,\n prefix,\n runId,\n state,\n storage,\n}: {\n readonly agent: CloudflareAlarmAgent;\n readonly budget: NormalizedAlarmDrainBudget;\n readonly prefix: string;\n readonly runId: string;\n readonly state: AlarmDrainState;\n readonly storage: CloudflareDurableObjectStorage;\n}): Promise<void> {\n try {\n const run = await agent.resume(runId);\n if (!run) {\n if (await shouldRetryScheduledRun(storage, prefix, runId)) {\n state.failedRuns.push({\n error: \"Run was not claimable during this alarm.\",\n id: runId,\n });\n return;\n }\n await ackScheduledCloudflareRun(storage, runId, { prefix });\n return;\n }\n state.resumedRuns.push(runId);\n appendRunDrainEvents(\n state,\n await drainAgentRunWithBudget(run, {\n deadlineMs: budget.deadlineMs,\n maxEvents: eventSlotsRemaining(state, budget),\n startedAt: budget.startedAt,\n })\n );\n if (shouldContinueRunDrain(state)) {\n return;\n }\n await ackScheduledCloudflareRun(storage, runId, { prefix });\n } catch (error) {\n state.failedRuns.push({ error: describeError(error), id: runId });\n }\n}\n\nexport async function resumeScheduledSessionPrompt({\n agent,\n budget,\n prefix,\n prompt,\n state,\n storage,\n}: {\n readonly agent: CloudflareAlarmAgent;\n readonly budget: NormalizedAlarmDrainBudget;\n readonly prefix: string;\n readonly prompt: CloudflareScheduledSessionPrompt;\n readonly state: AlarmDrainState;\n readonly storage: CloudflareDurableObjectStorage;\n}): Promise<void> {\n try {\n const runId = await scheduledSessionPromptRunId(storage, prefix, prompt);\n if (!runId) {\n state.failedSessionPrompts.push({\n error: \"Session prompt did not include or resolve to a run id.\",\n id: sessionPromptId(prompt),\n });\n return;\n }\n\n const run = await agent.resume(runId);\n if (!run) {\n if (await shouldRetryScheduledSessionPrompt(storage, prefix, prompt)) {\n state.failedSessionPrompts.push({\n error: \"Session prompt was not claimable during this alarm.\",\n id: sessionPromptId(prompt),\n });\n return;\n }\n await ackScheduledCloudflareSessionPrompt(storage, prompt, { prefix });\n return;\n }\n appendRunDrainEvents(\n state,\n await drainAgentRunWithBudget(run, {\n deadlineMs: budget.deadlineMs,\n maxEvents: eventSlotsRemaining(state, budget),\n startedAt: budget.startedAt,\n })\n );\n if (shouldContinueRunDrain(state)) {\n return;\n }\n state.consumedSessionPrompts.push(sessionPromptId(prompt));\n await ackScheduledCloudflareSessionPrompt(storage, prompt, { prefix });\n } catch (error) {\n state.failedSessionPrompts.push({\n error: describeError(error),\n id: sessionPromptId(prompt),\n });\n }\n}\n\nasync function shouldRetryScheduledRun(\n storage: CloudflareDurableObjectStorage,\n prefix: string,\n runId: string\n): Promise<boolean> {\n const run = await createCloudflareDurableObjectHost({\n prefix,\n storage,\n }).store.runs.get(runId);\n return run ? isRetryableRunStatus(run.status) : true;\n}\n\nasync function shouldRetryScheduledSessionPrompt(\n storage: CloudflareDurableObjectStorage,\n prefix: string,\n prompt: CloudflareScheduledSessionPrompt\n): Promise<boolean> {\n const runId = await scheduledSessionPromptRunId(storage, prefix, prompt);\n return runId\n ? await shouldRetryScheduledRun(storage, prefix, runId)\n : prompt.idempotencyKey !== undefined;\n}\n\nasync function scheduledSessionPromptRunId(\n storage: CloudflareDurableObjectStorage,\n prefix: string,\n prompt: CloudflareScheduledSessionPrompt\n): Promise<string | undefined> {\n if (prompt.runId) {\n return prompt.runId;\n }\n if (!prompt.idempotencyKey) {\n return;\n }\n\n const notification = await createCloudflareDurableObjectHost({\n prefix,\n storage,\n }).store.notifications.getByIdempotencyKey(prompt.idempotencyKey);\n return notification?.runId;\n}\n\nfunction sessionPromptId(prompt: CloudflareScheduledSessionPrompt): string {\n return prompt.idempotencyKey ?? prompt.runId ?? \"<missing-run-id>\";\n}\n\nfunction isRetryableRunStatus(status: RunStatus | undefined): boolean {\n return (\n status === \"leased\" ||\n status === \"queued\" ||\n status === \"running\" ||\n status === \"suspended\"\n );\n}\n\nfunction describeError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction appendRunDrainEvents(\n state: AlarmDrainState,\n result: AgentRunDrainResult\n): void {\n state.events.push(...result.events);\n state.droppedEvents += result.droppedEvents;\n if (result.stoppedReason) {\n state.reasons.add(result.stoppedReason);\n }\n}\n\nfunction shouldContinueRunDrain(state: AlarmDrainState): boolean {\n return state.reasons.has(\"deadline\") || state.reasons.has(\"event-budget\");\n}\n"],"mappings":";;;;AAmBA,eAAsB,mBAAmB,EACvC,OACA,QACA,QACA,OACA,OACA,WAQgB;CAChB,IAAI;EACF,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK;EACpC,IAAI,CAAC,KAAK;GACR,IAAI,MAAM,wBAAwB,SAAS,QAAQ,KAAK,GAAG;IACzD,MAAM,WAAW,KAAK;KACpB,OAAO;KACP,IAAI;IACN,CAAC;IACD;GACF;GACA,MAAM,0BAA0B,SAAS,OAAO,EAAE,OAAO,CAAC;GAC1D;EACF;EACA,MAAM,YAAY,KAAK,KAAK;EAC5B,qBACE,OACA,MAAM,wBAAwB,KAAK;GACjC,YAAY,OAAO;GACnB,WAAW,oBAAoB,OAAO,MAAM;GAC5C,WAAW,OAAO;EACpB,CAAC,CACH;EACA,IAAI,uBAAuB,KAAK,GAC9B;EAEF,MAAM,0BAA0B,SAAS,OAAO,EAAE,OAAO,CAAC;CAC5D,SAAS,OAAO;EACd,MAAM,WAAW,KAAK;GAAE,OAAO,cAAc,KAAK;GAAG,IAAI;EAAM,CAAC;CAClE;AACF;AAEA,eAAsB,6BAA6B,EACjD,OACA,QACA,QACA,QACA,OACA,WAQgB;CAChB,IAAI;EACF,MAAM,QAAQ,MAAM,4BAA4B,SAAS,QAAQ,MAAM;EACvE,IAAI,CAAC,OAAO;GACV,MAAM,qBAAqB,KAAK;IAC9B,OAAO;IACP,IAAI,gBAAgB,MAAM;GAC5B,CAAC;GACD;EACF;EAEA,MAAM,MAAM,MAAM,MAAM,OAAO,KAAK;EACpC,IAAI,CAAC,KAAK;GACR,IAAI,MAAM,kCAAkC,SAAS,QAAQ,MAAM,GAAG;IACpE,MAAM,qBAAqB,KAAK;KAC9B,OAAO;KACP,IAAI,gBAAgB,MAAM;IAC5B,CAAC;IACD;GACF;GACA,MAAM,oCAAoC,SAAS,QAAQ,EAAE,OAAO,CAAC;GACrE;EACF;EACA,qBACE,OACA,MAAM,wBAAwB,KAAK;GACjC,YAAY,OAAO;GACnB,WAAW,oBAAoB,OAAO,MAAM;GAC5C,WAAW,OAAO;EACpB,CAAC,CACH;EACA,IAAI,uBAAuB,KAAK,GAC9B;EAEF,MAAM,uBAAuB,KAAK,gBAAgB,MAAM,CAAC;EACzD,MAAM,oCAAoC,SAAS,QAAQ,EAAE,OAAO,CAAC;CACvE,SAAS,OAAO;EACd,MAAM,qBAAqB,KAAK;GAC9B,OAAO,cAAc,KAAK;GAC1B,IAAI,gBAAgB,MAAM;EAC5B,CAAC;CACH;AACF;AAEA,eAAe,wBACb,SACA,QACA,OACkB;CAClB,MAAM,MAAM,MAAM,kCAAkC;EAClD;EACA;CACF,CAAC,EAAE,MAAM,KAAK,IAAI,KAAK;CACvB,OAAO,MAAM,qBAAqB,IAAI,MAAM,IAAI;AAClD;AAEA,eAAe,kCACb,SACA,QACA,QACkB;CAClB,MAAM,QAAQ,MAAM,4BAA4B,SAAS,QAAQ,MAAM;CACvE,OAAO,QACH,MAAM,wBAAwB,SAAS,QAAQ,KAAK,IACpD,OAAO,mBAAmB,KAAA;AAChC;AAEA,eAAe,4BACb,SACA,QACA,QAC6B;CAC7B,IAAI,OAAO,OACT,OAAO,OAAO;CAEhB,IAAI,CAAC,OAAO,gBACV;CAOF,QAAO,MAJoB,kCAAkC;EAC3D;EACA;CACF,CAAC,EAAE,MAAM,cAAc,oBAAoB,OAAO,cAAc,IAC3C;AACvB;AAEA,SAAS,gBAAgB,QAAkD;CACzE,OAAO,OAAO,kBAAkB,OAAO,SAAS;AAClD;AAEA,SAAS,qBAAqB,QAAwC;CACpE,OACE,WAAW,YACX,WAAW,YACX,WAAW,aACX,WAAW;AAEf;AAEA,SAAS,cAAc,OAAwB;CAC7C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC9D;AAEA,SAAS,qBACP,OACA,QACM;CACN,MAAM,OAAO,KAAK,GAAG,OAAO,MAAM;CAClC,MAAM,iBAAiB,OAAO;CAC9B,IAAI,OAAO,eACT,MAAM,QAAQ,IAAI,OAAO,aAAa;AAE1C;AAEA,SAAS,uBAAuB,OAAiC;CAC/D,OAAO,MAAM,QAAQ,IAAI,UAAU,KAAK,MAAM,QAAQ,IAAI,cAAc;AAC1E"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getRun, putRun, readList, storeKey, withTransaction } from "./cloudflare-store-utils.js";
|
|
2
|
+
//#region src/cloudflare/cloudflare-checkpoint-store.ts
|
|
3
|
+
var DurableObjectCheckpointStore = class {
|
|
4
|
+
#prefix;
|
|
5
|
+
#storage;
|
|
6
|
+
constructor(storage, prefix) {
|
|
7
|
+
this.#prefix = prefix;
|
|
8
|
+
this.#storage = storage;
|
|
9
|
+
}
|
|
10
|
+
async append(checkpoint, options) {
|
|
11
|
+
return await withTransaction(this.#storage, async (storage) => {
|
|
12
|
+
const run = await getRun(storage, this.#prefix, checkpoint.runId);
|
|
13
|
+
const currentVersion = run?.checkpointVersion ?? 0;
|
|
14
|
+
if (currentVersion !== options.expectedVersion) return {
|
|
15
|
+
currentVersion,
|
|
16
|
+
ok: false,
|
|
17
|
+
reason: "stale-version"
|
|
18
|
+
};
|
|
19
|
+
const checkpoints = await readList(storage, storeKey(this.#prefix, "checkpoints", checkpoint.runId));
|
|
20
|
+
checkpoints.push(structuredClone(checkpoint));
|
|
21
|
+
await storage.put(storeKey(this.#prefix, "checkpoints", checkpoint.runId), checkpoints);
|
|
22
|
+
if (run) await putRun(storage, this.#prefix, {
|
|
23
|
+
...run,
|
|
24
|
+
checkpointVersion: checkpoint.version
|
|
25
|
+
});
|
|
26
|
+
return {
|
|
27
|
+
ok: true,
|
|
28
|
+
version: checkpoint.version
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async latest(runId) {
|
|
33
|
+
return (await readList(this.#storage, storeKey(this.#prefix, "checkpoints", runId))).at(-1) ?? null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { DurableObjectCheckpointStore };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=cloudflare-checkpoint-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-checkpoint-store.js","names":["#prefix","#storage"],"sources":["../../src/cloudflare/cloudflare-checkpoint-store.ts"],"sourcesContent":["import type {\n CheckpointStore,\n CheckpointWriteResult,\n RunCheckpoint,\n} from \"../execution\";\nimport {\n getRun,\n putRun,\n readList,\n storeKey,\n withTransaction,\n} from \"./cloudflare-store-utils\";\nimport type { CloudflareDurableObjectStorage } from \"./durable-object-storage\";\n\nexport class DurableObjectCheckpointStore implements CheckpointStore {\n readonly #prefix: string;\n readonly #storage: CloudflareDurableObjectStorage;\n\n constructor(storage: CloudflareDurableObjectStorage, prefix: string) {\n this.#prefix = prefix;\n this.#storage = storage;\n }\n\n async append(\n checkpoint: RunCheckpoint,\n options: { readonly expectedVersion: number }\n ): Promise<CheckpointWriteResult> {\n return await withTransaction(this.#storage, async (storage) => {\n const run = await getRun(storage, this.#prefix, checkpoint.runId);\n const currentVersion = run?.checkpointVersion ?? 0;\n if (currentVersion !== options.expectedVersion) {\n return {\n currentVersion,\n ok: false,\n reason: \"stale-version\",\n };\n }\n\n const checkpoints = await readList<RunCheckpoint>(\n storage,\n storeKey(this.#prefix, \"checkpoints\", checkpoint.runId)\n );\n checkpoints.push(structuredClone(checkpoint));\n await storage.put(\n storeKey(this.#prefix, \"checkpoints\", checkpoint.runId),\n checkpoints\n );\n if (run) {\n await putRun(storage, this.#prefix, {\n ...run,\n checkpointVersion: checkpoint.version,\n });\n }\n return { ok: true, version: checkpoint.version };\n });\n }\n\n async latest(runId: string): Promise<RunCheckpoint | null> {\n const checkpoints = await readList<RunCheckpoint>(\n this.#storage,\n storeKey(this.#prefix, \"checkpoints\", runId)\n );\n return checkpoints.at(-1) ?? null;\n }\n}\n"],"mappings":";;AAcA,IAAa,+BAAb,MAAqE;CACnE;CACA;CAEA,YAAY,SAAyC,QAAgB;EACnE,KAAKA,UAAU;EACf,KAAKC,WAAW;CAClB;CAEA,MAAM,OACJ,YACA,SACgC;EAChC,OAAO,MAAM,gBAAgB,KAAKA,UAAU,OAAO,YAAY;GAC7D,MAAM,MAAM,MAAM,OAAO,SAAS,KAAKD,SAAS,WAAW,KAAK;GAChE,MAAM,iBAAiB,KAAK,qBAAqB;GACjD,IAAI,mBAAmB,QAAQ,iBAC7B,OAAO;IACL;IACA,IAAI;IACJ,QAAQ;GACV;GAGF,MAAM,cAAc,MAAM,SACxB,SACA,SAAS,KAAKA,SAAS,eAAe,WAAW,KAAK,CACxD;GACA,YAAY,KAAK,gBAAgB,UAAU,CAAC;GAC5C,MAAM,QAAQ,IACZ,SAAS,KAAKA,SAAS,eAAe,WAAW,KAAK,GACtD,WACF;GACA,IAAI,KACF,MAAM,OAAO,SAAS,KAAKA,SAAS;IAClC,GAAG;IACH,mBAAmB,WAAW;GAChC,CAAC;GAEH,OAAO;IAAE,IAAI;IAAM,SAAS,WAAW;GAAQ;EACjD,CAAC;CACH;CAEA,MAAM,OAAO,OAA8C;EAKzD,QAAO,MAJmB,SACxB,KAAKC,UACL,SAAS,KAAKD,SAAS,eAAe,KAAK,CAC7C,GACmB,GAAG,EAAE,KAAK;CAC/B;AACF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { CloudflareDurableObjectNamespace, CloudflareDurableObjectStub } from "./cloudflare-host.js";
|
|
2
|
+
|
|
3
|
+
//#region src/cloudflare/cloudflare-durable-object-fetch.d.ts
|
|
4
|
+
interface CloudflareDurableObjectStubOptions<Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub> {
|
|
5
|
+
readonly namespace?: CloudflareDurableObjectNamespace<Stub>;
|
|
6
|
+
readonly objectName: string;
|
|
7
|
+
}
|
|
8
|
+
interface CloudflareDurableObjectFetchOptions<Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub> extends CloudflareDurableObjectStubOptions<Stub> {
|
|
9
|
+
readonly request: Request;
|
|
10
|
+
}
|
|
11
|
+
declare function getCloudflareDurableObjectStub<Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub>({
|
|
12
|
+
namespace,
|
|
13
|
+
objectName
|
|
14
|
+
}: CloudflareDurableObjectStubOptions<Stub>): Stub | undefined;
|
|
15
|
+
declare function fetchCloudflareDurableObject<Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub>({
|
|
16
|
+
request,
|
|
17
|
+
...options
|
|
18
|
+
}: CloudflareDurableObjectFetchOptions<Stub>): Promise<Response | undefined>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { CloudflareDurableObjectFetchOptions, CloudflareDurableObjectStubOptions, fetchCloudflareDurableObject, getCloudflareDurableObjectStub };
|
|
21
|
+
//# sourceMappingURL=cloudflare-durable-object-fetch.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/cloudflare/cloudflare-durable-object-fetch.ts
|
|
2
|
+
function getCloudflareDurableObjectStub({ namespace, objectName }) {
|
|
3
|
+
return namespace?.get(namespace.idFromName(objectName));
|
|
4
|
+
}
|
|
5
|
+
async function fetchCloudflareDurableObject({ request, ...options }) {
|
|
6
|
+
return await getCloudflareDurableObjectStub(options)?.fetch(request);
|
|
7
|
+
}
|
|
8
|
+
//#endregion
|
|
9
|
+
export { fetchCloudflareDurableObject, getCloudflareDurableObjectStub };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=cloudflare-durable-object-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-durable-object-fetch.js","names":[],"sources":["../../src/cloudflare/cloudflare-durable-object-fetch.ts"],"sourcesContent":["import type {\n CloudflareDurableObjectNamespace,\n CloudflareDurableObjectStub,\n} from \"./cloudflare-host\";\n\nexport interface CloudflareDurableObjectStubOptions<\n Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub,\n> {\n readonly namespace?: CloudflareDurableObjectNamespace<Stub>;\n readonly objectName: string;\n}\n\nexport interface CloudflareDurableObjectFetchOptions<\n Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub,\n> extends CloudflareDurableObjectStubOptions<Stub> {\n readonly request: Request;\n}\n\nexport function getCloudflareDurableObjectStub<\n Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub,\n>({\n namespace,\n objectName,\n}: CloudflareDurableObjectStubOptions<Stub>): Stub | undefined {\n return namespace?.get(namespace.idFromName(objectName));\n}\n\nexport async function fetchCloudflareDurableObject<\n Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub,\n>({\n request,\n ...options\n}: CloudflareDurableObjectFetchOptions<Stub>): Promise<Response | undefined> {\n return await getCloudflareDurableObjectStub(options)?.fetch(request);\n}\n"],"mappings":";AAkBA,SAAgB,+BAEd,EACA,WACA,cAC6D;CAC7D,OAAO,WAAW,IAAI,UAAU,WAAW,UAAU,CAAC;AACxD;AAEA,eAAsB,6BAEpB,EACA,SACA,GAAG,WACwE;CAC3E,OAAO,MAAM,+BAA+B,OAAO,GAAG,MAAM,OAAO;AACrE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readList, storeKey, withTransaction } from "./cloudflare-store-utils.js";
|
|
2
|
+
//#region src/cloudflare/cloudflare-event-store.ts
|
|
3
|
+
var DurableObjectEventStore = class {
|
|
4
|
+
#prefix;
|
|
5
|
+
#storage;
|
|
6
|
+
constructor(storage, prefix) {
|
|
7
|
+
this.#prefix = prefix;
|
|
8
|
+
this.#storage = storage;
|
|
9
|
+
}
|
|
10
|
+
async append(runId, event) {
|
|
11
|
+
return await withTransaction(this.#storage, async (storage) => {
|
|
12
|
+
const events = await readList(storage, storeKey(this.#prefix, "events", runId));
|
|
13
|
+
const cursor = { offset: events.length + 1 };
|
|
14
|
+
events.push({
|
|
15
|
+
cursor,
|
|
16
|
+
event: structuredClone(event),
|
|
17
|
+
runId
|
|
18
|
+
});
|
|
19
|
+
await storage.put(storeKey(this.#prefix, "events", runId), events);
|
|
20
|
+
return cursor;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async *read(runId, cursor) {
|
|
24
|
+
await Promise.resolve();
|
|
25
|
+
const events = await readList(this.#storage, storeKey(this.#prefix, "events", runId));
|
|
26
|
+
const start = cursor?.offset ?? 0;
|
|
27
|
+
for (const event of events.slice(start)) yield event;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
//#endregion
|
|
31
|
+
export { DurableObjectEventStore };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=cloudflare-event-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-event-store.js","names":["#prefix","#storage"],"sources":["../../src/cloudflare/cloudflare-event-store.ts"],"sourcesContent":["import type { EventCursor, EventStore, StoredAgentEvent } from \"../execution\";\nimport type { AgentEvent } from \"../index\";\nimport { readList, storeKey, withTransaction } from \"./cloudflare-store-utils\";\nimport type { CloudflareDurableObjectStorage } from \"./durable-object-storage\";\n\nexport class DurableObjectEventStore implements EventStore {\n readonly #prefix: string;\n readonly #storage: CloudflareDurableObjectStorage;\n\n constructor(storage: CloudflareDurableObjectStorage, prefix: string) {\n this.#prefix = prefix;\n this.#storage = storage;\n }\n\n async append(runId: string, event: AgentEvent): Promise<EventCursor> {\n return await withTransaction(this.#storage, async (storage) => {\n const events = await readList<StoredAgentEvent>(\n storage,\n storeKey(this.#prefix, \"events\", runId)\n );\n const cursor = { offset: events.length + 1 };\n events.push({ cursor, event: structuredClone(event), runId });\n await storage.put(storeKey(this.#prefix, \"events\", runId), events);\n return cursor;\n });\n }\n\n async *read(\n runId: string,\n cursor?: EventCursor\n ): AsyncIterable<StoredAgentEvent> {\n await Promise.resolve();\n const events = await readList<StoredAgentEvent>(\n this.#storage,\n storeKey(this.#prefix, \"events\", runId)\n );\n const start = cursor?.offset ?? 0;\n for (const event of events.slice(start)) {\n yield event;\n }\n }\n}\n"],"mappings":";;AAKA,IAAa,0BAAb,MAA2D;CACzD;CACA;CAEA,YAAY,SAAyC,QAAgB;EACnE,KAAKA,UAAU;EACf,KAAKC,WAAW;CAClB;CAEA,MAAM,OAAO,OAAe,OAAyC;EACnE,OAAO,MAAM,gBAAgB,KAAKA,UAAU,OAAO,YAAY;GAC7D,MAAM,SAAS,MAAM,SACnB,SACA,SAAS,KAAKD,SAAS,UAAU,KAAK,CACxC;GACA,MAAM,SAAS,EAAE,QAAQ,OAAO,SAAS,EAAE;GAC3C,OAAO,KAAK;IAAE;IAAQ,OAAO,gBAAgB,KAAK;IAAG;GAAM,CAAC;GAC5D,MAAM,QAAQ,IAAI,SAAS,KAAKA,SAAS,UAAU,KAAK,GAAG,MAAM;GACjE,OAAO;EACT,CAAC;CACH;CAEA,OAAO,KACL,OACA,QACiC;EACjC,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,MAAM,SACnB,KAAKC,UACL,SAAS,KAAKD,SAAS,UAAU,KAAK,CACxC;EACA,MAAM,QAAQ,QAAQ,UAAU;EAChC,KAAK,MAAM,SAAS,OAAO,MAAM,KAAK,GACpC,MAAM;CAEV;AACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getSession, putSession, storeKey, withTransaction } from "./cloudflare-store-utils.js";
|
|
2
|
+
//#region src/cloudflare/cloudflare-execution-session-store.ts
|
|
3
|
+
var DurableObjectExecutionSessionStore = class {
|
|
4
|
+
#prefix;
|
|
5
|
+
#storage;
|
|
6
|
+
constructor(storage, prefix) {
|
|
7
|
+
this.#prefix = prefix;
|
|
8
|
+
this.#storage = storage;
|
|
9
|
+
}
|
|
10
|
+
async commit(sessionKey, next, options) {
|
|
11
|
+
return await withTransaction(this.#storage, async (storage) => {
|
|
12
|
+
const currentVersion = (await getSession(storage, this.#prefix, sessionKey))?.version ?? null;
|
|
13
|
+
if (options.expectedVersion !== currentVersion) return {
|
|
14
|
+
ok: false,
|
|
15
|
+
reason: "conflict"
|
|
16
|
+
};
|
|
17
|
+
const version = String((await storage.get(storeKey(this.#prefix, "session-version", sessionKey)) ?? 0) + 1);
|
|
18
|
+
await storage.put(storeKey(this.#prefix, "session-version", sessionKey), Number(version));
|
|
19
|
+
await putSession(storage, this.#prefix, sessionKey, {
|
|
20
|
+
state: next.state,
|
|
21
|
+
version
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
ok: true,
|
|
25
|
+
version
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async delete(sessionKey) {
|
|
30
|
+
await this.#storage.delete(storeKey(this.#prefix, "session", sessionKey));
|
|
31
|
+
await this.#storage.delete(storeKey(this.#prefix, "session-version", sessionKey));
|
|
32
|
+
}
|
|
33
|
+
async load(sessionKey) {
|
|
34
|
+
return await getSession(this.#storage, this.#prefix, sessionKey);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
//#endregion
|
|
38
|
+
export { DurableObjectExecutionSessionStore };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=cloudflare-execution-session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-execution-session-store.js","names":["#prefix","#storage"],"sources":["../../src/cloudflare/cloudflare-execution-session-store.ts"],"sourcesContent":["import type {\n CommitResult,\n ExpectedSessionVersion,\n SessionStore,\n SessionStoreCommit,\n StoredSession,\n} from \"../index\";\nimport {\n getSession,\n putSession,\n storeKey,\n withTransaction,\n} from \"./cloudflare-store-utils\";\nimport type { CloudflareDurableObjectStorage } from \"./durable-object-storage\";\n\nexport class DurableObjectExecutionSessionStore implements SessionStore {\n readonly #prefix: string;\n readonly #storage: CloudflareDurableObjectStorage;\n\n constructor(storage: CloudflareDurableObjectStorage, prefix: string) {\n this.#prefix = prefix;\n this.#storage = storage;\n }\n\n async commit(\n sessionKey: string,\n next: SessionStoreCommit,\n options: { readonly expectedVersion: ExpectedSessionVersion }\n ): Promise<CommitResult> {\n return await withTransaction(this.#storage, async (storage) => {\n const stored = await getSession(storage, this.#prefix, sessionKey);\n const currentVersion = stored?.version ?? null;\n if (options.expectedVersion !== currentVersion) {\n return { ok: false, reason: \"conflict\" };\n }\n\n const version = String(\n ((await storage.get<number>(\n storeKey(this.#prefix, \"session-version\", sessionKey)\n )) ?? 0) + 1\n );\n await storage.put(\n storeKey(this.#prefix, \"session-version\", sessionKey),\n Number(version)\n );\n await putSession(storage, this.#prefix, sessionKey, {\n state: next.state,\n version,\n });\n return { ok: true, version };\n });\n }\n\n async delete(sessionKey: string): Promise<void> {\n await this.#storage.delete(storeKey(this.#prefix, \"session\", sessionKey));\n await this.#storage.delete(\n storeKey(this.#prefix, \"session-version\", sessionKey)\n );\n }\n\n async load(sessionKey: string): Promise<StoredSession | null> {\n return await getSession(this.#storage, this.#prefix, sessionKey);\n }\n}\n"],"mappings":";;AAeA,IAAa,qCAAb,MAAwE;CACtE;CACA;CAEA,YAAY,SAAyC,QAAgB;EACnE,KAAKA,UAAU;EACf,KAAKC,WAAW;CAClB;CAEA,MAAM,OACJ,YACA,MACA,SACuB;EACvB,OAAO,MAAM,gBAAgB,KAAKA,UAAU,OAAO,YAAY;GAE7D,MAAM,kBAAiB,MADF,WAAW,SAAS,KAAKD,SAAS,UAAU,IAClC,WAAW;GAC1C,IAAI,QAAQ,oBAAoB,gBAC9B,OAAO;IAAE,IAAI;IAAO,QAAQ;GAAW;GAGzC,MAAM,UAAU,QACZ,MAAM,QAAQ,IACd,SAAS,KAAKA,SAAS,mBAAmB,UAAU,CACtD,KAAM,KAAK,CACb;GACA,MAAM,QAAQ,IACZ,SAAS,KAAKA,SAAS,mBAAmB,UAAU,GACpD,OAAO,OAAO,CAChB;GACA,MAAM,WAAW,SAAS,KAAKA,SAAS,YAAY;IAClD,OAAO,KAAK;IACZ;GACF,CAAC;GACD,OAAO;IAAE,IAAI;IAAM;GAAQ;EAC7B,CAAC;CACH;CAEA,MAAM,OAAO,YAAmC;EAC9C,MAAM,KAAKC,SAAS,OAAO,SAAS,KAAKD,SAAS,WAAW,UAAU,CAAC;EACxE,MAAM,KAAKC,SAAS,OAClB,SAAS,KAAKD,SAAS,mBAAmB,UAAU,CACtD;CACF;CAEA,MAAM,KAAK,YAAmD;EAC5D,OAAO,MAAM,WAAW,KAAKC,UAAU,KAAKD,SAAS,UAAU;CACjE;AACF"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { DurableObjectCheckpointStore } from "./cloudflare-checkpoint-store.js";
|
|
2
|
+
import { DurableObjectEventStore } from "./cloudflare-event-store.js";
|
|
3
|
+
import { DurableObjectExecutionSessionStore } from "./cloudflare-execution-session-store.js";
|
|
4
|
+
import { DurableObjectNotificationInbox } from "./cloudflare-notification-store.js";
|
|
5
|
+
import { DurableObjectRunStore } from "./cloudflare-run-store.js";
|
|
6
|
+
//#region src/cloudflare/cloudflare-execution-store.ts
|
|
7
|
+
var DurableObjectExecutionStore = class DurableObjectExecutionStore {
|
|
8
|
+
checkpoints;
|
|
9
|
+
events;
|
|
10
|
+
notifications;
|
|
11
|
+
runs;
|
|
12
|
+
sessions;
|
|
13
|
+
#prefix;
|
|
14
|
+
#storage;
|
|
15
|
+
constructor({ prefix = "pss-runtime", storage }) {
|
|
16
|
+
this.#prefix = prefix;
|
|
17
|
+
this.#storage = storage;
|
|
18
|
+
this.checkpoints = new DurableObjectCheckpointStore(storage, prefix);
|
|
19
|
+
this.events = new DurableObjectEventStore(storage, prefix);
|
|
20
|
+
this.notifications = new DurableObjectNotificationInbox(storage, prefix);
|
|
21
|
+
this.runs = new DurableObjectRunStore(storage, prefix);
|
|
22
|
+
this.sessions = new DurableObjectExecutionSessionStore(storage, prefix);
|
|
23
|
+
}
|
|
24
|
+
async transaction(fn) {
|
|
25
|
+
if (this.#storage.transaction) return await this.#storage.transaction((storage) => fn(new DurableObjectExecutionStore({
|
|
26
|
+
prefix: this.#prefix,
|
|
27
|
+
storage
|
|
28
|
+
})));
|
|
29
|
+
return await fn(this);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { DurableObjectExecutionStore };
|
|
34
|
+
|
|
35
|
+
//# sourceMappingURL=cloudflare-execution-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-execution-store.js","names":["#prefix","#storage"],"sources":["../../src/cloudflare/cloudflare-execution-store.ts"],"sourcesContent":["import type {\n CheckpointStore,\n EventStore,\n ExecutionStore,\n ExecutionStoreTransaction,\n NotificationInbox,\n RunStore,\n} from \"../execution\";\nimport type { SessionStore } from \"../index\";\nimport { DurableObjectCheckpointStore } from \"./cloudflare-checkpoint-store\";\nimport { DurableObjectEventStore } from \"./cloudflare-event-store\";\nimport { DurableObjectExecutionSessionStore } from \"./cloudflare-execution-session-store\";\nimport { DurableObjectNotificationInbox } from \"./cloudflare-notification-store\";\nimport { DurableObjectRunStore } from \"./cloudflare-run-store\";\nimport type { CloudflareDurableObjectStorage } from \"./durable-object-storage\";\n\nexport class DurableObjectExecutionStore implements ExecutionStore {\n readonly checkpoints: CheckpointStore;\n readonly events: EventStore;\n readonly notifications: NotificationInbox;\n readonly runs: RunStore;\n readonly sessions: SessionStore;\n readonly #prefix: string;\n readonly #storage: CloudflareDurableObjectStorage;\n\n constructor({\n prefix = \"pss-runtime\",\n storage,\n }: {\n readonly prefix?: string;\n readonly storage: CloudflareDurableObjectStorage;\n }) {\n this.#prefix = prefix;\n this.#storage = storage;\n this.checkpoints = new DurableObjectCheckpointStore(storage, prefix);\n this.events = new DurableObjectEventStore(storage, prefix);\n this.notifications = new DurableObjectNotificationInbox(storage, prefix);\n this.runs = new DurableObjectRunStore(storage, prefix);\n this.sessions = new DurableObjectExecutionSessionStore(storage, prefix);\n }\n\n async transaction<T>(\n fn: (tx: ExecutionStoreTransaction) => Promise<T>\n ): Promise<T> {\n if (this.#storage.transaction) {\n return await this.#storage.transaction((storage) =>\n fn(new DurableObjectExecutionStore({ prefix: this.#prefix, storage }))\n );\n }\n\n return await fn(this);\n }\n}\n"],"mappings":";;;;;;AAgBA,IAAa,8BAAb,MAAa,4BAAsD;CACjE;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,EACV,SAAS,eACT,WAIC;EACD,KAAKA,UAAU;EACf,KAAKC,WAAW;EAChB,KAAK,cAAc,IAAI,6BAA6B,SAAS,MAAM;EACnE,KAAK,SAAS,IAAI,wBAAwB,SAAS,MAAM;EACzD,KAAK,gBAAgB,IAAI,+BAA+B,SAAS,MAAM;EACvE,KAAK,OAAO,IAAI,sBAAsB,SAAS,MAAM;EACrD,KAAK,WAAW,IAAI,mCAAmC,SAAS,MAAM;CACxE;CAEA,MAAM,YACJ,IACY;EACZ,IAAI,KAAKA,SAAS,aAChB,OAAO,MAAM,KAAKA,SAAS,aAAa,YACtC,GAAG,IAAI,4BAA4B;GAAE,QAAQ,KAAKD;GAAS;EAAQ,CAAC,CAAC,CACvE;EAGF,OAAO,MAAM,GAAG,IAAI;CACtB;AACF"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { SessionStore } from "../session/store/types.js";
|
|
2
|
+
import { ExecutionHost, ExecutionScheduler } from "../execution/types.js";
|
|
3
|
+
import { CloudflareDurableObjectStorage as CloudflareDurableObjectStorage$1, InMemoryCloudflareDurableObjectStorage as InMemoryCloudflareDurableObjectStorage$1 } from "./durable-object-storage.js";
|
|
4
|
+
|
|
5
|
+
//#region src/cloudflare/cloudflare-host.d.ts
|
|
6
|
+
interface CloudflareScheduledSessionPrompt {
|
|
7
|
+
readonly idempotencyKey?: string;
|
|
8
|
+
readonly notificationId?: string;
|
|
9
|
+
readonly runId?: string;
|
|
10
|
+
readonly sessionKey: string;
|
|
11
|
+
}
|
|
12
|
+
type CloudflareDurableObjectStorage = CloudflareDurableObjectStorage$1;
|
|
13
|
+
type CloudflareDurableObjectId = unknown;
|
|
14
|
+
interface CloudflareDurableObjectStub {
|
|
15
|
+
fetch(request: Request): Promise<Response>;
|
|
16
|
+
}
|
|
17
|
+
interface CloudflareDurableObjectNamespace<Stub extends CloudflareDurableObjectStub = CloudflareDurableObjectStub> {
|
|
18
|
+
get(id: CloudflareDurableObjectId): Stub;
|
|
19
|
+
idFromName(name: string): CloudflareDurableObjectId;
|
|
20
|
+
}
|
|
21
|
+
interface CloudflareDurableObjectState {
|
|
22
|
+
readonly storage: CloudflareDurableObjectStorage;
|
|
23
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
24
|
+
}
|
|
25
|
+
declare class InMemoryCloudflareDurableObjectStorage extends InMemoryCloudflareDurableObjectStorage$1 {}
|
|
26
|
+
declare function createCloudflareDurableObjectHost({
|
|
27
|
+
prefix,
|
|
28
|
+
sessionStore,
|
|
29
|
+
storage,
|
|
30
|
+
scheduler
|
|
31
|
+
}: {
|
|
32
|
+
readonly prefix?: string;
|
|
33
|
+
readonly scheduler?: ExecutionScheduler;
|
|
34
|
+
readonly sessionStore?: SessionStore;
|
|
35
|
+
readonly storage: CloudflareDurableObjectStorage;
|
|
36
|
+
}): ExecutionHost;
|
|
37
|
+
declare function createCloudflareAlarmScheduler({
|
|
38
|
+
prefix,
|
|
39
|
+
storage
|
|
40
|
+
}: {
|
|
41
|
+
readonly prefix?: string;
|
|
42
|
+
readonly storage: CloudflareDurableObjectStorage;
|
|
43
|
+
}): ExecutionScheduler;
|
|
44
|
+
declare function listScheduledCloudflareRuns(storage: CloudflareDurableObjectStorage, options?: {
|
|
45
|
+
readonly prefix?: string;
|
|
46
|
+
}): Promise<readonly string[]>;
|
|
47
|
+
declare function ackScheduledCloudflareRun(storage: CloudflareDurableObjectStorage, runId: string, options?: {
|
|
48
|
+
readonly prefix?: string;
|
|
49
|
+
}): Promise<void>;
|
|
50
|
+
declare function listScheduledCloudflareSessionPrompts(storage: CloudflareDurableObjectStorage, options?: {
|
|
51
|
+
readonly prefix?: string;
|
|
52
|
+
}): Promise<readonly CloudflareScheduledSessionPrompt[]>;
|
|
53
|
+
declare function ackScheduledCloudflareSessionPrompt(storage: CloudflareDurableObjectStorage, prompt: CloudflareScheduledSessionPrompt, options?: {
|
|
54
|
+
readonly prefix?: string;
|
|
55
|
+
}): Promise<void>;
|
|
56
|
+
declare function rescheduleCloudflareAlarm(storage: CloudflareDurableObjectStorage, options?: {
|
|
57
|
+
readonly runAfterMs?: number;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
//#endregion
|
|
60
|
+
export { CloudflareDurableObjectId, CloudflareDurableObjectNamespace, CloudflareDurableObjectState, CloudflareDurableObjectStorage, CloudflareDurableObjectStub, CloudflareScheduledSessionPrompt, InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
|
|
61
|
+
//# sourceMappingURL=cloudflare-host.d.ts.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { DurableObjectExecutionStore } from "./cloudflare-execution-store.js";
|
|
2
|
+
import { InMemoryCloudflareDurableObjectStorage as InMemoryCloudflareDurableObjectStorage$1 } from "./durable-object-storage.js";
|
|
3
|
+
//#region src/cloudflare/cloudflare-host.ts
|
|
4
|
+
const defaultPrefix = "pss-runtime";
|
|
5
|
+
var InMemoryCloudflareDurableObjectStorage = class extends InMemoryCloudflareDurableObjectStorage$1 {};
|
|
6
|
+
function createCloudflareDurableObjectHost({ prefix = defaultPrefix, sessionStore, storage, scheduler = createCloudflareAlarmScheduler({
|
|
7
|
+
prefix,
|
|
8
|
+
storage
|
|
9
|
+
}) }) {
|
|
10
|
+
const store = new DurableObjectExecutionStore({
|
|
11
|
+
prefix,
|
|
12
|
+
storage
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
capabilities: {},
|
|
16
|
+
scheduler,
|
|
17
|
+
store: sessionStore ? executionStoreWithSessions(store, sessionStore) : store
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function createCloudflareAlarmScheduler({ prefix = defaultPrefix, storage }) {
|
|
21
|
+
return {
|
|
22
|
+
enqueueRun: async (runId, options) => {
|
|
23
|
+
await appendUnique(storage, scheduledRunsKey(prefix), runId);
|
|
24
|
+
await setAlarm(storage, options?.runAfterMs ?? 0);
|
|
25
|
+
},
|
|
26
|
+
resumeSession: async (sessionKey, options) => {
|
|
27
|
+
await appendSessionPrompt(storage, scheduledSessionPromptsKey(prefix), {
|
|
28
|
+
idempotencyKey: options?.idempotencyKey,
|
|
29
|
+
notificationId: options?.notificationId,
|
|
30
|
+
runId: options?.runId,
|
|
31
|
+
sessionKey
|
|
32
|
+
});
|
|
33
|
+
await setAlarm(storage, 0);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
async function listScheduledCloudflareRuns(storage, options = {}) {
|
|
38
|
+
return await readList(storage, scheduledRunsKey(options.prefix ?? defaultPrefix));
|
|
39
|
+
}
|
|
40
|
+
async function ackScheduledCloudflareRun(storage, runId, options = {}) {
|
|
41
|
+
await removeFromList(storage, scheduledRunsKey(options.prefix ?? defaultPrefix), (scheduledRunId) => scheduledRunId === runId);
|
|
42
|
+
}
|
|
43
|
+
async function listScheduledCloudflareSessionPrompts(storage, options = {}) {
|
|
44
|
+
return await readList(storage, scheduledSessionPromptsKey(options.prefix ?? defaultPrefix));
|
|
45
|
+
}
|
|
46
|
+
async function ackScheduledCloudflareSessionPrompt(storage, prompt, options = {}) {
|
|
47
|
+
await removeFromList(storage, scheduledSessionPromptsKey(options.prefix ?? defaultPrefix), (scheduledPrompt) => sessionPromptsMatch(scheduledPrompt, prompt));
|
|
48
|
+
}
|
|
49
|
+
async function rescheduleCloudflareAlarm(storage, options = {}) {
|
|
50
|
+
await setAlarm(storage, options.runAfterMs ?? 0);
|
|
51
|
+
}
|
|
52
|
+
async function appendUnique(storage, key, value) {
|
|
53
|
+
await withTransaction(storage, async (tx) => {
|
|
54
|
+
const values = await readList(tx, key);
|
|
55
|
+
if (!values.includes(value)) {
|
|
56
|
+
values.push(value);
|
|
57
|
+
await tx.put(key, values);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async function appendSessionPrompt(storage, key, prompt) {
|
|
62
|
+
await withTransaction(storage, async (tx) => {
|
|
63
|
+
const values = await readList(tx, key);
|
|
64
|
+
if (!values.some((existing) => existing.sessionKey === prompt.sessionKey && existing.idempotencyKey === prompt.idempotencyKey && existing.runId === prompt.runId)) {
|
|
65
|
+
values.push(prompt);
|
|
66
|
+
await tx.put(key, values);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function removeFromList(storage, key, shouldRemove) {
|
|
71
|
+
await withTransaction(storage, async (tx) => {
|
|
72
|
+
const remaining = (await readList(tx, key)).filter((value) => !shouldRemove(value));
|
|
73
|
+
await tx.put(key, remaining);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function readList(storage, key) {
|
|
77
|
+
return (await storage.get(key) ?? []).map((item) => structuredClone(item));
|
|
78
|
+
}
|
|
79
|
+
async function setAlarm(storage, runAfterMs) {
|
|
80
|
+
await storage.setAlarm?.(Date.now() + Math.max(0, runAfterMs));
|
|
81
|
+
}
|
|
82
|
+
async function withTransaction(storage, fn) {
|
|
83
|
+
return storage.transaction ? await storage.transaction(fn) : await fn(storage);
|
|
84
|
+
}
|
|
85
|
+
function scheduledRunsKey(prefix) {
|
|
86
|
+
return `${prefix}:scheduled-runs`;
|
|
87
|
+
}
|
|
88
|
+
function scheduledSessionPromptsKey(prefix) {
|
|
89
|
+
return `${prefix}:scheduled-session-prompts`;
|
|
90
|
+
}
|
|
91
|
+
function sessionPromptsMatch(left, right) {
|
|
92
|
+
return left.idempotencyKey === right.idempotencyKey && left.notificationId === right.notificationId && left.runId === right.runId && left.sessionKey === right.sessionKey;
|
|
93
|
+
}
|
|
94
|
+
function executionStoreWithSessions(store, sessions) {
|
|
95
|
+
return {
|
|
96
|
+
checkpoints: store.checkpoints,
|
|
97
|
+
events: store.events,
|
|
98
|
+
notifications: store.notifications,
|
|
99
|
+
runs: store.runs,
|
|
100
|
+
sessions,
|
|
101
|
+
transaction: (fn) => store.transaction((tx) => fn({
|
|
102
|
+
checkpoints: tx.checkpoints,
|
|
103
|
+
events: tx.events,
|
|
104
|
+
notifications: tx.notifications,
|
|
105
|
+
runs: tx.runs,
|
|
106
|
+
sessions
|
|
107
|
+
}))
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { InMemoryCloudflareDurableObjectStorage, ackScheduledCloudflareRun, ackScheduledCloudflareSessionPrompt, createCloudflareAlarmScheduler, createCloudflareDurableObjectHost, listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm };
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=cloudflare-host.js.map
|