agents 0.21.0 → 0.23.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.
- package/README.md +21 -1
- package/dist/{agent-tool-types-CzGGB-20.d.ts → agent-routing-DE5zmCQ8.d.ts} +1844 -1336
- package/dist/agent-routing.d.ts +14 -0
- package/dist/agent-routing.js +187 -0
- package/dist/agent-routing.js.map +1 -0
- package/dist/agent-tool-types.d.ts +26 -26
- package/dist/{agent-tools-zR2d5uij.d.ts → agent-tools-DtXMTDGM.d.ts} +7 -7
- package/dist/agent-tools.d.ts +21 -21
- package/dist/agent-tools.js +2 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/browser/ai.js +6 -2
- package/dist/browser/ai.js.map +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/callable-decorator-DP__HhBA.d.ts +72 -0
- package/dist/callable-decorator.d.ts +18 -0
- package/dist/callable-decorator.js +71 -0
- package/dist/callable-decorator.js.map +1 -0
- package/dist/capability-BjSKYpzg.js +42 -0
- package/dist/capability-BjSKYpzg.js.map +1 -0
- package/dist/capability-runner-Be_-PLR1.d.ts +459 -0
- package/dist/channel-Bnm4S7T2.d.ts +491 -0
- package/dist/channels/ai-sdk.d.ts +46 -0
- package/dist/channels/ai-sdk.js +120 -0
- package/dist/channels/ai-sdk.js.map +1 -0
- package/dist/channels/email.d.ts +95 -0
- package/dist/channels/email.js +323 -0
- package/dist/channels/email.js.map +1 -0
- package/dist/channels/index.d.ts +233 -0
- package/dist/channels/index.js +608 -0
- package/dist/channels/index.js.map +1 -0
- package/dist/channels/slack.d.ts +140 -0
- package/dist/channels/slack.js +614 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/tanstack-ai.d.ts +39 -0
- package/dist/channels/tanstack-ai.js +17 -0
- package/dist/channels/tanstack-ai.js.map +1 -0
- package/dist/channels/telegram.d.ts +106 -0
- package/dist/channels/telegram.js +427 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/voice.d.ts +45 -0
- package/dist/channels/voice.js +122 -0
- package/dist/channels/voice.js.map +1 -0
- package/dist/chat/index.d.ts +2328 -2015
- package/dist/chat/index.js +891 -521
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/react.d.ts +14 -1
- package/dist/chat/react.js +82 -52
- package/dist/chat/react.js.map +1 -1
- package/dist/chat/transport.js +1 -1
- package/dist/chat-sdk/index.d.ts +7 -7
- package/dist/chat-sdk/index.js +1 -1
- package/dist/{client-zqKcsyFa.js → client-jagG8a9_.js} +129 -37
- package/dist/client-jagG8a9_.js.map +1 -0
- package/dist/client.d.ts +1 -1
- package/dist/client.js +1 -1
- package/dist/{cloudflare-BduZwmYK.js → cloudflare-Dzvc7V2N.js} +10 -3
- package/dist/{cloudflare-BduZwmYK.js.map → cloudflare-Dzvc7V2N.js.map} +1 -1
- package/dist/context/index.d.ts +216 -0
- package/dist/context/index.js +454 -0
- package/dist/context/index.js.map +1 -0
- package/dist/current-agent-Da_C9a3b.d.ts +266 -0
- package/dist/current-agent-DhoDkSnH.js +51 -0
- package/dist/current-agent-DhoDkSnH.js.map +1 -0
- package/dist/diagnostics-BzvaX2UT.js +45 -0
- package/dist/diagnostics-BzvaX2UT.js.map +1 -0
- package/dist/diagnostics-C4jcz3VK.js +360 -0
- package/dist/diagnostics-C4jcz3VK.js.map +1 -0
- package/dist/{do-oauth-client-provider-VTZj2VtM.d.ts → do-oauth-client-provider-Tmf1vgKz.d.ts} +2 -2
- package/dist/{email-CL27preh.d.ts → email-7TatiTnl.d.ts} +38 -9
- package/dist/email-send.d.ts +15 -0
- package/dist/email-send.js +32 -0
- package/dist/email-send.js.map +1 -0
- package/dist/email.d.ts +14 -10
- package/dist/email.js.map +1 -1
- package/dist/{handler-stateless-C_bo-Ytq.d.ts → handler-stateless-DxYpJ_XF.d.ts} +3 -3
- package/dist/{handler-stateless-CIkKPETH.js → handler-stateless-VvrWSAVA.js} +5 -5
- package/dist/handler-stateless-VvrWSAVA.js.map +1 -0
- package/dist/index-BB0kqhIz.d.ts +101 -0
- package/dist/index-XDkuQ7zm.d.ts +89 -0
- package/dist/{index-BRnybD6X.d.ts → index-YSKgfgg9.d.ts} +21 -31
- package/dist/index.d.ts +113 -101
- package/dist/index.js +11 -7234
- package/dist/ingress-BfetZbMO.js +83 -0
- package/dist/ingress-BfetZbMO.js.map +1 -0
- package/dist/internal-CYlgHl1l.js +59 -0
- package/dist/internal-CYlgHl1l.js.map +1 -0
- package/dist/internal_context-BlxFEWfn.d.ts +19 -0
- package/dist/internal_context.d.ts +10 -4
- package/dist/internal_context.js +1 -10
- package/dist/{client-invoker-BNSZxAkv.d.ts → invoker-CG0_p_Wq.d.ts} +2 -2
- package/dist/{client-invoker-VNZ7X0nn.js → invoker-CHMnoxIA.js} +2 -2
- package/dist/invoker-CHMnoxIA.js.map +1 -0
- package/dist/lifecycle/index.d.ts +66 -0
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle-CMRGjZdw.js +1299 -0
- package/dist/lifecycle-CMRGjZdw.js.map +1 -0
- package/dist/mcp/{do-oauth-client-provider.d.ts → client/do-oauth-client-provider.d.ts} +1 -1
- package/dist/mcp/{do-oauth-client-provider.js → client/do-oauth-client-provider.js} +1 -1
- package/dist/mcp/client/do-oauth-client-provider.js.map +1 -0
- package/dist/mcp/client/index.d.ts +42 -0
- package/dist/mcp/{client.js → client/index.js} +1 -1
- package/dist/mcp/{x402.d.ts → client/x402.d.ts} +2 -2
- package/dist/mcp/{x402.js → client/x402.js} +2 -2
- package/dist/mcp/client/x402.js.map +1 -0
- package/dist/mcp/index.d.ts +36 -36
- package/dist/mcp/index.js +14 -16
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/{server.d.ts → server/index.d.ts} +1 -1
- package/dist/mcp/{server.js → server/index.js} +1 -1
- package/dist/observability/ai/index.js +50 -35
- package/dist/observability/ai/index.js.map +1 -1
- package/dist/observability/index.d.ts +4 -4
- package/dist/observability/index.js +3 -50
- package/dist/observability/index.js.map +1 -1
- package/dist/{protocol-Dqc2MQxo.js → protocol-B0nh6KNf.js} +19 -21
- package/dist/protocol-B0nh6KNf.js.map +1 -0
- package/dist/react.d.ts +4 -4
- package/dist/react.js +1 -1
- package/dist/{retries-CAvxtG9d.d.ts → retries-D9Ds-1lz.d.ts} +17 -6
- package/dist/retries.d.ts +8 -6
- package/dist/retries.js +13 -1
- package/dist/retries.js.map +1 -1
- package/dist/routing/index.d.ts +137 -0
- package/dist/routing/index.js +244 -0
- package/dist/routing/index.js.map +1 -0
- package/dist/sanitize-D9TujEK8.js +79 -0
- package/dist/sanitize-D9TujEK8.js.map +1 -0
- package/dist/schedule.d.ts +25 -94
- package/dist/schedule.js +1 -98
- package/dist/schedule.js.map +1 -1
- package/dist/scheduler-DD9NdYbF.js +665 -0
- package/dist/scheduler-DD9NdYbF.js.map +1 -0
- package/dist/scheduler-Dwh85ZGl.d.ts +223 -0
- package/dist/schedules/index.d.ts +22 -0
- package/dist/schedules/index.js +2 -0
- package/dist/schedules/parser.d.ts +79 -0
- package/dist/schedules/parser.js +103 -0
- package/dist/schedules/parser.js.map +1 -0
- package/dist/sentence-chunker-BAidJ4DA.d.ts +68 -0
- package/dist/serializable.d.ts +1 -1
- package/dist/sessions/index.d.ts +441 -0
- package/dist/sessions/index.js +2063 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/skills/index.d.ts +99 -0
- package/dist/skills/index.js +254 -5
- package/dist/skills/index.js.map +1 -1
- package/dist/sql-error-CPY-GXyI.d.ts +12 -0
- package/dist/sql-error.d.ts +2 -0
- package/dist/sql-error.js +16 -0
- package/dist/sql-error.js.map +1 -0
- package/dist/src-DlSHshb2.js +6963 -0
- package/dist/src-DlSHshb2.js.map +1 -0
- package/dist/streams/index.d.ts +120 -0
- package/dist/streams/index.js +107 -0
- package/dist/streams/index.js.map +1 -0
- package/dist/streams-D6tJ0NN9.d.ts +370 -0
- package/dist/streams-DZKgAj9b.js +709 -0
- package/dist/streams-DZKgAj9b.js.map +1 -0
- package/dist/sub-routing.d.ts +12 -12
- package/dist/surface-bZZJqBka.js +17 -0
- package/dist/surface-bZZJqBka.js.map +1 -0
- package/dist/tasks/index.d.ts +64 -0
- package/dist/tasks/index.js +2 -0
- package/dist/tasks-BRJ5zgya.d.ts +517 -0
- package/dist/tasks-ylZgBjhj.js +1656 -0
- package/dist/tasks-ylZgBjhj.js.map +1 -0
- package/dist/text-segment-joiner-BtAFQSA_.js +57 -0
- package/dist/text-segment-joiner-BtAFQSA_.js.map +1 -0
- package/dist/text-stream-CpdiKrJB.js +272 -0
- package/dist/text-stream-CpdiKrJB.js.map +1 -0
- package/dist/tokens-nHAKcN6M.js +52 -0
- package/dist/tokens-nHAKcN6M.js.map +1 -0
- package/dist/tool-schema-CBjGPrsQ.js +31 -0
- package/dist/tool-schema-CBjGPrsQ.js.map +1 -0
- package/dist/types-B7LojTe4.d.ts +202 -0
- package/dist/types-_Faxb570.d.ts +439 -0
- package/dist/voice/client.d.ts +226 -0
- package/dist/voice/client.js +932 -0
- package/dist/voice/client.js.map +1 -0
- package/dist/voice/errors.d.ts +43 -0
- package/dist/voice/errors.js +41 -0
- package/dist/voice/errors.js.map +1 -0
- package/dist/voice/index.d.ts +271 -0
- package/dist/voice/index.js +1812 -0
- package/dist/voice/index.js.map +1 -0
- package/dist/voice/react.d.ts +167 -0
- package/dist/voice/react.js +234 -0
- package/dist/voice/react.js.map +1 -0
- package/dist/voice/sfu.d.ts +71 -0
- package/dist/voice/sfu.js +157 -0
- package/dist/voice/sfu.js.map +1 -0
- package/dist/voice/text.d.ts +6 -0
- package/dist/voice/text.js +2 -0
- package/dist/voice/types.d.ts +58 -0
- package/dist/voice/types.js +18 -0
- package/dist/voice/types.js.map +1 -0
- package/dist/voice/workers-ai.d.ts +136 -0
- package/dist/voice/workers-ai.js +568 -0
- package/dist/voice/workers-ai.js.map +1 -0
- package/dist/websockets/index.d.ts +192 -0
- package/dist/websockets/index.js +2 -0
- package/dist/websockets-DUfRHPRq.js +502 -0
- package/dist/websockets-DUfRHPRq.js.map +1 -0
- package/dist/workflow-types.d.ts +25 -25
- package/dist/workflows.d.ts +22 -22
- package/dist/workflows.js +2 -1
- package/dist/workflows.js.map +1 -1
- package/dist/{ws-chat-transport-CIoOBbO7.js → ws-chat-transport-rWwta645.js} +152 -15
- package/dist/ws-chat-transport-rWwta645.js.map +1 -0
- package/docs/agent-class.md +29 -87
- package/docs/agent-tools.md +2 -1
- package/docs/channels.md +323 -0
- package/docs/chat-agents.md +19 -25
- package/docs/context.md +131 -0
- package/docs/durable-execution.md +1 -1
- package/docs/http-websockets.md +1 -11
- package/docs/human-in-the-loop.md +1 -1
- package/docs/index.md +16 -12
- package/docs/lifecycle.md +370 -0
- package/docs/long-running-agents.md +4 -6
- package/docs/mcp-client.md +55 -0
- package/docs/mcp-servers.md +5 -1
- package/docs/observability.md +11 -11
- package/docs/resumable-streaming.md +2 -2
- package/docs/routing.md +105 -0
- package/docs/scheduling.md +175 -15
- package/docs/server-driven-messages.md +1 -1
- package/docs/sessions.md +237 -871
- package/docs/streams.md +213 -0
- package/docs/sub-agents.md +185 -125
- package/docs/tasks.md +246 -0
- package/docs/voice.md +745 -0
- package/package.json +144 -33
- package/dist/cli/index.js +0 -26
- package/dist/cli/index.js.map +0 -1
- package/dist/client-invoker-VNZ7X0nn.js.map +0 -1
- package/dist/client-zqKcsyFa.js.map +0 -1
- package/dist/compaction-helpers-iiKMr2TQ.js +0 -340
- package/dist/compaction-helpers-iiKMr2TQ.js.map +0 -1
- package/dist/compaction-helpers-wUz6M3us.d.ts +0 -621
- package/dist/experimental/memory/session/index.d.ts +0 -670
- package/dist/experimental/memory/session/index.js +0 -2374
- package/dist/experimental/memory/session/index.js.map +0 -1
- package/dist/experimental/memory/utils/index.d.ts +0 -96
- package/dist/experimental/memory/utils/index.js +0 -79
- package/dist/experimental/memory/utils/index.js.map +0 -1
- package/dist/handler-stateless-CIkKPETH.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal_context-Dg4Cgjcu.d.ts +0 -37
- package/dist/internal_context.js.map +0 -1
- package/dist/mcp/client.d.ts +0 -42
- package/dist/mcp/do-oauth-client-provider.js.map +0 -1
- package/dist/mcp/x402.js.map +0 -1
- package/dist/protocol-Dqc2MQxo.js.map +0 -1
- package/dist/tool-output-truncation-CNnnGZQ3.js +0 -98
- package/dist/tool-output-truncation-CNnnGZQ3.js.map +0 -1
- package/dist/ws-chat-transport-CIoOBbO7.js.map +0 -1
- /package/dist/{cli/index.d.ts → index-BVVgDSdq.d.ts} +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import {
|
|
2
|
+
a as StreamJson,
|
|
3
|
+
c as StreamReadBatchesOptions,
|
|
4
|
+
d as StreamState,
|
|
5
|
+
f as StreamStatus,
|
|
6
|
+
i as StreamChunk,
|
|
7
|
+
l as StreamReadOptions,
|
|
8
|
+
n as Streams,
|
|
9
|
+
o as StreamListOptions,
|
|
10
|
+
p as StreamWriter,
|
|
11
|
+
r as StreamsOptions,
|
|
12
|
+
s as StreamOpenOptions,
|
|
13
|
+
t as DEFAULT_MAX_CHUNK_BYTES,
|
|
14
|
+
u as StreamSettleOptions
|
|
15
|
+
} from "../streams-D6tJ0NN9.js";
|
|
16
|
+
|
|
17
|
+
//#region src/streams/errors.d.ts
|
|
18
|
+
/**
|
|
19
|
+
* Error classes for the Streams capability. Each carries a stable `name` so
|
|
20
|
+
* hosts and tests can classify failures without depending on message text.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Thrown when `open()` targets a terminal stream, or an append reaches a
|
|
24
|
+
* stream that settled (or was deleted) after the writer was created.
|
|
25
|
+
*
|
|
26
|
+
* @experimental The API surface may change before stabilizing.
|
|
27
|
+
*/
|
|
28
|
+
declare class StreamClosedError extends Error {
|
|
29
|
+
/** The stream that no longer accepts writes. */
|
|
30
|
+
readonly streamId: string;
|
|
31
|
+
constructor(streamId: string, detail: string);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Thrown when `read()` targets a stream that was never opened (or was
|
|
35
|
+
* deleted). `status()` returns `null` instead, for existence probes.
|
|
36
|
+
*
|
|
37
|
+
* @experimental The API surface may change before stabilizing.
|
|
38
|
+
*/
|
|
39
|
+
declare class StreamNotFoundError extends Error {
|
|
40
|
+
readonly streamId: string;
|
|
41
|
+
constructor(streamId: string);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Thrown when a chunk or metadata value is not JSON-serializable or exceeds
|
|
45
|
+
* the configured size limit.
|
|
46
|
+
*
|
|
47
|
+
* @experimental The API surface may change before stabilizing.
|
|
48
|
+
*/
|
|
49
|
+
declare class StreamSerializationError extends Error {
|
|
50
|
+
constructor(context: string, detail: string);
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/streams/sse.d.ts
|
|
54
|
+
/**
|
|
55
|
+
* Options accepted by {@link sseResponse}.
|
|
56
|
+
*
|
|
57
|
+
* @experimental The API surface may change before stabilizing.
|
|
58
|
+
*/
|
|
59
|
+
interface SSEResponseOptions {
|
|
60
|
+
/**
|
|
61
|
+
* The incoming request. Used for resume (the `Last-Event-ID` header a
|
|
62
|
+
* reconnecting `EventSource` sends, or a `?from=` query parameter) and to
|
|
63
|
+
* abort the tail when the client disconnects (`request.signal`).
|
|
64
|
+
*/
|
|
65
|
+
request?: Request;
|
|
66
|
+
/** First sequence number to yield. Overrides request-derived resume. */
|
|
67
|
+
from?: number;
|
|
68
|
+
/** Extra abort signal; composed with `request.signal`. */
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
/** Maximum chunks per write. Defaults to the read batch size (100). */
|
|
71
|
+
batchSize?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Milliseconds between `: heartbeat` comment frames while tailing, keeping
|
|
74
|
+
* idle proxies from killing the connection. 0 disables. Default: 30000.
|
|
75
|
+
*/
|
|
76
|
+
heartbeatMs?: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Serve one durable stream as a Server-Sent Events response: replay from the
|
|
80
|
+
* resume point, emit an `up-to-date` control event on reaching the tail,
|
|
81
|
+
* tail live appends, and finish with `done` (completed) or `error` (errored,
|
|
82
|
+
* carrying the recorded reason). Returns a 404 response when the stream does
|
|
83
|
+
* not exist.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* async onRequest(request: Request) {
|
|
87
|
+
* return sseResponse(this.streams, "reply:123", { request });
|
|
88
|
+
* }
|
|
89
|
+
* // client: new EventSource(url) — reconnect resume is automatic.
|
|
90
|
+
* ```
|
|
91
|
+
*
|
|
92
|
+
* @experimental The API surface may change before stabilizing.
|
|
93
|
+
*/
|
|
94
|
+
declare function sseResponse(
|
|
95
|
+
streams: Streams,
|
|
96
|
+
streamId: string,
|
|
97
|
+
options?: SSEResponseOptions
|
|
98
|
+
): Promise<Response>;
|
|
99
|
+
//#endregion
|
|
100
|
+
export {
|
|
101
|
+
DEFAULT_MAX_CHUNK_BYTES,
|
|
102
|
+
type SSEResponseOptions,
|
|
103
|
+
type StreamChunk,
|
|
104
|
+
StreamClosedError,
|
|
105
|
+
type StreamJson,
|
|
106
|
+
type StreamListOptions,
|
|
107
|
+
StreamNotFoundError,
|
|
108
|
+
type StreamOpenOptions,
|
|
109
|
+
type StreamReadBatchesOptions,
|
|
110
|
+
type StreamReadOptions,
|
|
111
|
+
StreamSerializationError,
|
|
112
|
+
type StreamSettleOptions,
|
|
113
|
+
type StreamState,
|
|
114
|
+
type StreamStatus,
|
|
115
|
+
type StreamWriter,
|
|
116
|
+
Streams,
|
|
117
|
+
type StreamsOptions,
|
|
118
|
+
sseResponse
|
|
119
|
+
};
|
|
120
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { a as StreamSerializationError, i as StreamNotFoundError, n as Streams, r as StreamClosedError, t as DEFAULT_MAX_CHUNK_BYTES } from "../streams-DZKgAj9b.js";
|
|
2
|
+
//#region src/streams/sse.ts
|
|
3
|
+
const encoder = new TextEncoder();
|
|
4
|
+
function resumeFrom(options) {
|
|
5
|
+
if (options.from !== void 0) return Math.max(0, options.from);
|
|
6
|
+
const request = options.request;
|
|
7
|
+
if (request) {
|
|
8
|
+
const header = request.headers.get("Last-Event-ID");
|
|
9
|
+
if (header !== null && header !== "") {
|
|
10
|
+
const lastEventId = Number(header);
|
|
11
|
+
if (Number.isInteger(lastEventId) && lastEventId >= 0) return lastEventId + 1;
|
|
12
|
+
}
|
|
13
|
+
const fromParam = new URL(request.url).searchParams.get("from");
|
|
14
|
+
if (fromParam !== null) {
|
|
15
|
+
const from = Number(fromParam);
|
|
16
|
+
if (Number.isInteger(from) && from >= 0) return from;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
function frame(seq, chunk) {
|
|
22
|
+
return `id: ${seq}\ndata: ${JSON.stringify(chunk)}\n\n`;
|
|
23
|
+
}
|
|
24
|
+
function controlFrame(event, data) {
|
|
25
|
+
return `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Serve one durable stream as a Server-Sent Events response: replay from the
|
|
29
|
+
* resume point, emit an `up-to-date` control event on reaching the tail,
|
|
30
|
+
* tail live appends, and finish with `done` (completed) or `error` (errored,
|
|
31
|
+
* carrying the recorded reason). Returns a 404 response when the stream does
|
|
32
|
+
* not exist.
|
|
33
|
+
*
|
|
34
|
+
* ```ts
|
|
35
|
+
* async onRequest(request: Request) {
|
|
36
|
+
* return sseResponse(this.streams, "reply:123", { request });
|
|
37
|
+
* }
|
|
38
|
+
* // client: new EventSource(url) — reconnect resume is automatic.
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @experimental The API surface may change before stabilizing.
|
|
42
|
+
*/
|
|
43
|
+
async function sseResponse(streams, streamId, options = {}) {
|
|
44
|
+
if (await streams.status(streamId) === null) return new Response(`Stream "${streamId}" does not exist`, { status: 404 });
|
|
45
|
+
const from = resumeFrom(options);
|
|
46
|
+
const heartbeatMs = options.heartbeatMs ?? 3e4;
|
|
47
|
+
const abort = new AbortController();
|
|
48
|
+
const onUpstreamAbort = () => abort.abort();
|
|
49
|
+
if (options.signal?.aborted || options.request?.signal.aborted) abort.abort();
|
|
50
|
+
else {
|
|
51
|
+
options.signal?.addEventListener("abort", onUpstreamAbort, { once: true });
|
|
52
|
+
options.request?.signal.addEventListener("abort", onUpstreamAbort, { once: true });
|
|
53
|
+
}
|
|
54
|
+
let open = true;
|
|
55
|
+
const body = new ReadableStream({
|
|
56
|
+
start: (controller) => {
|
|
57
|
+
const write = (text) => {
|
|
58
|
+
if (!open) return;
|
|
59
|
+
try {
|
|
60
|
+
controller.enqueue(encoder.encode(text));
|
|
61
|
+
} catch {
|
|
62
|
+
open = false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const heartbeat = heartbeatMs > 0 ? setInterval(() => write(": heartbeat\n\n"), heartbeatMs) : null;
|
|
66
|
+
const finish = () => {
|
|
67
|
+
if (heartbeat !== null) clearInterval(heartbeat);
|
|
68
|
+
options.signal?.removeEventListener("abort", onUpstreamAbort);
|
|
69
|
+
options.request?.signal.removeEventListener("abort", onUpstreamAbort);
|
|
70
|
+
if (open) {
|
|
71
|
+
open = false;
|
|
72
|
+
try {
|
|
73
|
+
controller.close();
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
(async () => {
|
|
78
|
+
try {
|
|
79
|
+
const batches = streams.readBatches(streamId, {
|
|
80
|
+
from,
|
|
81
|
+
signal: abort.signal,
|
|
82
|
+
batchSize: options.batchSize,
|
|
83
|
+
onUpToDate: () => write(controlFrame("up-to-date", {}))
|
|
84
|
+
});
|
|
85
|
+
for await (const batch of batches) write(batch.map((chunk) => frame(chunk.seq, chunk.chunk)).join(""));
|
|
86
|
+
const status = await streams.status(streamId);
|
|
87
|
+
if (status?.state === "errored") write(controlFrame("error", { reason: status.error ?? null }));
|
|
88
|
+
else write(controlFrame("done", {}));
|
|
89
|
+
} catch {} finally {
|
|
90
|
+
finish();
|
|
91
|
+
}
|
|
92
|
+
})();
|
|
93
|
+
},
|
|
94
|
+
cancel: () => {
|
|
95
|
+
open = false;
|
|
96
|
+
abort.abort();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
return new Response(body, { headers: {
|
|
100
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
101
|
+
"cache-control": "no-store"
|
|
102
|
+
} });
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
export { DEFAULT_MAX_CHUNK_BYTES, StreamClosedError, StreamNotFoundError, StreamSerializationError, Streams, sseResponse };
|
|
106
|
+
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/streams/sse.ts"],"sourcesContent":["/**\n * Server-Sent Events serving for durable streams: turn one stream into an\n * SSE `Response` with resume built into the protocol. Every chunk is emitted\n * with its sequence number as the SSE `id:` field, so a browser\n * `EventSource` that reconnects sends `Last-Event-ID` automatically and the\n * helper resumes from the next chunk — cursor persistence with zero client\n * code. Control events mark the replay→live transition (`up-to-date`) and\n * settlement (`done` / `error`), mirroring the stream's own lifecycle.\n */\n\nimport type { Streams } from \"./streams\";\nimport type { StreamJson } from \"./types\";\n\nconst encoder = new TextEncoder();\n\n/**\n * Options accepted by {@link sseResponse}.\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport interface SSEResponseOptions {\n /**\n * The incoming request. Used for resume (the `Last-Event-ID` header a\n * reconnecting `EventSource` sends, or a `?from=` query parameter) and to\n * abort the tail when the client disconnects (`request.signal`).\n */\n request?: Request;\n /** First sequence number to yield. Overrides request-derived resume. */\n from?: number;\n /** Extra abort signal; composed with `request.signal`. */\n signal?: AbortSignal;\n /** Maximum chunks per write. Defaults to the read batch size (100). */\n batchSize?: number;\n /**\n * Milliseconds between `: heartbeat` comment frames while tailing, keeping\n * idle proxies from killing the connection. 0 disables. Default: 30000.\n */\n heartbeatMs?: number;\n}\n\nfunction resumeFrom(options: SSEResponseOptions): number {\n if (options.from !== undefined) return Math.max(0, options.from);\n const request = options.request;\n if (request) {\n // A fresh EventSource sends no Last-Event-ID at all; Number(null) is 0,\n // so the header must be checked for presence before parsing or every\n // first connection would skip chunk 0.\n const header = request.headers.get(\"Last-Event-ID\");\n if (header !== null && header !== \"\") {\n const lastEventId = Number(header);\n // Last-Event-ID names the last chunk the client received; resume after.\n if (Number.isInteger(lastEventId) && lastEventId >= 0) {\n return lastEventId + 1;\n }\n }\n const fromParam = new URL(request.url).searchParams.get(\"from\");\n if (fromParam !== null) {\n const from = Number(fromParam);\n if (Number.isInteger(from) && from >= 0) return from;\n }\n }\n return 0;\n}\n\nfunction frame(seq: number, chunk: StreamJson): string {\n // JSON.stringify never emits raw newlines, so one data line is always safe.\n return `id: ${seq}\\ndata: ${JSON.stringify(chunk)}\\n\\n`;\n}\n\nfunction controlFrame(event: string, data: StreamJson): string {\n return `event: ${event}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n}\n\n/**\n * Serve one durable stream as a Server-Sent Events response: replay from the\n * resume point, emit an `up-to-date` control event on reaching the tail,\n * tail live appends, and finish with `done` (completed) or `error` (errored,\n * carrying the recorded reason). Returns a 404 response when the stream does\n * not exist.\n *\n * ```ts\n * async onRequest(request: Request) {\n * return sseResponse(this.streams, \"reply:123\", { request });\n * }\n * // client: new EventSource(url) — reconnect resume is automatic.\n * ```\n *\n * @experimental The API surface may change before stabilizing.\n */\nexport async function sseResponse(\n streams: Streams,\n streamId: string,\n options: SSEResponseOptions = {}\n): Promise<Response> {\n if ((await streams.status(streamId)) === null) {\n return new Response(`Stream \"${streamId}\" does not exist`, { status: 404 });\n }\n\n const from = resumeFrom(options);\n const heartbeatMs = options.heartbeatMs ?? 30_000;\n const abort = new AbortController();\n const onUpstreamAbort = () => abort.abort();\n // A listener added to an already-aborted signal never fires: check first,\n // or a pre-aborted request would tail a live stream forever.\n if (options.signal?.aborted || options.request?.signal.aborted) {\n abort.abort();\n } else {\n options.signal?.addEventListener(\"abort\", onUpstreamAbort, { once: true });\n options.request?.signal.addEventListener(\"abort\", onUpstreamAbort, {\n once: true\n });\n }\n\n let open = true;\n const body = new ReadableStream<Uint8Array>({\n start: (controller) => {\n const write = (text: string) => {\n if (!open) return;\n try {\n controller.enqueue(encoder.encode(text));\n } catch {\n // The stream was cancelled between the check and the write (a\n // heartbeat tick racing a client disconnect): stop writing.\n open = false;\n }\n };\n const heartbeat =\n heartbeatMs > 0\n ? setInterval(() => write(\": heartbeat\\n\\n\"), heartbeatMs)\n : null;\n const finish = () => {\n if (heartbeat !== null) clearInterval(heartbeat);\n options.signal?.removeEventListener(\"abort\", onUpstreamAbort);\n options.request?.signal.removeEventListener(\"abort\", onUpstreamAbort);\n if (open) {\n open = false;\n try {\n controller.close();\n } catch {\n // Already cancelled by the consumer.\n }\n }\n };\n\n void (async () => {\n try {\n const batches = streams.readBatches(streamId, {\n from,\n signal: abort.signal,\n batchSize: options.batchSize,\n onUpToDate: () => write(controlFrame(\"up-to-date\", {}))\n });\n for await (const batch of batches) {\n write(batch.map((chunk) => frame(chunk.seq, chunk.chunk)).join(\"\"));\n }\n const status = await streams.status(streamId);\n if (status?.state === \"errored\") {\n write(controlFrame(\"error\", { reason: status.error ?? null }));\n } else {\n write(controlFrame(\"done\", {}));\n }\n } catch {\n // The client disconnected or the caller aborted: nothing to send.\n } finally {\n finish();\n }\n })();\n },\n cancel: () => {\n open = false;\n abort.abort();\n }\n });\n\n return new Response(body, {\n headers: {\n \"content-type\": \"text/event-stream; charset=utf-8\",\n \"cache-control\": \"no-store\"\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { s as LifecycleCapability } from "./capability-runner-Be_-PLR1.js";
|
|
2
|
+
|
|
3
|
+
//#region src/streams/types.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* JSON-serializable data accepted as stream chunks and metadata.
|
|
6
|
+
*
|
|
7
|
+
* @experimental The API surface may change before stabilizing.
|
|
8
|
+
*/
|
|
9
|
+
type StreamJson =
|
|
10
|
+
| string
|
|
11
|
+
| number
|
|
12
|
+
| boolean
|
|
13
|
+
| null
|
|
14
|
+
| StreamJson[]
|
|
15
|
+
| {
|
|
16
|
+
[key: string]: StreamJson;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* States a stream moves through. A stream is `streaming` from `open()` until
|
|
20
|
+
* its producer settles it; both terminal states keep the chunk log readable.
|
|
21
|
+
*
|
|
22
|
+
* @experimental The API surface may change before stabilizing.
|
|
23
|
+
*/
|
|
24
|
+
type StreamState = "streaming" | "completed" | "errored";
|
|
25
|
+
/**
|
|
26
|
+
* One durable chunk. `seq` is the stream's monotonic cursor: 0-based,
|
|
27
|
+
* assigned at append time, and stable across replays.
|
|
28
|
+
*
|
|
29
|
+
* @experimental The API surface may change before stabilizing.
|
|
30
|
+
*/
|
|
31
|
+
interface StreamChunk {
|
|
32
|
+
readonly seq: number;
|
|
33
|
+
readonly chunk: StreamJson;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Read-only status of one stream — the recovery-evidence surface a Task's
|
|
37
|
+
* `recover` callback consults.
|
|
38
|
+
*
|
|
39
|
+
* @experimental The API surface may change before stabilizing.
|
|
40
|
+
*/
|
|
41
|
+
interface StreamStatus {
|
|
42
|
+
streamId: string;
|
|
43
|
+
state: StreamState;
|
|
44
|
+
/** The next sequence number to be assigned == durable chunk count. */
|
|
45
|
+
cursor: number;
|
|
46
|
+
/** Application lookup key assigned at `open()`, when one was. */
|
|
47
|
+
tag?: string;
|
|
48
|
+
metadata?: Record<string, StreamJson>;
|
|
49
|
+
/** Reason recorded by `error()`, when the state is `errored`. */
|
|
50
|
+
error?: string;
|
|
51
|
+
createdAt: number;
|
|
52
|
+
/**
|
|
53
|
+
* Last write activity: advances with every append and with settlement.
|
|
54
|
+
* The liveness signal retention policies key off — a `streaming` stream
|
|
55
|
+
* whose `updatedAt` is old has a producer that stopped appending. (For a
|
|
56
|
+
* live stream this is derived from the chunk log's newest entry; the
|
|
57
|
+
* stored row is only stamped at open and settle.)
|
|
58
|
+
*/
|
|
59
|
+
updatedAt: number;
|
|
60
|
+
closedAt?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Producer handle returned by `Streams.open()`. Appends are synchronous
|
|
64
|
+
* durable writes; a terminal stream rejects further appends.
|
|
65
|
+
*
|
|
66
|
+
* @experimental The API surface may change before stabilizing.
|
|
67
|
+
*/
|
|
68
|
+
interface StreamWriter {
|
|
69
|
+
readonly streamId: string;
|
|
70
|
+
/** The next sequence number to be assigned. */
|
|
71
|
+
readonly cursor: number;
|
|
72
|
+
/** Durably append one chunk and wake live readers. Returns its `seq`. */
|
|
73
|
+
append(chunk: StreamJson): number;
|
|
74
|
+
/**
|
|
75
|
+
* Settle the stream as completed. No-op if already terminal or deleted:
|
|
76
|
+
* `options.commit` runs only when this call ends the stream.
|
|
77
|
+
*/
|
|
78
|
+
close(options?: StreamSettleOptions): void;
|
|
79
|
+
/** Settle the stream as errored. Same no-op contract as {@link close}. */
|
|
80
|
+
error(reason?: string, options?: StreamSettleOptions): void;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The cutover: settle a stream, run the caller's own synchronous writes
|
|
84
|
+
* (typically persisting the finished message), and discard the stream's
|
|
85
|
+
* rows, all in ONE SQLite transaction. A crash leaves either the live
|
|
86
|
+
* stream or the finished message, never neither. `commit` must not await
|
|
87
|
+
* and must not throw for a reason it wants ignored: a throw rolls the
|
|
88
|
+
* settle back and leaves the stream live.
|
|
89
|
+
*
|
|
90
|
+
* Settlement stays idempotent: on a stream already terminal or deleted,
|
|
91
|
+
* `commit` does not run and nothing is discarded. Events and reader wakeups
|
|
92
|
+
* fire after the transaction commits, never for a rolled-back cutover.
|
|
93
|
+
*
|
|
94
|
+
* @experimental The API surface may change before stabilizing.
|
|
95
|
+
*/
|
|
96
|
+
interface StreamSettleOptions {
|
|
97
|
+
/** Synchronous writes to commit with the settlement. */
|
|
98
|
+
readonly commit?: () => void;
|
|
99
|
+
/**
|
|
100
|
+
* Delete the stream's rows in the same transaction. The stream ceases
|
|
101
|
+
* to exist (`status()` returns null); readers tailing it end. Use when
|
|
102
|
+
* the chunks have been handed off, so nothing is left to sweep later.
|
|
103
|
+
*/
|
|
104
|
+
readonly discard?: boolean;
|
|
105
|
+
}
|
|
106
|
+
/** Options accepted by `Streams.open()`. */
|
|
107
|
+
interface StreamOpenOptions {
|
|
108
|
+
/** JSON metadata retained with the stream. */
|
|
109
|
+
metadata?: Record<string, StreamJson>;
|
|
110
|
+
/**
|
|
111
|
+
* Indexed application lookup key, set once at creation. Deliberately not
|
|
112
|
+
* unique: an operation that produces successive streams (a retried turn, a
|
|
113
|
+
* regenerated reply) stamps each with the same tag, and
|
|
114
|
+
* `list({ tag, limit: 1 })` finds the latest. Reopening a live stream with
|
|
115
|
+
* a *different* tag throws — a config conflict, not a new stream.
|
|
116
|
+
*/
|
|
117
|
+
tag?: string;
|
|
118
|
+
}
|
|
119
|
+
/** Options accepted by `Streams.read()`. */
|
|
120
|
+
interface StreamReadOptions {
|
|
121
|
+
/** First sequence number to yield (inclusive). Defaults to 0. */
|
|
122
|
+
from?: number;
|
|
123
|
+
/** Abort a read that is tailing a live stream. */
|
|
124
|
+
signal?: AbortSignal;
|
|
125
|
+
}
|
|
126
|
+
/** Options accepted by `Streams.readBatches()`. */
|
|
127
|
+
interface StreamReadBatchesOptions extends StreamReadOptions {
|
|
128
|
+
/** Maximum chunks per yielded batch. Defaults to 100. */
|
|
129
|
+
batchSize?: number;
|
|
130
|
+
/**
|
|
131
|
+
* Invoked once, the first time the reader reaches the durable tail —
|
|
132
|
+
* i.e. every chunk stored so far has been yielded. Distinct from the
|
|
133
|
+
* stream ending: a live stream is "up to date" while tailing. Useful as a
|
|
134
|
+
* transition signal (flush replayed UI, show a live indicator).
|
|
135
|
+
*/
|
|
136
|
+
onUpToDate?: () => void;
|
|
137
|
+
}
|
|
138
|
+
/** Filters accepted by `Streams.list()`. */
|
|
139
|
+
interface StreamListOptions {
|
|
140
|
+
state?: StreamState | StreamState[];
|
|
141
|
+
/** Only streams opened with this exact tag (indexed). */
|
|
142
|
+
tag?: string;
|
|
143
|
+
limit?: number;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* @internal Raw `cf_agents_streams` SQLite row.
|
|
147
|
+
*
|
|
148
|
+
* While `state` is `streaming`, `chunk_count` and `updated_at` are NOT
|
|
149
|
+
* maintained per append (appends write only the chunk log; the log's tail
|
|
150
|
+
* is authoritative — see `Streams.#tail`). Both are stamped exact by the
|
|
151
|
+
* settle UPDATE, so terminal rows read straight through. Consumers of a
|
|
152
|
+
* live row must derive cursor/liveness rather than trust these columns.
|
|
153
|
+
*/
|
|
154
|
+
type StreamRow = {
|
|
155
|
+
stream_id: string;
|
|
156
|
+
state: StreamState;
|
|
157
|
+
tag: string | null;
|
|
158
|
+
metadata: string | null;
|
|
159
|
+
error_message: string | null;
|
|
160
|
+
chunk_count: number;
|
|
161
|
+
created_at: number;
|
|
162
|
+
updated_at: number;
|
|
163
|
+
closed_at: number | null;
|
|
164
|
+
};
|
|
165
|
+
/** @internal One chunk as read back from a `cf_agents_stream_blocks` row. */
|
|
166
|
+
type StreamChunkRow = {
|
|
167
|
+
stream_id: string;
|
|
168
|
+
seq: number;
|
|
169
|
+
chunk: string;
|
|
170
|
+
created_at: number;
|
|
171
|
+
};
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/streams/streams.d.ts
|
|
174
|
+
/** Default ceiling for one serialized chunk (1 MiB). */
|
|
175
|
+
declare const DEFAULT_MAX_CHUNK_BYTES = 1048576;
|
|
176
|
+
/**
|
|
177
|
+
* Policy for a Streams capability.
|
|
178
|
+
*
|
|
179
|
+
* @experimental The API surface may change before stabilizing.
|
|
180
|
+
*/
|
|
181
|
+
interface StreamsOptions {
|
|
182
|
+
/** Ceiling for one serialized chunk. Default: 1 MiB. */
|
|
183
|
+
readonly maxChunkBytes?: number;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* @internal Synchronous operations returned by
|
|
187
|
+
* {@link Streams.__DO_NOT_USE_WILL_BREAK__sync}. For same-isolate first-party
|
|
188
|
+
* machinery only (the chat `ResumableStream` adapter); every method bypasses
|
|
189
|
+
* `lifecycle.ready()`, so the caller owns startup ordering.
|
|
190
|
+
*/
|
|
191
|
+
interface StreamsSyncInternal {
|
|
192
|
+
/** Idempotent DDL — safe to call before the Lifecycle starts. */
|
|
193
|
+
ensureTables(): void;
|
|
194
|
+
getStream(streamId: string): StreamRow | undefined;
|
|
195
|
+
/** Insert a live stream row (no idempotency — caller checks first). */
|
|
196
|
+
insertStream(
|
|
197
|
+
streamId: string,
|
|
198
|
+
tag: string | null,
|
|
199
|
+
metadata: Record<string, StreamJson> | undefined
|
|
200
|
+
): void;
|
|
201
|
+
/** The read-fenced append: one chunk insert at the log tail, reader wakeup. */
|
|
202
|
+
append(streamId: string, chunk: StreamJson): number;
|
|
203
|
+
/**
|
|
204
|
+
* The newest chunk's timestamp, or null for an empty log. One PK-served
|
|
205
|
+
* read — the per-append liveness signal retention sweeps verify against
|
|
206
|
+
* (a live row's `updated_at` is set at open and not bumped by appends).
|
|
207
|
+
*/
|
|
208
|
+
lastChunkAt(streamId: string): number | null;
|
|
209
|
+
/**
|
|
210
|
+
* Segments durably appended so far: the chunk log's tail, read in the
|
|
211
|
+
* calling synchronous block. Zero for an unknown stream.
|
|
212
|
+
*/
|
|
213
|
+
cursor(streamId: string): number;
|
|
214
|
+
/**
|
|
215
|
+
* Observe every deletion of a stream's rows — the public `delete()`, the
|
|
216
|
+
* aperture's own deletes, and a cutover's discard — with the row and its
|
|
217
|
+
* cursor as they were just before removal, in the same synchronous block
|
|
218
|
+
* (and, for a cutover, the same transaction). Hooks must be synchronous
|
|
219
|
+
* and must not await: the cutover runs them inside `transactionSync`.
|
|
220
|
+
* The chat adapter uses this to keep its recovery progress marker exact
|
|
221
|
+
* however a chat row leaves the table. Returns the unsubscribe: an owner
|
|
222
|
+
* constructed again (a host whose startup retried) must drop its earlier
|
|
223
|
+
* hook, or a deletion is observed once per construction.
|
|
224
|
+
*/
|
|
225
|
+
onDelete(hook: (row: StreamRow, cursor: number) => void): () => void;
|
|
226
|
+
/**
|
|
227
|
+
* Idempotent settlement with events and reader wakeup. With `options`,
|
|
228
|
+
* the settle, the caller's `commit` writes and the log discard run in
|
|
229
|
+
* one SQLite transaction (see {@link StreamSettleOptions}). Returns
|
|
230
|
+
* whether the stream transitioned; on a repeat or a deleted stream the
|
|
231
|
+
* `commit` callback does not run.
|
|
232
|
+
*/
|
|
233
|
+
settle(
|
|
234
|
+
streamId: string,
|
|
235
|
+
state: "completed" | "errored",
|
|
236
|
+
reason: string | null,
|
|
237
|
+
options?: StreamSettleOptions
|
|
238
|
+
): boolean;
|
|
239
|
+
/** Delete a stream and its chunks regardless of state. */
|
|
240
|
+
deleteUnchecked(streamId: string): void;
|
|
241
|
+
/** Delete many streams and their chunks regardless of state, silently. */
|
|
242
|
+
deleteMany(streamIds: string[]): void;
|
|
243
|
+
/**
|
|
244
|
+
* One page of a stream's chunk log from `fromSeq` (inclusive), ordered by
|
|
245
|
+
* seq. Paged rather than read-it-all so replaying a large stream holds
|
|
246
|
+
* one page of segment bodies in memory, not the whole turn.
|
|
247
|
+
*/
|
|
248
|
+
readChunks(
|
|
249
|
+
streamId: string,
|
|
250
|
+
fromSeq: number,
|
|
251
|
+
limit: number
|
|
252
|
+
): StreamChunkRow[];
|
|
253
|
+
/** Every stream row, newest first (created_at, then insertion order). */
|
|
254
|
+
listRows(): StreamRow[];
|
|
255
|
+
/**
|
|
256
|
+
* Every row carrying a tag, newest first, optionally narrowed to one
|
|
257
|
+
* state. Tags are non-unique and the table is shared across producers,
|
|
258
|
+
* so callers apply their own ownership filter (e.g. chat's metadata
|
|
259
|
+
* marker) rather than trusting the newest row.
|
|
260
|
+
*/
|
|
261
|
+
rowsByTag(tag: string, state?: StreamState): StreamRow[];
|
|
262
|
+
/**
|
|
263
|
+
* Import one historical stream row verbatim (migrations, test seeding):
|
|
264
|
+
* explicit timestamps and count, no events, no wakeups.
|
|
265
|
+
*/
|
|
266
|
+
importStream(row: {
|
|
267
|
+
streamId: string;
|
|
268
|
+
state: StreamState;
|
|
269
|
+
tag: string | null;
|
|
270
|
+
metadata: Record<string, StreamJson> | undefined;
|
|
271
|
+
chunkCount: number;
|
|
272
|
+
createdAt: number;
|
|
273
|
+
updatedAt: number;
|
|
274
|
+
closedAt: number | null;
|
|
275
|
+
}): void;
|
|
276
|
+
/**
|
|
277
|
+
* Import one historical chunk at the log's tail: one INSERT, nothing else.
|
|
278
|
+
* The stream row is not touched — importers pass the final `chunkCount`
|
|
279
|
+
* and `updatedAt` to {@link importStream}, so the row is exact at rest
|
|
280
|
+
* without a per-chunk row write.
|
|
281
|
+
*/
|
|
282
|
+
importChunk(streamId: string, chunk: StreamJson, createdAt: number): void;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Durable incremental output for a Lifecycle Object.
|
|
286
|
+
*
|
|
287
|
+
* `open()` a stream, `append()` chunks (synchronous durable writes that wake
|
|
288
|
+
* live readers), and settle it with `close()` or `error()`. `read()` replays
|
|
289
|
+
* persisted chunks from a cursor and then tails live appends; `status()`
|
|
290
|
+
* reports the state and cursor — the recovery evidence a Task's `recover`
|
|
291
|
+
* callback consults after its producer was interrupted.
|
|
292
|
+
*
|
|
293
|
+
* @experimental The API surface may change before stabilizing.
|
|
294
|
+
*/
|
|
295
|
+
declare class Streams extends LifecycleCapability {
|
|
296
|
+
#private;
|
|
297
|
+
constructor(options?: StreamsOptions);
|
|
298
|
+
/** Migrate stream storage during Lifecycle startup. */
|
|
299
|
+
onStart(): Promise<void>;
|
|
300
|
+
/**
|
|
301
|
+
* Open a stream for writing. Idempotent on the id: reopening a live stream
|
|
302
|
+
* returns a writer positioned at its current cursor; reopening a terminal
|
|
303
|
+
* stream throws {@link StreamClosedError}.
|
|
304
|
+
*/
|
|
305
|
+
open(streamId: string, options?: StreamOpenOptions): Promise<StreamWriter>;
|
|
306
|
+
/**
|
|
307
|
+
* Replay persisted chunks from `from` (inclusive), then tail live appends
|
|
308
|
+
* until the stream settles. Ends when the stream reaches a terminal state
|
|
309
|
+
* and every durable chunk has been yielded; a read of an `errored` stream
|
|
310
|
+
* still yields its chunks and then simply ends — consult {@link status}
|
|
311
|
+
* for the terminal outcome. Aborting `options.signal` throws its reason.
|
|
312
|
+
*/
|
|
313
|
+
read(
|
|
314
|
+
streamId: string,
|
|
315
|
+
options?: StreamReadOptions
|
|
316
|
+
): AsyncGenerator<StreamChunk, void, undefined>;
|
|
317
|
+
/**
|
|
318
|
+
* Batched form of {@link read}: yields non-empty arrays of consecutive
|
|
319
|
+
* chunks instead of one chunk at a time. Replay yields up to
|
|
320
|
+
* `options.batchSize` chunks per array; a live tail yields everything
|
|
321
|
+
* that accumulated since the last wakeup as one array — so a consumer
|
|
322
|
+
* paying per write (an SSE flush, an RPC hop, a history append) pays
|
|
323
|
+
* once per backlog, not once per chunk. Same lifecycle as {@link read}:
|
|
324
|
+
* ends when the stream settles and every durable chunk has been
|
|
325
|
+
* yielded; aborting `options.signal` throws its reason.
|
|
326
|
+
*/
|
|
327
|
+
readBatches(
|
|
328
|
+
streamId: string,
|
|
329
|
+
options?: StreamReadBatchesOptions
|
|
330
|
+
): AsyncGenerator<StreamChunk[], void, undefined>;
|
|
331
|
+
/** Read one stream's state and cursor, or null when it does not exist. */
|
|
332
|
+
status(streamId: string): Promise<StreamStatus | null>;
|
|
333
|
+
/** List streams, newest first. */
|
|
334
|
+
list(options?: StreamListOptions): Promise<StreamStatus[]>;
|
|
335
|
+
/**
|
|
336
|
+
* Delete one terminal stream and its chunk log.
|
|
337
|
+
*
|
|
338
|
+
* @returns True when a terminal stream was deleted; false when none
|
|
339
|
+
* exists. Throws on a live stream — settle it first.
|
|
340
|
+
*/
|
|
341
|
+
delete(streamId: string): Promise<boolean>;
|
|
342
|
+
/**
|
|
343
|
+
* @internal Synchronous storage operations for same-isolate first-party
|
|
344
|
+
* machinery — today the chat `ResumableStream` adapter, whose whole public
|
|
345
|
+
* surface is synchronous and constructed before the Lifecycle starts.
|
|
346
|
+
* Bypasses `lifecycle.ready()`: the caller owns startup ordering. The
|
|
347
|
+
* invariant-bearing writes (append fence, settlement, wakeups, events) go
|
|
348
|
+
* through the same private methods as the public API, so live readers and
|
|
349
|
+
* diagnostics observe aperture writes exactly like capability writes. Will
|
|
350
|
+
* break without notice; never use from application code.
|
|
351
|
+
*/
|
|
352
|
+
__DO_NOT_USE_WILL_BREAK__sync(): StreamsSyncInternal;
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
export {
|
|
356
|
+
StreamJson as a,
|
|
357
|
+
StreamReadBatchesOptions as c,
|
|
358
|
+
StreamState as d,
|
|
359
|
+
StreamStatus as f,
|
|
360
|
+
StreamChunk as i,
|
|
361
|
+
StreamReadOptions as l,
|
|
362
|
+
Streams as n,
|
|
363
|
+
StreamListOptions as o,
|
|
364
|
+
StreamWriter as p,
|
|
365
|
+
StreamsOptions as r,
|
|
366
|
+
StreamOpenOptions as s,
|
|
367
|
+
DEFAULT_MAX_CHUNK_BYTES as t,
|
|
368
|
+
StreamSettleOptions as u
|
|
369
|
+
};
|
|
370
|
+
//# sourceMappingURL=streams-D6tJ0NN9.d.ts.map
|