agents 0.5.1 → 0.7.0

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.
@@ -1,5 +1,6 @@
1
- import { t as MCPObservabilityEvent } from "./mcp-DA0kDE7K.js";
1
+ import { n as MCPObservabilityEvent } from "./agent-DnmmRjyv.js";
2
2
  import { AgentMcpOAuthProvider } from "./mcp/do-oauth-client-provider.js";
3
+ import { McpAgent } from "./mcp/index.js";
3
4
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
4
5
  import {
5
6
  SSEClientTransport,
@@ -12,12 +13,24 @@ import {
12
13
  import {
13
14
  ElicitRequest,
14
15
  ElicitResult,
16
+ InitializeRequestParams,
17
+ JSONRPCMessage,
18
+ MessageExtraInfo,
15
19
  Prompt,
20
+ RequestId,
16
21
  Resource,
17
22
  ResourceTemplate,
18
23
  ServerCapabilities,
19
24
  Tool
20
25
  } from "@modelcontextprotocol/sdk/types.js";
26
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
27
+ import {
28
+ Transport,
29
+ TransportSendOptions
30
+ } from "@modelcontextprotocol/sdk/shared/transport.js";
31
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
32
+ import { Client as Client$1 } from "@modelcontextprotocol/sdk/client";
33
+ import { EventStore } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
21
34
 
22
35
  //#region src/core/events.d.ts
23
36
  interface Disposable {
@@ -33,7 +46,8 @@ declare class Emitter<T> implements Disposable {
33
46
  //#endregion
34
47
  //#region src/mcp/types.d.ts
35
48
  type MaybePromise<T> = T | Promise<T>;
36
- type BaseTransportType = "sse" | "streamable-http";
49
+ type HttpTransportType = "sse" | "streamable-http";
50
+ type BaseTransportType = HttpTransportType | "rpc";
37
51
  type TransportType = BaseTransportType | "auto";
38
52
  interface CORSOptions {
39
53
  origin?: string;
@@ -48,6 +62,221 @@ interface ServeOptions {
48
62
  transport?: BaseTransportType;
49
63
  jurisdiction?: DurableObjectJurisdiction;
50
64
  }
65
+ type McpClientOptions = ConstructorParameters<typeof Client$1>[1];
66
+ //#endregion
67
+ //#region src/mcp/client-transports.d.ts
68
+ /**
69
+ * @deprecated Use SSEClientTransport from @modelcontextprotocol/sdk/client/sse.js instead. This alias will be removed in the next major version.
70
+ */
71
+ declare class SSEEdgeClientTransport extends SSEClientTransport {
72
+ constructor(url: URL, options: SSEClientTransportOptions);
73
+ }
74
+ /**
75
+ * @deprecated Use StreamableHTTPClientTransport from @modelcontextprotocol/sdk/client/streamableHttp.js instead. This alias will be removed in the next major version.
76
+ */
77
+ declare class StreamableHTTPEdgeClientTransport extends StreamableHTTPClientTransport {
78
+ constructor(url: URL, options: StreamableHTTPClientTransportOptions);
79
+ }
80
+ //#endregion
81
+ //#region src/mcp/worker-transport.d.ts
82
+ interface MCPStorageApi {
83
+ get(): Promise<TransportState | undefined> | TransportState | undefined;
84
+ set(state: TransportState): Promise<void> | void;
85
+ }
86
+ interface TransportState {
87
+ sessionId?: string;
88
+ initialized: boolean;
89
+ initializeParams?: InitializeRequestParams;
90
+ }
91
+ interface WorkerTransportOptions {
92
+ /**
93
+ * Function that generates a session ID for the transport.
94
+ * The session ID SHOULD be globally unique and cryptographically secure.
95
+ * Return undefined to disable session management (stateless mode).
96
+ */
97
+ sessionIdGenerator?: () => string;
98
+ /**
99
+ * Enable traditional Request/Response mode, this will disable streaming.
100
+ */
101
+ enableJsonResponse?: boolean;
102
+ /**
103
+ * Callback fired when a new session is initialized.
104
+ */
105
+ onsessioninitialized?: (sessionId: string) => void;
106
+ /**
107
+ * Callback fired when a session is closed via DELETE request.
108
+ */
109
+ onsessionclosed?: (sessionId: string) => void;
110
+ corsOptions?: CORSOptions;
111
+ /**
112
+ * Optional storage api for persisting transport state.
113
+ * Use this to store session state in Durable Object/Agent storage
114
+ * so it survives hibernation/restart.
115
+ */
116
+ storage?: MCPStorageApi;
117
+ /**
118
+ * Event store for resumability support.
119
+ * If provided, enables clients to reconnect and resume messages using Last-Event-ID.
120
+ */
121
+ eventStore?: EventStore;
122
+ /**
123
+ * Retry interval in milliseconds to suggest to clients in SSE retry field.
124
+ * Controls client reconnection timing for polling behavior.
125
+ */
126
+ retryInterval?: number;
127
+ }
128
+ declare class WorkerTransport implements Transport {
129
+ started: boolean;
130
+ private initialized;
131
+ private sessionIdGenerator?;
132
+ private enableJsonResponse;
133
+ private onsessioninitialized?;
134
+ private onsessionclosed?;
135
+ private standaloneSseStreamId;
136
+ private streamMapping;
137
+ private requestToStreamMapping;
138
+ private requestResponseMap;
139
+ private corsOptions?;
140
+ private storage?;
141
+ private stateRestored;
142
+ private eventStore?;
143
+ private retryInterval?;
144
+ private initializeParams?;
145
+ sessionId?: string;
146
+ onclose?: () => void;
147
+ onerror?: (error: Error) => void;
148
+ onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
149
+ constructor(options?: WorkerTransportOptions);
150
+ /**
151
+ * Restore transport state from persistent storage.
152
+ * This is automatically called on start.
153
+ */
154
+ private restoreState;
155
+ /**
156
+ * Persist current transport state to storage.
157
+ */
158
+ private saveState;
159
+ start(): Promise<void>;
160
+ /**
161
+ * Validates the MCP-Protocol-Version header on incoming requests.
162
+ *
163
+ * This performs a simple check: if a version header is present, it must be
164
+ * in the SUPPORTED_PROTOCOL_VERSIONS list. We do not track the negotiated
165
+ * version or enforce version consistency across requests - the SDK handles
166
+ * version negotiation during initialization, and we simply reject any
167
+ * explicitly unsupported versions.
168
+ *
169
+ * - Header present and supported: Accept
170
+ * - Header present and unsupported: 400 Bad Request
171
+ * - Header missing: Accept (version validation is optional)
172
+ */
173
+ private validateProtocolVersion;
174
+ private getHeaders;
175
+ handleRequest(request: Request, parsedBody?: unknown): Promise<Response>;
176
+ private handleGetRequest;
177
+ private handlePostRequest;
178
+ private handleDeleteRequest;
179
+ private handleOptionsRequest;
180
+ private handleUnsupportedRequest;
181
+ private validateSession;
182
+ close(): Promise<void>;
183
+ /**
184
+ * Close an SSE stream for a specific request, triggering client reconnection.
185
+ * Use this to implement polling behavior during long-running operations -
186
+ * client will reconnect after the retry interval specified in the priming event.
187
+ */
188
+ closeSSEStream(requestId: RequestId): void;
189
+ send(message: JSONRPCMessage, options?: TransportSendOptions): Promise<void>;
190
+ }
191
+ //#endregion
192
+ //#region src/mcp/auth-context.d.ts
193
+ interface McpAuthContext {
194
+ props: Record<string, unknown>;
195
+ }
196
+ declare function getMcpAuthContext(): McpAuthContext | undefined;
197
+ //#endregion
198
+ //#region src/mcp/handler.d.ts
199
+ interface CreateMcpHandlerOptions extends WorkerTransportOptions {
200
+ /**
201
+ * The route path that this MCP handler should respond to.
202
+ * If specified, the handler will only process requests that match this route.
203
+ * @default "/mcp"
204
+ */
205
+ route?: string;
206
+ /**
207
+ * An optional auth context to use for handling MCP requests.
208
+ * If not provided, the handler will look for props in the execution context.
209
+ */
210
+ authContext?: McpAuthContext;
211
+ /**
212
+ * An optional transport to use for handling MCP requests.
213
+ * If not provided, a WorkerTransport will be created with the provided WorkerTransportOptions.
214
+ */
215
+ transport?: WorkerTransport;
216
+ }
217
+ declare function createMcpHandler(
218
+ server: McpServer | Server,
219
+ options?: CreateMcpHandlerOptions
220
+ ): (request: Request, env: unknown, ctx: ExecutionContext) => Promise<Response>;
221
+ /**
222
+ * @deprecated This has been renamed to createMcpHandler, and experimental_createMcpHandler will be removed in the next major version
223
+ */
224
+ declare function experimental_createMcpHandler(
225
+ server: McpServer | Server,
226
+ options?: CreateMcpHandlerOptions
227
+ ): (request: Request, env: unknown, ctx: ExecutionContext) => Promise<Response>;
228
+ //#endregion
229
+ //#region src/mcp/rpc.d.ts
230
+ declare const RPC_DO_PREFIX = "rpc:";
231
+ interface RPCClientTransportOptions<T extends McpAgent = McpAgent> {
232
+ namespace: DurableObjectNamespace<T>;
233
+ name: string;
234
+ props?: Record<string, unknown>;
235
+ }
236
+ declare class RPCClientTransport implements Transport {
237
+ private _namespace;
238
+ private _name;
239
+ private _props?;
240
+ private _stub?;
241
+ private _started;
242
+ private _protocolVersion?;
243
+ sessionId?: string;
244
+ onclose?: () => void;
245
+ onerror?: (error: Error) => void;
246
+ onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
247
+ constructor(options: RPCClientTransportOptions<McpAgent>);
248
+ setProtocolVersion(version: string): void;
249
+ getProtocolVersion(): string | undefined;
250
+ start(): Promise<void>;
251
+ close(): Promise<void>;
252
+ send(
253
+ message: JSONRPCMessage | JSONRPCMessage[],
254
+ options?: TransportSendOptions
255
+ ): Promise<void>;
256
+ }
257
+ interface RPCServerTransportOptions {
258
+ timeout?: number;
259
+ }
260
+ declare class RPCServerTransport implements Transport {
261
+ private _started;
262
+ private _pendingResponse;
263
+ private _responseResolver;
264
+ private _protocolVersion?;
265
+ private _timeout;
266
+ sessionId?: string;
267
+ onclose?: () => void;
268
+ onerror?: (error: Error) => void;
269
+ onmessage?: (message: JSONRPCMessage, extra?: MessageExtraInfo) => void;
270
+ constructor(options?: RPCServerTransportOptions);
271
+ setProtocolVersion(version: string): void;
272
+ getProtocolVersion(): string | undefined;
273
+ start(): Promise<void>;
274
+ close(): Promise<void>;
275
+ send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
276
+ handle(
277
+ message: JSONRPCMessage | JSONRPCMessage[]
278
+ ): Promise<JSONRPCMessage | JSONRPCMessage[] | undefined>;
279
+ }
51
280
  //#endregion
52
281
  //#region src/mcp/client-connection.d.ts
53
282
  /**
@@ -71,9 +300,14 @@ declare const MCPConnectionState: {
71
300
  */
72
301
  type MCPConnectionState =
73
302
  (typeof MCPConnectionState)[keyof typeof MCPConnectionState];
303
+ /**
304
+ * Transport options for MCP client connections.
305
+ * Combines transport-specific options with auth provider and type selection.
306
+ */
74
307
  type MCPTransportOptions = (
75
308
  | SSEClientTransportOptions
76
309
  | StreamableHTTPClientTransportOptions
310
+ | RPCClientTransportOptions
77
311
  ) & {
78
312
  authProvider?: AgentMcpOAuthProvider;
79
313
  type?: TransportType;
@@ -91,7 +325,7 @@ declare class MCPClientConnection {
91
325
  url: URL;
92
326
  options: {
93
327
  transport: MCPTransportOptions;
94
- client: ConstructorParameters<typeof Client>[1];
328
+ client: McpClientOptions;
95
329
  };
96
330
  client: Client;
97
331
  connectionState: MCPConnectionState;
@@ -112,7 +346,7 @@ declare class MCPClientConnection {
112
346
  info: ConstructorParameters<typeof Client>[0],
113
347
  options?: {
114
348
  transport: MCPTransportOptions;
115
- client: ConstructorParameters<typeof Client>[1];
349
+ client: McpClientOptions;
116
350
  }
117
351
  );
118
352
  /**
@@ -320,7 +554,7 @@ declare class MCPClientConnection {
320
554
  */
321
555
  getTransport(
322
556
  transportType: BaseTransportType
323
- ): StreamableHTTPClientTransport | SSEClientTransport;
557
+ ): StreamableHTTPClientTransport | SSEClientTransport | RPCClientTransport;
324
558
  private tryConnect;
325
559
  private _capabilityErrorHandler;
326
560
  }
@@ -340,16 +574,31 @@ type MCPServerRow = {
340
574
  };
341
575
  //#endregion
342
576
  export {
343
- BaseTransportType as a,
344
- ServeOptions as c,
345
- Event as d,
577
+ ServeOptions as C,
578
+ Event as E,
579
+ McpClientOptions as S,
580
+ Emitter as T,
581
+ WorkerTransportOptions as _,
582
+ RPCClientTransport as a,
583
+ BaseTransportType as b,
584
+ RPCServerTransportOptions as c,
585
+ createMcpHandler as d,
586
+ experimental_createMcpHandler as f,
587
+ WorkerTransport as g,
588
+ TransportState as h,
346
589
  MCPTransportOptions as i,
347
- TransportType as l,
590
+ RPC_DO_PREFIX as l,
591
+ getMcpAuthContext as m,
348
592
  MCPClientConnection as n,
349
- CORSOptions as o,
593
+ RPCClientTransportOptions as o,
594
+ McpAuthContext as p,
350
595
  MCPConnectionState as r,
351
- MaybePromise as s,
596
+ RPCServerTransport as s,
352
597
  MCPServerRow as t,
353
- Emitter as u
598
+ CreateMcpHandlerOptions as u,
599
+ SSEEdgeClientTransport as v,
600
+ TransportType as w,
601
+ MaybePromise as x,
602
+ StreamableHTTPEdgeClientTransport as y
354
603
  };
355
- //# sourceMappingURL=client-storage-D633wI1S.d.ts.map
604
+ //# sourceMappingURL=client-storage-tusTuoSF.d.ts.map
@@ -1,5 +1,5 @@
1
1
  import { RetryOptions } from "../retries.js";
2
- import "../client-storage-D633wI1S.js";
2
+ import "../client-storage-tusTuoSF.js";
3
3
  import { Agent, Schedule } from "../index.js";
4
4
 
5
5
  //#region src/experimental/forever.d.ts
@@ -52,7 +52,7 @@ type RawFiberRow = {
52
52
  created_at: number;
53
53
  };
54
54
  type Constructor<T = object> = new (...args: any[]) => T;
55
- type AgentLike = Constructor<Pick<Agent<Cloudflare.Env>, "sql" | "scheduleEvery" | "cancelSchedule" | "alarm">>;
55
+ type AgentLike = Constructor<Pick<Agent<Cloudflare.Env>, "sql" | "scheduleEvery" | "cancelSchedule" | "alarm" | "keepAlive" | "keepAliveWhile">>;
56
56
  declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
57
57
  debugFibers?: boolean;
58
58
  }): {
@@ -61,8 +61,7 @@ declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
61
61
  _fiberRecoveryInProgress: boolean; /** @internal */
62
62
  _fiberLastCleanupTime: number; /** @internal */
63
63
  _fiberDebug(msg: string, ...args: unknown[]): void; /** @internal */
64
- _cf_fiberHeartbeat(): Promise<void>;
65
- keepAlive(): Promise<() => void>;
64
+ _cf_keepAliveHeartbeat(): Promise<void>;
66
65
  spawnFiber(methodName: keyof /*elided*/any, payload?: unknown, options?: {
67
66
  maxRetries?: number;
68
67
  }): string;
@@ -93,26 +92,16 @@ declare function withFibers<TBase extends AgentLike>(Base: TBase, options?: {
93
92
  _checkInterruptedFibers(): Promise<void>; /** @internal */
94
93
  _cleanupOrphanedHeartbeats(): void; /** @internal */
95
94
  _maybeCleanupFibers(): void;
95
+ readonly alarm: () => Promise<void>;
96
96
  sql: <T = Record<string, string | number | boolean | null>>(strings: TemplateStringsArray, ...values: (string | number | boolean | null)[]) => T[];
97
97
  scheduleEvery: <T = string>(intervalSeconds: number, callback: keyof Agent<Cloudflare.Env, unknown, Record<string, unknown>>, payload?: T | undefined, options?: {
98
98
  retry?: RetryOptions;
99
99
  }) => Promise<Schedule<T>>;
100
100
  cancelSchedule: (id: string) => Promise<boolean>;
101
- readonly alarm: () => Promise<void>;
101
+ keepAlive: () => Promise<() => void>;
102
+ keepAliveWhile: <T>(fn: () => Promise<T>) => Promise<T>;
102
103
  };
103
104
  } & TBase;
104
- /**
105
- * Keep a Durable Object alive via a scheduled heartbeat.
106
- * Returns a disposer function that cancels the heartbeat.
107
- *
108
- * Standalone version usable by any Agent subclass without requiring
109
- * the full fiber mixin. The agent must have a no-op method with the
110
- * given callbackName for the scheduler to invoke.
111
- *
112
- * @param agent - The agent instance (must have scheduleEvery and cancelSchedule)
113
- * @param callbackName - Name of a no-op method on the agent class (must exist)
114
- */
115
- declare function keepAlive(agent: Pick<Agent<Cloudflare.Env>, "scheduleEvery" | "cancelSchedule">, callbackName: string): Promise<() => void>;
116
105
  //#endregion
117
- export { FiberCompleteContext, FiberContext, FiberRecoveryContext, FiberState, keepAlive, withFibers };
106
+ export { FiberCompleteContext, FiberContext, FiberRecoveryContext, FiberState, withFibers };
118
107
  //# sourceMappingURL=forever.d.ts.map
@@ -33,8 +33,6 @@ import { nanoid } from "nanoid";
33
33
  *
34
34
  * @experimental This API is not yet stable and may change.
35
35
  */
36
- console.warn("[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.");
37
- const KEEP_ALIVE_INTERVAL_MS = 1e4;
38
36
  const FIBER_CLEANUP_INTERVAL_MS = 600 * 1e3;
39
37
  const FIBER_CLEANUP_COMPLETED_MS = 1440 * 60 * 1e3;
40
38
  const FIBER_CLEANUP_FAILED_MS = 10080 * 60 * 1e3;
@@ -47,6 +45,7 @@ function withFibers(Base, options) {
47
45
  this._fiberActiveFibers = /* @__PURE__ */ new Set();
48
46
  this._fiberRecoveryInProgress = false;
49
47
  this._fiberLastCleanupTime = 0;
48
+ console.warn("[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.");
50
49
  this.sql`
51
50
  CREATE TABLE IF NOT EXISTS cf_agents_fibers (
52
51
  id TEXT PRIMARY KEY NOT NULL,
@@ -69,21 +68,9 @@ function withFibers(Base, options) {
69
68
  /** @internal */ _fiberDebug(msg, ...args) {
70
69
  if (debugEnabled) console.debug(`[fiber] ${msg}`, ...args);
71
70
  }
72
- /** @internal */ async _cf_fiberHeartbeat() {
71
+ /** @internal */ async _cf_keepAliveHeartbeat() {
73
72
  await this._checkInterruptedFibers();
74
73
  }
75
- async keepAlive() {
76
- const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
77
- const schedule = await this.scheduleEvery(heartbeatSeconds, "_cf_fiberHeartbeat");
78
- this._fiberDebug("keepAlive started, schedule=%s", schedule.id);
79
- let disposed = false;
80
- return () => {
81
- if (disposed) return;
82
- disposed = true;
83
- this._fiberDebug("keepAlive disposed, schedule=%s", schedule.id);
84
- this.cancelSchedule(schedule.id);
85
- };
86
- }
87
74
  spawnFiber(methodName, payload, options) {
88
75
  this._maybeCleanupFibers();
89
76
  const name = methodName;
@@ -333,7 +320,7 @@ function withFibers(Base, options) {
333
320
  /** @internal */ _cleanupOrphanedHeartbeats() {
334
321
  this.sql`
335
322
  DELETE FROM cf_agents_schedules
336
- WHERE callback = '_cf_fiberHeartbeat'
323
+ WHERE callback = '_cf_keepAliveHeartbeat'
337
324
  `;
338
325
  this._fiberDebug("cleaned up orphaned heartbeat schedules");
339
326
  }
@@ -354,28 +341,7 @@ function withFibers(Base, options) {
354
341
  }
355
342
  return FiberAgent;
356
343
  }
357
- /**
358
- * Keep a Durable Object alive via a scheduled heartbeat.
359
- * Returns a disposer function that cancels the heartbeat.
360
- *
361
- * Standalone version usable by any Agent subclass without requiring
362
- * the full fiber mixin. The agent must have a no-op method with the
363
- * given callbackName for the scheduler to invoke.
364
- *
365
- * @param agent - The agent instance (must have scheduleEvery and cancelSchedule)
366
- * @param callbackName - Name of a no-op method on the agent class (must exist)
367
- */
368
- async function keepAlive(agent, callbackName) {
369
- const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1e3);
370
- const schedule = await agent.scheduleEvery(heartbeatSeconds, callbackName);
371
- let disposed = false;
372
- return () => {
373
- if (disposed) return;
374
- disposed = true;
375
- agent.cancelSchedule(schedule.id);
376
- };
377
- }
378
344
 
379
345
  //#endregion
380
- export { keepAlive, withFibers };
346
+ export { withFibers };
381
347
  //# sourceMappingURL=forever.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"forever.js","names":[],"sources":["../../src/experimental/forever.ts"],"sourcesContent":["/**\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n * !! WARNING: EXPERIMENTAL — DO NOT USE IN PRODUCTION !!\n * !! !!\n * !! This API is under active development and WILL break between !!\n * !! releases. Method names, types, behavior, and the mixin signature !!\n * !! are all subject to change without notice. !!\n * !! !!\n * !! If you use this, pin your agents version and expect to rewrite !!\n * !! your code when upgrading. !!\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n *\n * Experimental fiber mixin for durable long-running execution.\n *\n * Usage:\n * import { Agent } from \"agents\";\n * import { withFibers } from \"agents/experimental/forever\";\n *\n * class MyAgent extends withFibers(Agent)<Env, State> {\n * async doWork(payload, fiberCtx) { ... }\n * }\n *\n * This mixin adds:\n * - keepAlive() — keep the DO alive via scheduled heartbeats\n * - spawnFiber() — fire-and-forget durable execution\n * - stashFiber() — checkpoint progress that survives eviction\n * - cancelFiber() / getFiber() — manage running fibers\n * - onFiberComplete / onFiberRecovered / onFibersRecovered — lifecycle hooks\n *\n * @experimental This API is not yet stable and may change.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { nanoid } from \"nanoid\";\nimport type { Agent } from \"../index\";\n\nconsole.warn(\n \"[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.\"\n);\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport type FiberState = {\n id: string;\n callback: string;\n payload: unknown;\n snapshot: unknown | null;\n status: \"running\" | \"completed\" | \"failed\" | \"interrupted\" | \"cancelled\";\n retryCount: number;\n maxRetries: number;\n result: unknown | null;\n error: string | null;\n startedAt: number | null;\n updatedAt: number | null;\n completedAt: number | null;\n createdAt: number;\n};\n\nexport type FiberRecoveryContext = {\n id: string;\n methodName: string;\n payload: unknown;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberContext = {\n id: string;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberCompleteContext = {\n id: string;\n methodName: string;\n payload: unknown;\n result: unknown;\n};\n\n// ── Internal types ────────────────────────────────────────────────────\n\ntype RawFiberRow = {\n id: string;\n callback: string;\n payload: string | null;\n snapshot: string | null;\n status: string;\n retry_count: number;\n max_retries: number;\n result: string | null;\n error: string | null;\n started_at: number | null;\n updated_at: number | null;\n completed_at: number | null;\n created_at: number;\n};\n\n// ── Constants ─────────────────────────────────────────────────────────\n\nconst KEEP_ALIVE_INTERVAL_MS = 10_000;\nconst FIBER_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;\nconst FIBER_CLEANUP_COMPLETED_MS = 24 * 60 * 60 * 1000;\nconst FIBER_CLEANUP_FAILED_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst fiberContext = new AsyncLocalStorage<{ fiberId: string }>();\n\n// ── Mixin ─────────────────────────────────────────────────────────────\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor constraint\ntype Constructor<T = object> = new (...args: any[]) => T;\n\ntype AgentLike = Constructor<\n Pick<\n Agent<Cloudflare.Env>,\n \"sql\" | \"scheduleEvery\" | \"cancelSchedule\" | \"alarm\"\n >\n>;\n\nexport function withFibers<TBase extends AgentLike>(\n Base: TBase,\n options?: { debugFibers?: boolean }\n) {\n const debugEnabled = options?.debugFibers ?? false;\n\n class FiberAgent extends Base {\n // ── Fiber state ───────────────────────────────────────────────\n\n /** @internal */ _fiberActiveFibers = new Set<string>();\n /** @internal */ _fiberRecoveryInProgress = false;\n /** @internal */ _fiberLastCleanupTime = 0;\n\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor\n constructor(...args: any[]) {\n super(...args);\n\n // Create the fibers table\n (this as unknown as Agent<Cloudflare.Env>).sql`\n CREATE TABLE IF NOT EXISTS cf_agents_fibers (\n id TEXT PRIMARY KEY NOT NULL,\n callback TEXT NOT NULL,\n payload TEXT,\n snapshot TEXT,\n status TEXT NOT NULL DEFAULT 'running'\n CHECK(status IN ('running', 'completed', 'failed', 'interrupted', 'cancelled')),\n retry_count INTEGER NOT NULL DEFAULT 0,\n max_retries INTEGER NOT NULL DEFAULT 3,\n result TEXT,\n error TEXT,\n started_at INTEGER,\n updated_at INTEGER,\n completed_at INTEGER,\n created_at INTEGER NOT NULL\n )\n `;\n }\n\n // ── Debug logging ─────────────────────────────────────────────\n\n /** @internal */ _fiberDebug(msg: string, ...args: unknown[]) {\n if (debugEnabled) {\n console.debug(`[fiber] ${msg}`, ...args);\n }\n }\n\n // ── Heartbeat callback ────────────────────────────────────────\n\n // Note: TypeScript `private` is compile-time only. The scheduler\n // dispatches callbacks by string name (`this[row.callback]`),\n // which works at runtime. The name is stable (stored in SQLite).\n /** @internal */ async _cf_fiberHeartbeat() {\n await this._checkInterruptedFibers();\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n async keepAlive(): Promise<() => void> {\n const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1000);\n const schedule = await (\n this as unknown as Agent<Cloudflare.Env>\n ).scheduleEvery(\n heartbeatSeconds,\n \"_cf_fiberHeartbeat\" as keyof Agent<Cloudflare.Env>\n );\n\n this._fiberDebug(\"keepAlive started, schedule=%s\", schedule.id);\n\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n this._fiberDebug(\"keepAlive disposed, schedule=%s\", schedule.id);\n void this.cancelSchedule(schedule.id);\n };\n }\n\n spawnFiber(\n methodName: keyof this,\n payload?: unknown,\n options?: { maxRetries?: number }\n ): string {\n this._maybeCleanupFibers();\n\n const name = methodName as string;\n if (typeof this[methodName] !== \"function\") {\n throw new Error(`this.${name} is not a function`);\n }\n\n const id = nanoid();\n const now = Date.now();\n const maxRetries = options?.maxRetries ?? 3;\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n INSERT INTO cf_agents_fibers (id, callback, payload, status, max_retries, retry_count, started_at, updated_at, created_at)\n VALUES (${id}, ${name}, ${JSON.stringify(payload ?? null)}, 'running', ${maxRetries}, 0, ${now}, ${now}, ${now})\n `;\n\n this._fiberActiveFibers.add(id);\n this._fiberDebug(\n \"spawned fiber=%s method=%s maxRetries=%d\",\n id,\n name,\n maxRetries\n );\n\n void this._startFiber(id, name, payload, maxRetries).catch((e) => {\n console.error(`Unhandled error in fiber ${id}:`, e);\n });\n\n return id;\n }\n\n stashFiber(data: unknown): void {\n const ctx = fiberContext.getStore();\n if (!ctx) {\n throw new Error(\n \"stashFiber() can only be called within a fiber execution context\"\n );\n }\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET snapshot = ${JSON.stringify(data)}, updated_at = ${now}\n WHERE id = ${ctx.fiberId}\n `;\n this._fiberDebug(\"stash fiber=%s\", ctx.fiberId);\n }\n\n /**\n * Note: cancellation is cooperative. The status is set to 'cancelled'\n * in SQLite, and the _runFiber retry loop checks for this status at\n * the top of each iteration.\n */\n cancelFiber(fiberId: string): boolean {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) return false;\n if (\n fiber.status === \"completed\" ||\n fiber.status === \"failed\" ||\n fiber.status === \"cancelled\"\n ) {\n return false;\n }\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'cancelled', updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n this._fiberActiveFibers.delete(fiberId);\n this._fiberDebug(\"cancelled fiber=%s\", fiberId);\n return true;\n }\n\n getFiber(fiberId: string): FiberState | null {\n const raw = this._getRawFiber(fiberId);\n if (!raw) return null;\n return this._toFiberState(raw);\n }\n\n restartFiber(fiberId: string): void {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) {\n throw new Error(`Fiber ${fiberId} not found`);\n }\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'running', started_at = ${now}, updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n\n this._fiberActiveFibers.add(fiberId);\n this._fiberDebug(\n \"restarting fiber=%s method=%s retryCount=%d\",\n fiberId,\n fiber.callback,\n fiber.retry_count\n );\n\n const parsedPayload = fiber.payload\n ? JSON.parse(fiber.payload)\n : undefined;\n\n void this._startFiber(\n fiberId,\n fiber.callback,\n parsedPayload,\n fiber.max_retries\n ).catch((e) => {\n console.error(`Error restarting fiber ${fiberId}:`, e);\n });\n }\n\n // ── Lifecycle hooks (override in subclass) ────────────────────\n\n /**\n * Manually trigger fiber recovery check.\n * In production, this runs automatically via the heartbeat schedule.\n * Useful for testing or when you need immediate recovery after\n * detecting an eviction.\n */\n async checkFibers(): Promise<void> {\n await this._checkInterruptedFibers();\n }\n\n // oxlint-disable-next-line @typescript-eslint/no-unused-vars -- overridable hook\n onFiberComplete(_ctx: FiberCompleteContext): void | Promise<void> {}\n\n onFiberRecovered(ctx: FiberRecoveryContext): void | Promise<void> {\n this.restartFiber(ctx.id);\n }\n\n async onFibersRecovered(fibers: FiberRecoveryContext[]): Promise<void> {\n for (const fiber of fibers) {\n await this.onFiberRecovered(fiber);\n }\n }\n\n // ── Private implementation ────────────────────────────────────\n\n /** @internal */ _getRawFiber(fiberId: string): RawFiberRow | null {\n const result = (this as unknown as Agent<Cloudflare.Env>)\n .sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers WHERE id = ${fiberId}\n `;\n return result && result.length > 0 ? result[0] : null;\n }\n\n /** @internal */ _safeJsonParse(value: string | null): unknown {\n if (value === null) return null;\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n }\n\n /** @internal */ _toFiberState(raw: RawFiberRow): FiberState {\n return {\n id: raw.id,\n callback: raw.callback,\n payload: this._safeJsonParse(raw.payload),\n snapshot: this._safeJsonParse(raw.snapshot),\n status: raw.status as FiberState[\"status\"],\n retryCount: raw.retry_count,\n maxRetries: raw.max_retries,\n result: this._safeJsonParse(raw.result),\n error: raw.error,\n startedAt: raw.started_at,\n updatedAt: raw.updated_at,\n completedAt: raw.completed_at,\n createdAt: raw.created_at\n };\n }\n\n /** @internal */ async _startFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number\n ): Promise<void> {\n const disposeKeepAlive = await this.keepAlive();\n await this._runFiber(\n id,\n methodName,\n payload,\n maxRetries,\n disposeKeepAlive\n );\n }\n\n /** @internal */ async _runFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number,\n disposeKeepAlive: () => void\n ): Promise<void> {\n try {\n while (true) {\n const fiber = this._getRawFiber(id);\n if (!fiber || fiber.status === \"cancelled\") {\n this._fiberDebug(\n \"fiber=%s exiting: %s\",\n id,\n !fiber ? \"not found\" : \"cancelled\"\n );\n return;\n }\n\n try {\n await fiberContext.run({ fiberId: id }, async () => {\n const snapshot = this._safeJsonParse(fiber.snapshot);\n const retryCount = fiber.retry_count;\n\n const callback = this[methodName as keyof this];\n if (typeof callback !== \"function\") {\n throw new Error(`Fiber method ${methodName} not found`);\n }\n\n const result = await (\n callback as (p: unknown, ctx: FiberContext) => Promise<unknown>\n ).call(this, payload, { id, snapshot, retryCount });\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'completed',\n result = ${JSON.stringify(result ?? null)},\n completed_at = ${now},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n\n this._fiberDebug(\"fiber=%s completed method=%s\", id, methodName);\n\n try {\n await this.onFiberComplete({\n id,\n methodName,\n payload,\n result\n });\n } catch (e) {\n console.error(\"Error in onFiberComplete:\", e);\n }\n });\n\n return;\n } catch (e) {\n const now = Date.now();\n const currentFiber = this._getRawFiber(id);\n const newRetryCount = (currentFiber?.retry_count ?? 0) + 1;\n\n if (newRetryCount > maxRetries) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = ${errorMsg},\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s failed after %d retries: %s\",\n id,\n newRetryCount,\n errorMsg\n );\n return;\n }\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET retry_count = ${newRetryCount}, updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s retrying (%d/%d)\",\n id,\n newRetryCount,\n maxRetries\n );\n continue;\n }\n }\n } finally {\n this._fiberActiveFibers.delete(id);\n disposeKeepAlive();\n }\n }\n\n /** @internal */ async _checkInterruptedFibers(): Promise<void> {\n if (this._fiberRecoveryInProgress) return;\n this._fiberRecoveryInProgress = true;\n\n try {\n const runningFibers = (this as unknown as Agent<Cloudflare.Env>)\n .sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers\n WHERE status = 'running'\n ORDER BY created_at ASC\n `;\n\n if (!runningFibers || runningFibers.length === 0) return;\n\n const interrupted: FiberRecoveryContext[] = [];\n\n for (const fiber of runningFibers) {\n if (this._fiberActiveFibers.has(fiber.id)) continue;\n\n const newRetryCount = fiber.retry_count + 1;\n const now = Date.now();\n\n if (newRetryCount > fiber.max_retries) {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = 'max retries exceeded (eviction recovery)',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n this._fiberDebug(\n \"fiber=%s max retries exceeded on recovery\",\n fiber.id\n );\n } else {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'interrupted',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n\n interrupted.push({\n id: fiber.id,\n methodName: fiber.callback,\n payload: this._safeJsonParse(fiber.payload),\n snapshot: this._safeJsonParse(fiber.snapshot),\n retryCount: newRetryCount\n });\n }\n }\n\n if (interrupted.length > 0) {\n this._fiberDebug(\n \"recovering %d interrupted fibers\",\n interrupted.length\n );\n\n this._cleanupOrphanedHeartbeats();\n\n try {\n await this.onFibersRecovered(interrupted);\n } catch (e) {\n console.error(\"Error in onFibersRecovered:\", e);\n }\n }\n } finally {\n this._fiberRecoveryInProgress = false;\n }\n }\n\n /** @internal */ _cleanupOrphanedHeartbeats() {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n DELETE FROM cf_agents_schedules\n WHERE callback = '_cf_fiberHeartbeat'\n `;\n this._fiberDebug(\"cleaned up orphaned heartbeat schedules\");\n }\n\n /** @internal */ _maybeCleanupFibers() {\n const now = Date.now();\n if (now - this._fiberLastCleanupTime < FIBER_CLEANUP_INTERVAL_MS) {\n return;\n }\n this._fiberLastCleanupTime = now;\n\n const completedCutoff = now - FIBER_CLEANUP_COMPLETED_MS;\n const failedCutoff = now - FIBER_CLEANUP_FAILED_MS;\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n DELETE FROM cf_agents_fibers\n WHERE (status = 'completed' AND completed_at < ${completedCutoff})\n OR (status = 'failed' AND updated_at < ${failedCutoff})\n OR (status = 'cancelled' AND updated_at < ${completedCutoff})\n `;\n\n this._fiberDebug(\n \"cleanup: checked for old completed/failed/cancelled fibers\"\n );\n }\n }\n\n return FiberAgent;\n}\n\n// ── Standalone keepAlive ──────────────────────────────────────────────\n\n/**\n * Keep a Durable Object alive via a scheduled heartbeat.\n * Returns a disposer function that cancels the heartbeat.\n *\n * Standalone version usable by any Agent subclass without requiring\n * the full fiber mixin. The agent must have a no-op method with the\n * given callbackName for the scheduler to invoke.\n *\n * @param agent - The agent instance (must have scheduleEvery and cancelSchedule)\n * @param callbackName - Name of a no-op method on the agent class (must exist)\n */\nexport async function keepAlive(\n agent: Pick<Agent<Cloudflare.Env>, \"scheduleEvery\" | \"cancelSchedule\">,\n callbackName: string\n): Promise<() => void> {\n const heartbeatSeconds = Math.ceil(KEEP_ALIVE_INTERVAL_MS / 1000);\n const schedule = await agent.scheduleEvery(\n heartbeatSeconds,\n callbackName as keyof Agent<Cloudflare.Env>\n );\n\n let disposed = false;\n return () => {\n if (disposed) return;\n disposed = true;\n void agent.cancelSchedule(schedule.id);\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"forever.js","names":[],"sources":["../../src/experimental/forever.ts"],"sourcesContent":["/**\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n * !! WARNING: EXPERIMENTAL — DO NOT USE IN PRODUCTION !!\n * !! !!\n * !! This API is under active development and WILL break between !!\n * !! releases. Method names, types, behavior, and the mixin signature !!\n * !! are all subject to change without notice. !!\n * !! !!\n * !! If you use this, pin your agents version and expect to rewrite !!\n * !! your code when upgrading. !!\n * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n *\n * Experimental fiber mixin for durable long-running execution.\n *\n * Usage:\n * import { Agent } from \"agents\";\n * import { withFibers } from \"agents/experimental/forever\";\n *\n * class MyAgent extends withFibers(Agent)<Env, State> {\n * async doWork(payload, fiberCtx) { ... }\n * }\n *\n * This mixin adds:\n * - keepAlive() — keep the DO alive via scheduled heartbeats\n * - spawnFiber() — fire-and-forget durable execution\n * - stashFiber() — checkpoint progress that survives eviction\n * - cancelFiber() / getFiber() — manage running fibers\n * - onFiberComplete / onFiberRecovered / onFibersRecovered — lifecycle hooks\n *\n * @experimental This API is not yet stable and may change.\n */\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport { nanoid } from \"nanoid\";\nimport type { Agent } from \"../index\";\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport type FiberState = {\n id: string;\n callback: string;\n payload: unknown;\n snapshot: unknown | null;\n status: \"running\" | \"completed\" | \"failed\" | \"interrupted\" | \"cancelled\";\n retryCount: number;\n maxRetries: number;\n result: unknown | null;\n error: string | null;\n startedAt: number | null;\n updatedAt: number | null;\n completedAt: number | null;\n createdAt: number;\n};\n\nexport type FiberRecoveryContext = {\n id: string;\n methodName: string;\n payload: unknown;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberContext = {\n id: string;\n snapshot: unknown | null;\n retryCount: number;\n};\n\nexport type FiberCompleteContext = {\n id: string;\n methodName: string;\n payload: unknown;\n result: unknown;\n};\n\n// ── Internal types ────────────────────────────────────────────────────\n\ntype RawFiberRow = {\n id: string;\n callback: string;\n payload: string | null;\n snapshot: string | null;\n status: string;\n retry_count: number;\n max_retries: number;\n result: string | null;\n error: string | null;\n started_at: number | null;\n updated_at: number | null;\n completed_at: number | null;\n created_at: number;\n};\n\n// ── Constants ─────────────────────────────────────────────────────────\n\nconst FIBER_CLEANUP_INTERVAL_MS = 10 * 60 * 1000;\nconst FIBER_CLEANUP_COMPLETED_MS = 24 * 60 * 60 * 1000;\nconst FIBER_CLEANUP_FAILED_MS = 7 * 24 * 60 * 60 * 1000;\n\nconst fiberContext = new AsyncLocalStorage<{ fiberId: string }>();\n\n// ── Mixin ─────────────────────────────────────────────────────────────\n\n// oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor constraint\ntype Constructor<T = object> = new (...args: any[]) => T;\n\ntype AgentLike = Constructor<\n Pick<\n Agent<Cloudflare.Env>,\n | \"sql\"\n | \"scheduleEvery\"\n | \"cancelSchedule\"\n | \"alarm\"\n | \"keepAlive\"\n | \"keepAliveWhile\"\n >\n>;\n\nexport function withFibers<TBase extends AgentLike>(\n Base: TBase,\n options?: { debugFibers?: boolean }\n) {\n const debugEnabled = options?.debugFibers ?? false;\n\n class FiberAgent extends Base {\n // ── Fiber state ───────────────────────────────────────────────\n\n /** @internal */ _fiberActiveFibers = new Set<string>();\n /** @internal */ _fiberRecoveryInProgress = false;\n /** @internal */ _fiberLastCleanupTime = 0;\n\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any -- mixin constructor\n constructor(...args: any[]) {\n super(...args);\n\n console.warn(\n \"[agents/experimental/forever] WARNING: You are using an experimental API that WILL break between releases. Do not use in production.\"\n );\n\n // Create the fibers table\n (this as unknown as Agent<Cloudflare.Env>).sql`\n CREATE TABLE IF NOT EXISTS cf_agents_fibers (\n id TEXT PRIMARY KEY NOT NULL,\n callback TEXT NOT NULL,\n payload TEXT,\n snapshot TEXT,\n status TEXT NOT NULL DEFAULT 'running'\n CHECK(status IN ('running', 'completed', 'failed', 'interrupted', 'cancelled')),\n retry_count INTEGER NOT NULL DEFAULT 0,\n max_retries INTEGER NOT NULL DEFAULT 3,\n result TEXT,\n error TEXT,\n started_at INTEGER,\n updated_at INTEGER,\n completed_at INTEGER,\n created_at INTEGER NOT NULL\n )\n `;\n }\n\n // ── Debug logging ─────────────────────────────────────────────\n\n /** @internal */ _fiberDebug(msg: string, ...args: unknown[]) {\n if (debugEnabled) {\n console.debug(`[fiber] ${msg}`, ...args);\n }\n }\n\n // ── Heartbeat callback override ───────────────────────────────\n\n // Override the base Agent's no-op heartbeat to add fiber recovery.\n // The scheduler dispatches by string name, so this override runs\n // when the keepAlive schedule fires.\n /** @internal */ async _cf_keepAliveHeartbeat() {\n await this._checkInterruptedFibers();\n }\n\n // ── Public API ────────────────────────────────────────────────\n\n spawnFiber(\n methodName: keyof this,\n payload?: unknown,\n options?: { maxRetries?: number }\n ): string {\n this._maybeCleanupFibers();\n\n const name = methodName as string;\n if (typeof this[methodName] !== \"function\") {\n throw new Error(`this.${name} is not a function`);\n }\n\n const id = nanoid();\n const now = Date.now();\n const maxRetries = options?.maxRetries ?? 3;\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n INSERT INTO cf_agents_fibers (id, callback, payload, status, max_retries, retry_count, started_at, updated_at, created_at)\n VALUES (${id}, ${name}, ${JSON.stringify(payload ?? null)}, 'running', ${maxRetries}, 0, ${now}, ${now}, ${now})\n `;\n\n this._fiberActiveFibers.add(id);\n this._fiberDebug(\n \"spawned fiber=%s method=%s maxRetries=%d\",\n id,\n name,\n maxRetries\n );\n\n void this._startFiber(id, name, payload, maxRetries).catch((e) => {\n console.error(`Unhandled error in fiber ${id}:`, e);\n });\n\n return id;\n }\n\n stashFiber(data: unknown): void {\n const ctx = fiberContext.getStore();\n if (!ctx) {\n throw new Error(\n \"stashFiber() can only be called within a fiber execution context\"\n );\n }\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET snapshot = ${JSON.stringify(data)}, updated_at = ${now}\n WHERE id = ${ctx.fiberId}\n `;\n this._fiberDebug(\"stash fiber=%s\", ctx.fiberId);\n }\n\n /**\n * Note: cancellation is cooperative. The status is set to 'cancelled'\n * in SQLite, and the _runFiber retry loop checks for this status at\n * the top of each iteration.\n */\n cancelFiber(fiberId: string): boolean {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) return false;\n if (\n fiber.status === \"completed\" ||\n fiber.status === \"failed\" ||\n fiber.status === \"cancelled\"\n ) {\n return false;\n }\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'cancelled', updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n this._fiberActiveFibers.delete(fiberId);\n this._fiberDebug(\"cancelled fiber=%s\", fiberId);\n return true;\n }\n\n getFiber(fiberId: string): FiberState | null {\n const raw = this._getRawFiber(fiberId);\n if (!raw) return null;\n return this._toFiberState(raw);\n }\n\n restartFiber(fiberId: string): void {\n const fiber = this._getRawFiber(fiberId);\n if (!fiber) {\n throw new Error(`Fiber ${fiberId} not found`);\n }\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'running', started_at = ${now}, updated_at = ${now}\n WHERE id = ${fiberId}\n `;\n\n this._fiberActiveFibers.add(fiberId);\n this._fiberDebug(\n \"restarting fiber=%s method=%s retryCount=%d\",\n fiberId,\n fiber.callback,\n fiber.retry_count\n );\n\n const parsedPayload = fiber.payload\n ? JSON.parse(fiber.payload)\n : undefined;\n\n void this._startFiber(\n fiberId,\n fiber.callback,\n parsedPayload,\n fiber.max_retries\n ).catch((e) => {\n console.error(`Error restarting fiber ${fiberId}:`, e);\n });\n }\n\n // ── Lifecycle hooks (override in subclass) ────────────────────\n\n /**\n * Manually trigger fiber recovery check.\n * In production, this runs automatically via the heartbeat schedule.\n * Useful for testing or when you need immediate recovery after\n * detecting an eviction.\n */\n async checkFibers(): Promise<void> {\n await this._checkInterruptedFibers();\n }\n\n // oxlint-disable-next-line @typescript-eslint/no-unused-vars -- overridable hook\n onFiberComplete(_ctx: FiberCompleteContext): void | Promise<void> {}\n\n onFiberRecovered(ctx: FiberRecoveryContext): void | Promise<void> {\n this.restartFiber(ctx.id);\n }\n\n async onFibersRecovered(fibers: FiberRecoveryContext[]): Promise<void> {\n for (const fiber of fibers) {\n await this.onFiberRecovered(fiber);\n }\n }\n\n // ── Private implementation ────────────────────────────────────\n\n /** @internal */ _getRawFiber(fiberId: string): RawFiberRow | null {\n const result = (this as unknown as Agent<Cloudflare.Env>)\n .sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers WHERE id = ${fiberId}\n `;\n return result && result.length > 0 ? result[0] : null;\n }\n\n /** @internal */ _safeJsonParse(value: string | null): unknown {\n if (value === null) return null;\n try {\n return JSON.parse(value);\n } catch {\n return null;\n }\n }\n\n /** @internal */ _toFiberState(raw: RawFiberRow): FiberState {\n return {\n id: raw.id,\n callback: raw.callback,\n payload: this._safeJsonParse(raw.payload),\n snapshot: this._safeJsonParse(raw.snapshot),\n status: raw.status as FiberState[\"status\"],\n retryCount: raw.retry_count,\n maxRetries: raw.max_retries,\n result: this._safeJsonParse(raw.result),\n error: raw.error,\n startedAt: raw.started_at,\n updatedAt: raw.updated_at,\n completedAt: raw.completed_at,\n createdAt: raw.created_at\n };\n }\n\n /** @internal */ async _startFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number\n ): Promise<void> {\n const disposeKeepAlive = await this.keepAlive();\n await this._runFiber(\n id,\n methodName,\n payload,\n maxRetries,\n disposeKeepAlive\n );\n }\n\n /** @internal */ async _runFiber(\n id: string,\n methodName: string,\n payload: unknown,\n maxRetries: number,\n disposeKeepAlive: () => void\n ): Promise<void> {\n try {\n while (true) {\n const fiber = this._getRawFiber(id);\n if (!fiber || fiber.status === \"cancelled\") {\n this._fiberDebug(\n \"fiber=%s exiting: %s\",\n id,\n !fiber ? \"not found\" : \"cancelled\"\n );\n return;\n }\n\n try {\n await fiberContext.run({ fiberId: id }, async () => {\n const snapshot = this._safeJsonParse(fiber.snapshot);\n const retryCount = fiber.retry_count;\n\n const callback = this[methodName as keyof this];\n if (typeof callback !== \"function\") {\n throw new Error(`Fiber method ${methodName} not found`);\n }\n\n const result = await (\n callback as (p: unknown, ctx: FiberContext) => Promise<unknown>\n ).call(this, payload, { id, snapshot, retryCount });\n\n const now = Date.now();\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'completed',\n result = ${JSON.stringify(result ?? null)},\n completed_at = ${now},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n\n this._fiberDebug(\"fiber=%s completed method=%s\", id, methodName);\n\n try {\n await this.onFiberComplete({\n id,\n methodName,\n payload,\n result\n });\n } catch (e) {\n console.error(\"Error in onFiberComplete:\", e);\n }\n });\n\n return;\n } catch (e) {\n const now = Date.now();\n const currentFiber = this._getRawFiber(id);\n const newRetryCount = (currentFiber?.retry_count ?? 0) + 1;\n\n if (newRetryCount > maxRetries) {\n const errorMsg = e instanceof Error ? e.message : String(e);\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = ${errorMsg},\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s failed after %d retries: %s\",\n id,\n newRetryCount,\n errorMsg\n );\n return;\n }\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET retry_count = ${newRetryCount}, updated_at = ${now}\n WHERE id = ${id}\n `;\n this._fiberDebug(\n \"fiber=%s retrying (%d/%d)\",\n id,\n newRetryCount,\n maxRetries\n );\n continue;\n }\n }\n } finally {\n this._fiberActiveFibers.delete(id);\n disposeKeepAlive();\n }\n }\n\n /** @internal */ async _checkInterruptedFibers(): Promise<void> {\n if (this._fiberRecoveryInProgress) return;\n this._fiberRecoveryInProgress = true;\n\n try {\n const runningFibers = (this as unknown as Agent<Cloudflare.Env>)\n .sql<RawFiberRow>`\n SELECT * FROM cf_agents_fibers\n WHERE status = 'running'\n ORDER BY created_at ASC\n `;\n\n if (!runningFibers || runningFibers.length === 0) return;\n\n const interrupted: FiberRecoveryContext[] = [];\n\n for (const fiber of runningFibers) {\n if (this._fiberActiveFibers.has(fiber.id)) continue;\n\n const newRetryCount = fiber.retry_count + 1;\n const now = Date.now();\n\n if (newRetryCount > fiber.max_retries) {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'failed',\n error = 'max retries exceeded (eviction recovery)',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n this._fiberDebug(\n \"fiber=%s max retries exceeded on recovery\",\n fiber.id\n );\n } else {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n UPDATE cf_agents_fibers\n SET status = 'interrupted',\n retry_count = ${newRetryCount},\n updated_at = ${now}\n WHERE id = ${fiber.id}\n `;\n\n interrupted.push({\n id: fiber.id,\n methodName: fiber.callback,\n payload: this._safeJsonParse(fiber.payload),\n snapshot: this._safeJsonParse(fiber.snapshot),\n retryCount: newRetryCount\n });\n }\n }\n\n if (interrupted.length > 0) {\n this._fiberDebug(\n \"recovering %d interrupted fibers\",\n interrupted.length\n );\n\n this._cleanupOrphanedHeartbeats();\n\n try {\n await this.onFibersRecovered(interrupted);\n } catch (e) {\n console.error(\"Error in onFibersRecovered:\", e);\n }\n }\n } finally {\n this._fiberRecoveryInProgress = false;\n }\n }\n\n /** @internal */ _cleanupOrphanedHeartbeats() {\n (this as unknown as Agent<Cloudflare.Env>).sql`\n DELETE FROM cf_agents_schedules\n WHERE callback = '_cf_keepAliveHeartbeat'\n `;\n this._fiberDebug(\"cleaned up orphaned heartbeat schedules\");\n }\n\n /** @internal */ _maybeCleanupFibers() {\n const now = Date.now();\n if (now - this._fiberLastCleanupTime < FIBER_CLEANUP_INTERVAL_MS) {\n return;\n }\n this._fiberLastCleanupTime = now;\n\n const completedCutoff = now - FIBER_CLEANUP_COMPLETED_MS;\n const failedCutoff = now - FIBER_CLEANUP_FAILED_MS;\n\n (this as unknown as Agent<Cloudflare.Env>).sql`\n DELETE FROM cf_agents_fibers\n WHERE (status = 'completed' AND completed_at < ${completedCutoff})\n OR (status = 'failed' AND updated_at < ${failedCutoff})\n OR (status = 'cancelled' AND updated_at < ${completedCutoff})\n `;\n\n this._fiberDebug(\n \"cleanup: checked for old completed/failed/cancelled fibers\"\n );\n }\n }\n\n return FiberAgent;\n}\n"],"mappings":"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"}