@minpeter/pss-runtime 0.1.0-next.1 → 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 +290 -61
- package/dist/agent-host-session-store.js +10 -0
- package/dist/agent-host-session-store.js.map +1 -0
- package/dist/agent-loop.js +57 -28
- package/dist/agent-loop.js.map +1 -1
- package/dist/agent-namespace.js +6 -3
- package/dist/agent-namespace.js.map +1 -1
- package/dist/agent-options.d.ts +29 -0
- package/dist/agent-options.js +16 -0
- package/dist/agent-options.js.map +1 -0
- package/dist/agent-resume.js +63 -0
- package/dist/agent-resume.js.map +1 -0
- package/dist/agent-session-entry.d.ts +13 -0
- package/dist/agent.d.ts +8 -44
- package/dist/agent.js +61 -83
- 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 +62 -0
- package/dist/execution/host.js.map +1 -0
- package/dist/execution/index.d.ts +6 -0
- package/dist/execution/index.js +4 -0
- package/dist/execution/memory-notifications.js +54 -0
- package/dist/execution/memory-notifications.js.map +1 -0
- package/dist/execution/memory-state.js +34 -0
- package/dist/execution/memory-state.js.map +1 -0
- package/dist/execution/memory-store.js +203 -0
- package/dist/execution/memory-store.js.map +1 -0
- package/dist/execution/memory.d.ts +7 -0
- package/dist/execution/memory.js +28 -0
- package/dist/execution/memory.js.map +1 -0
- package/dist/execution/types.d.ts +150 -0
- package/dist/index.d.ts +13 -6
- package/dist/index.js +6 -1
- package/dist/llm-tool-execution.d.ts +35 -0
- package/dist/llm-tool-execution.js +126 -0
- package/dist/llm-tool-execution.js.map +1 -0
- package/dist/llm.d.ts +11 -15
- package/dist/llm.js +5 -3
- package/dist/llm.js.map +1 -1
- package/dist/plugins.d.ts +42 -0
- package/dist/plugins.js +43 -0
- package/dist/plugins.js.map +1 -0
- 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 -22
- 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 +10 -24
- package/dist/session/runtime-input.js.map +1 -1
- package/dist/session/session-errors.js +1 -6
- package/dist/session/session-errors.js.map +1 -1
- package/dist/session/session-events.js +73 -0
- package/dist/session/session-events.js.map +1 -0
- package/dist/session/session-execution.js +88 -0
- package/dist/session/session-execution.js.map +1 -0
- package/dist/session/session-notification.js +59 -0
- package/dist/session/session-notification.js.map +1 -0
- 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 +125 -0
- package/dist/session/session-turn-processor.js.map +1 -0
- package/dist/session/session.js +81 -102
- package/dist/session/session.js.map +1 -1
- package/dist/session/snapshot.js.map +1 -1
- package/package.json +16 -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/hooks.d.ts +0 -32
- package/dist/subagent-job-cancel.js +0 -28
- package/dist/subagent-job-cancel.js.map +0 -1
- package/dist/subagent-job-output.js +0 -63
- package/dist/subagent-job-output.js.map +0 -1
- package/dist/subagent-jobs.js +0 -151
- 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 -92
- package/dist/subagents.js.map +0 -1
- /package/dist/session/{runtime-input.d.ts → session-execution.d.ts} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AgentEvent } from "../session/events.js";
|
|
2
|
+
import { AgentRun } from "../session/run.js";
|
|
3
|
+
import { CloudflareAlarmContinuationReason, CloudflareAlarmDrainBudget, FailedScheduledWork } from "./cloudflare-alarm-budget.js";
|
|
4
|
+
import { CloudflareDurableObjectStorage } from "./cloudflare-host.js";
|
|
5
|
+
|
|
6
|
+
//#region src/cloudflare/cloudflare-alarm-drainer.d.ts
|
|
7
|
+
interface CloudflareAlarmDrainSummary {
|
|
8
|
+
readonly consumedSessionPrompts: readonly string[];
|
|
9
|
+
readonly continuationReasons: readonly CloudflareAlarmContinuationReason[];
|
|
10
|
+
readonly continuationScheduled: boolean;
|
|
11
|
+
readonly droppedEvents: number;
|
|
12
|
+
readonly events: readonly AgentEvent[];
|
|
13
|
+
readonly failedRuns: readonly FailedScheduledWork[];
|
|
14
|
+
readonly failedSessionPrompts: readonly FailedScheduledWork[];
|
|
15
|
+
readonly markers: readonly string[];
|
|
16
|
+
readonly remainingRuns: number;
|
|
17
|
+
readonly remainingSessionPrompts: number;
|
|
18
|
+
readonly resumedRuns: readonly string[];
|
|
19
|
+
}
|
|
20
|
+
interface CloudflareAlarmAgent {
|
|
21
|
+
resume(runId: string): Promise<AgentRun | null>;
|
|
22
|
+
}
|
|
23
|
+
declare class CloudflareAlarmDrainFailureError extends Error {
|
|
24
|
+
readonly summary: CloudflareAlarmDrainSummary;
|
|
25
|
+
constructor(summary: CloudflareAlarmDrainSummary);
|
|
26
|
+
}
|
|
27
|
+
declare function drainCloudflareAlarm({
|
|
28
|
+
agent,
|
|
29
|
+
continuationRunAfterMs,
|
|
30
|
+
deadlineMs,
|
|
31
|
+
failureRunAfterMs,
|
|
32
|
+
maxEvents,
|
|
33
|
+
maxRuns,
|
|
34
|
+
maxSessionPrompts,
|
|
35
|
+
prefix,
|
|
36
|
+
storage,
|
|
37
|
+
throwOnFailure
|
|
38
|
+
}: {
|
|
39
|
+
readonly agent: CloudflareAlarmAgent;
|
|
40
|
+
readonly prefix: string;
|
|
41
|
+
readonly storage: CloudflareDurableObjectStorage;
|
|
42
|
+
} & CloudflareAlarmDrainBudget): Promise<CloudflareAlarmDrainSummary>;
|
|
43
|
+
//#endregion
|
|
44
|
+
export { CloudflareAlarmAgent, CloudflareAlarmDrainFailureError, CloudflareAlarmDrainSummary, drainCloudflareAlarm };
|
|
45
|
+
//# sourceMappingURL=cloudflare-alarm-drainer.d.ts.map
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createAlarmDrainState, normalizeAlarmDrainBudget, shouldStopRuns, shouldStopSessionPrompts } from "./cloudflare-alarm-budget.js";
|
|
2
|
+
import { listScheduledCloudflareRuns, listScheduledCloudflareSessionPrompts, rescheduleCloudflareAlarm } from "./cloudflare-host.js";
|
|
3
|
+
import { resumeScheduledRun, resumeScheduledSessionPrompt } from "./cloudflare-alarm-work.js";
|
|
4
|
+
//#region src/cloudflare/cloudflare-alarm-drainer.ts
|
|
5
|
+
var CloudflareAlarmDrainFailureError = class extends Error {
|
|
6
|
+
summary;
|
|
7
|
+
constructor(summary) {
|
|
8
|
+
super("Cloudflare alarm drain completed with failed scheduled work.");
|
|
9
|
+
this.name = "CloudflareAlarmDrainFailureError";
|
|
10
|
+
this.summary = summary;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
async function drainCloudflareAlarm({ agent, continuationRunAfterMs, deadlineMs, failureRunAfterMs, maxEvents, maxRuns, maxSessionPrompts, prefix, storage, throwOnFailure }) {
|
|
14
|
+
const budget = normalizeAlarmDrainBudget({
|
|
15
|
+
continuationRunAfterMs,
|
|
16
|
+
deadlineMs,
|
|
17
|
+
failureRunAfterMs,
|
|
18
|
+
maxEvents,
|
|
19
|
+
maxRuns,
|
|
20
|
+
maxSessionPrompts,
|
|
21
|
+
throwOnFailure
|
|
22
|
+
});
|
|
23
|
+
const state = createAlarmDrainState();
|
|
24
|
+
await drainRuns({
|
|
25
|
+
agent,
|
|
26
|
+
budget,
|
|
27
|
+
prefix,
|
|
28
|
+
state,
|
|
29
|
+
storage
|
|
30
|
+
});
|
|
31
|
+
await drainSessionPrompts({
|
|
32
|
+
agent,
|
|
33
|
+
budget,
|
|
34
|
+
prefix,
|
|
35
|
+
state,
|
|
36
|
+
storage
|
|
37
|
+
});
|
|
38
|
+
const summary = await summarizeDrain({
|
|
39
|
+
budget,
|
|
40
|
+
prefix,
|
|
41
|
+
state,
|
|
42
|
+
storage
|
|
43
|
+
});
|
|
44
|
+
if (budget.throwOnFailure && hasFailures(summary)) throw new CloudflareAlarmDrainFailureError(summary);
|
|
45
|
+
return summary;
|
|
46
|
+
}
|
|
47
|
+
async function drainRuns(options) {
|
|
48
|
+
for (const runId of await listScheduledCloudflareRuns(options.storage, { prefix: options.prefix })) {
|
|
49
|
+
if (shouldStopRuns(options.state, options.budget)) break;
|
|
50
|
+
options.state.runAttempts += 1;
|
|
51
|
+
await resumeScheduledRun({
|
|
52
|
+
...options,
|
|
53
|
+
runId
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function drainSessionPrompts(options) {
|
|
58
|
+
const prompts = await listScheduledCloudflareSessionPrompts(options.storage, { prefix: options.prefix });
|
|
59
|
+
for (const prompt of prompts) {
|
|
60
|
+
if (shouldStopSessionPrompts(options.state, options.budget)) break;
|
|
61
|
+
options.state.sessionPromptAttempts += 1;
|
|
62
|
+
await resumeScheduledSessionPrompt({
|
|
63
|
+
...options,
|
|
64
|
+
prompt
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function summarizeDrain({ budget, prefix, state, storage }) {
|
|
69
|
+
const remainingRuns = await listScheduledCloudflareRuns(storage, { prefix });
|
|
70
|
+
const remainingPrompts = await listScheduledCloudflareSessionPrompts(storage, { prefix });
|
|
71
|
+
if (state.failedRuns.length > 0 || state.failedSessionPrompts.length > 0) state.reasons.add("failure");
|
|
72
|
+
const continuationScheduled = shouldRearm(state, remainingRuns.length, remainingPrompts.length);
|
|
73
|
+
if (continuationScheduled) await rescheduleCloudflareAlarm(storage, { runAfterMs: state.reasons.has("failure") ? budget.failureRunAfterMs : budget.continuationRunAfterMs });
|
|
74
|
+
return {
|
|
75
|
+
consumedSessionPrompts: state.consumedSessionPrompts,
|
|
76
|
+
continuationReasons: [...state.reasons],
|
|
77
|
+
continuationScheduled,
|
|
78
|
+
droppedEvents: state.droppedEvents,
|
|
79
|
+
events: state.events,
|
|
80
|
+
failedRuns: state.failedRuns,
|
|
81
|
+
failedSessionPrompts: state.failedSessionPrompts,
|
|
82
|
+
markers: markersFor(state.reasons),
|
|
83
|
+
remainingRuns: remainingRuns.length,
|
|
84
|
+
remainingSessionPrompts: remainingPrompts.length,
|
|
85
|
+
resumedRuns: state.resumedRuns
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function shouldRearm(state, remainingRuns, remainingPrompts) {
|
|
89
|
+
return state.reasons.size > 0 && (remainingRuns > 0 || remainingPrompts > 0 || state.reasons.has("failure"));
|
|
90
|
+
}
|
|
91
|
+
function hasFailures(summary) {
|
|
92
|
+
return summary.failedRuns.length > 0 || summary.failedSessionPrompts.length > 0;
|
|
93
|
+
}
|
|
94
|
+
function markersFor(reasons) {
|
|
95
|
+
const markers = ["alarm:resume", "resume:session-prompt"];
|
|
96
|
+
if (reasons.size > 0) markers.push("alarm:continuation-rearm");
|
|
97
|
+
if (reasons.has("failure")) markers.push("alarm:failure");
|
|
98
|
+
return markers;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { CloudflareAlarmDrainFailureError, drainCloudflareAlarm };
|
|
102
|
+
|
|
103
|
+
//# sourceMappingURL=cloudflare-alarm-drainer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-alarm-drainer.js","names":[],"sources":["../../src/cloudflare/cloudflare-alarm-drainer.ts"],"sourcesContent":["import type { AgentEvent, AgentRun } from \"../index\";\nimport {\n type AlarmDrainState,\n type CloudflareAlarmContinuationReason,\n type CloudflareAlarmDrainBudget,\n createAlarmDrainState,\n type FailedScheduledWork,\n type NormalizedAlarmDrainBudget,\n normalizeAlarmDrainBudget,\n shouldStopRuns,\n shouldStopSessionPrompts,\n} from \"./cloudflare-alarm-budget\";\nimport {\n resumeScheduledRun,\n resumeScheduledSessionPrompt,\n} from \"./cloudflare-alarm-work\";\nimport {\n type CloudflareDurableObjectStorage,\n listScheduledCloudflareRuns,\n listScheduledCloudflareSessionPrompts,\n rescheduleCloudflareAlarm,\n} from \"./cloudflare-host\";\n\nexport type {\n CloudflareAlarmContinuationReason,\n CloudflareAlarmDrainBudget,\n FailedScheduledWork,\n} from \"./cloudflare-alarm-budget\";\n\nexport interface CloudflareAlarmDrainSummary {\n readonly consumedSessionPrompts: readonly string[];\n readonly continuationReasons: readonly CloudflareAlarmContinuationReason[];\n readonly continuationScheduled: boolean;\n readonly droppedEvents: number;\n readonly events: readonly AgentEvent[];\n readonly failedRuns: readonly FailedScheduledWork[];\n readonly failedSessionPrompts: readonly FailedScheduledWork[];\n readonly markers: readonly string[];\n readonly remainingRuns: number;\n readonly remainingSessionPrompts: number;\n readonly resumedRuns: readonly string[];\n}\n\nexport interface CloudflareAlarmAgent {\n resume(runId: string): Promise<AgentRun | null>;\n}\n\nexport class CloudflareAlarmDrainFailureError extends Error {\n readonly summary: CloudflareAlarmDrainSummary;\n\n constructor(summary: CloudflareAlarmDrainSummary) {\n super(\"Cloudflare alarm drain completed with failed scheduled work.\");\n this.name = \"CloudflareAlarmDrainFailureError\";\n this.summary = summary;\n }\n}\n\nexport async function drainCloudflareAlarm({\n agent,\n continuationRunAfterMs,\n deadlineMs,\n failureRunAfterMs,\n maxEvents,\n maxRuns,\n maxSessionPrompts,\n prefix,\n storage,\n throwOnFailure,\n}: {\n readonly agent: CloudflareAlarmAgent;\n readonly prefix: string;\n readonly storage: CloudflareDurableObjectStorage;\n} & CloudflareAlarmDrainBudget): Promise<CloudflareAlarmDrainSummary> {\n const budget = normalizeAlarmDrainBudget({\n continuationRunAfterMs,\n deadlineMs,\n failureRunAfterMs,\n maxEvents,\n maxRuns,\n maxSessionPrompts,\n throwOnFailure,\n });\n const state = createAlarmDrainState();\n\n await drainRuns({ agent, budget, prefix, state, storage });\n await drainSessionPrompts({ agent, budget, prefix, state, storage });\n\n const summary = await summarizeDrain({ budget, prefix, state, storage });\n if (budget.throwOnFailure && hasFailures(summary)) {\n throw new CloudflareAlarmDrainFailureError(summary);\n }\n return summary;\n}\n\nasync function drainRuns(options: DrainLoopOptions): Promise<void> {\n for (const runId of await listScheduledCloudflareRuns(options.storage, {\n prefix: options.prefix,\n })) {\n if (shouldStopRuns(options.state, options.budget)) {\n break;\n }\n options.state.runAttempts += 1;\n await resumeScheduledRun({ ...options, runId });\n }\n}\n\nasync function drainSessionPrompts(options: DrainLoopOptions): Promise<void> {\n const prompts = await listScheduledCloudflareSessionPrompts(options.storage, {\n prefix: options.prefix,\n });\n for (const prompt of prompts) {\n if (shouldStopSessionPrompts(options.state, options.budget)) {\n break;\n }\n options.state.sessionPromptAttempts += 1;\n await resumeScheduledSessionPrompt({ ...options, prompt });\n }\n}\n\nasync function summarizeDrain({\n budget,\n prefix,\n state,\n storage,\n}: {\n readonly budget: NormalizedAlarmDrainBudget;\n readonly prefix: string;\n readonly state: AlarmDrainState;\n readonly storage: CloudflareDurableObjectStorage;\n}): Promise<CloudflareAlarmDrainSummary> {\n const remainingRuns = await listScheduledCloudflareRuns(storage, { prefix });\n const remainingPrompts = await listScheduledCloudflareSessionPrompts(\n storage,\n {\n prefix,\n }\n );\n if (state.failedRuns.length > 0 || state.failedSessionPrompts.length > 0) {\n state.reasons.add(\"failure\");\n }\n const continuationScheduled = shouldRearm(\n state,\n remainingRuns.length,\n remainingPrompts.length\n );\n if (continuationScheduled) {\n await rescheduleCloudflareAlarm(storage, {\n runAfterMs: state.reasons.has(\"failure\")\n ? budget.failureRunAfterMs\n : budget.continuationRunAfterMs,\n });\n }\n return {\n consumedSessionPrompts: state.consumedSessionPrompts,\n continuationReasons: [...state.reasons],\n continuationScheduled,\n droppedEvents: state.droppedEvents,\n events: state.events,\n failedRuns: state.failedRuns,\n failedSessionPrompts: state.failedSessionPrompts,\n markers: markersFor(state.reasons),\n remainingRuns: remainingRuns.length,\n remainingSessionPrompts: remainingPrompts.length,\n resumedRuns: state.resumedRuns,\n };\n}\n\ninterface DrainLoopOptions {\n readonly agent: CloudflareAlarmAgent;\n readonly budget: NormalizedAlarmDrainBudget;\n readonly prefix: string;\n readonly state: AlarmDrainState;\n readonly storage: CloudflareDurableObjectStorage;\n}\n\nfunction shouldRearm(\n state: AlarmDrainState,\n remainingRuns: number,\n remainingPrompts: number\n): boolean {\n return (\n state.reasons.size > 0 &&\n (remainingRuns > 0 || remainingPrompts > 0 || state.reasons.has(\"failure\"))\n );\n}\n\nfunction hasFailures(summary: CloudflareAlarmDrainSummary): boolean {\n return (\n summary.failedRuns.length > 0 || summary.failedSessionPrompts.length > 0\n );\n}\n\nfunction markersFor(\n reasons: ReadonlySet<CloudflareAlarmContinuationReason>\n): readonly string[] {\n const markers = [\"alarm:resume\", \"resume:session-prompt\"];\n if (reasons.size > 0) {\n markers.push(\"alarm:continuation-rearm\");\n }\n if (reasons.has(\"failure\")) {\n markers.push(\"alarm:failure\");\n }\n return markers;\n}\n"],"mappings":";;;;AA+CA,IAAa,mCAAb,cAAsD,MAAM;CAC1D;CAEA,YAAY,SAAsC;EAChD,MAAM,8DAA8D;EACpE,KAAK,OAAO;EACZ,KAAK,UAAU;CACjB;AACF;AAEA,eAAsB,qBAAqB,EACzC,OACA,wBACA,YACA,mBACA,WACA,SACA,mBACA,QACA,SACA,kBAKoE;CACpE,MAAM,SAAS,0BAA0B;EACvC;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC;CACD,MAAM,QAAQ,sBAAsB;CAEpC,MAAM,UAAU;EAAE;EAAO;EAAQ;EAAQ;EAAO;CAAQ,CAAC;CACzD,MAAM,oBAAoB;EAAE;EAAO;EAAQ;EAAQ;EAAO;CAAQ,CAAC;CAEnE,MAAM,UAAU,MAAM,eAAe;EAAE;EAAQ;EAAQ;EAAO;CAAQ,CAAC;CACvE,IAAI,OAAO,kBAAkB,YAAY,OAAO,GAC9C,MAAM,IAAI,iCAAiC,OAAO;CAEpD,OAAO;AACT;AAEA,eAAe,UAAU,SAA0C;CACjE,KAAK,MAAM,SAAS,MAAM,4BAA4B,QAAQ,SAAS,EACrE,QAAQ,QAAQ,OAClB,CAAC,GAAG;EACF,IAAI,eAAe,QAAQ,OAAO,QAAQ,MAAM,GAC9C;EAEF,QAAQ,MAAM,eAAe;EAC7B,MAAM,mBAAmB;GAAE,GAAG;GAAS;EAAM,CAAC;CAChD;AACF;AAEA,eAAe,oBAAoB,SAA0C;CAC3E,MAAM,UAAU,MAAM,sCAAsC,QAAQ,SAAS,EAC3E,QAAQ,QAAQ,OAClB,CAAC;CACD,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,yBAAyB,QAAQ,OAAO,QAAQ,MAAM,GACxD;EAEF,QAAQ,MAAM,yBAAyB;EACvC,MAAM,6BAA6B;GAAE,GAAG;GAAS;EAAO,CAAC;CAC3D;AACF;AAEA,eAAe,eAAe,EAC5B,QACA,QACA,OACA,WAMuC;CACvC,MAAM,gBAAgB,MAAM,4BAA4B,SAAS,EAAE,OAAO,CAAC;CAC3E,MAAM,mBAAmB,MAAM,sCAC7B,SACA,EACE,OACF,CACF;CACA,IAAI,MAAM,WAAW,SAAS,KAAK,MAAM,qBAAqB,SAAS,GACrE,MAAM,QAAQ,IAAI,SAAS;CAE7B,MAAM,wBAAwB,YAC5B,OACA,cAAc,QACd,iBAAiB,MACnB;CACA,IAAI,uBACF,MAAM,0BAA0B,SAAS,EACvC,YAAY,MAAM,QAAQ,IAAI,SAAS,IACnC,OAAO,oBACP,OAAO,uBACb,CAAC;CAEH,OAAO;EACL,wBAAwB,MAAM;EAC9B,qBAAqB,CAAC,GAAG,MAAM,OAAO;EACtC;EACA,eAAe,MAAM;EACrB,QAAQ,MAAM;EACd,YAAY,MAAM;EAClB,sBAAsB,MAAM;EAC5B,SAAS,WAAW,MAAM,OAAO;EACjC,eAAe,cAAc;EAC7B,yBAAyB,iBAAiB;EAC1C,aAAa,MAAM;CACrB;AACF;AAUA,SAAS,YACP,OACA,eACA,kBACS;CACT,OACE,MAAM,QAAQ,OAAO,MACpB,gBAAgB,KAAK,mBAAmB,KAAK,MAAM,QAAQ,IAAI,SAAS;AAE7E;AAEA,SAAS,YAAY,SAA+C;CAClE,OACE,QAAQ,WAAW,SAAS,KAAK,QAAQ,qBAAqB,SAAS;AAE3E;AAEA,SAAS,WACP,SACmB;CACnB,MAAM,UAAU,CAAC,gBAAgB,uBAAuB;CACxD,IAAI,QAAQ,OAAO,GACjB,QAAQ,KAAK,0BAA0B;CAEzC,IAAI,QAAQ,IAAI,SAAS,GACvB,QAAQ,KAAK,eAAe;CAE9B,OAAO;AACT"}
|
|
@@ -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"}
|