@trigger.dev/sdk 4.4.5 → 4.5.0-rc.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.
Files changed (161) hide show
  1. package/dist/commonjs/v3/agentSkillsRuntime.d.ts +28 -0
  2. package/dist/commonjs/v3/agentSkillsRuntime.js +163 -0
  3. package/dist/commonjs/v3/agentSkillsRuntime.js.map +1 -0
  4. package/dist/commonjs/v3/ai-shared.d.ts +173 -0
  5. package/dist/commonjs/v3/ai-shared.js +25 -0
  6. package/dist/commonjs/v3/ai-shared.js.map +1 -0
  7. package/dist/commonjs/v3/ai.d.ts +2823 -5
  8. package/dist/commonjs/v3/ai.js +6197 -13
  9. package/dist/commonjs/v3/ai.js.map +1 -1
  10. package/dist/commonjs/v3/auth.d.ts +9 -0
  11. package/dist/commonjs/v3/auth.js.map +1 -1
  12. package/dist/commonjs/v3/chat-client.d.ts +301 -0
  13. package/dist/commonjs/v3/chat-client.js +624 -0
  14. package/dist/commonjs/v3/chat-client.js.map +1 -0
  15. package/dist/commonjs/v3/chat-react.d.ts +155 -0
  16. package/dist/commonjs/v3/chat-react.js +330 -0
  17. package/dist/commonjs/v3/chat-react.js.map +1 -0
  18. package/dist/commonjs/v3/chat-server.d.ts +206 -0
  19. package/dist/commonjs/v3/chat-server.js +737 -0
  20. package/dist/commonjs/v3/chat-server.js.map +1 -0
  21. package/dist/commonjs/v3/chat-server.test.d.ts +1 -0
  22. package/dist/commonjs/v3/chat-server.test.js +518 -0
  23. package/dist/commonjs/v3/chat-server.test.js.map +1 -0
  24. package/dist/commonjs/v3/chat-tab-coordinator.d.ts +65 -0
  25. package/dist/commonjs/v3/chat-tab-coordinator.js +235 -0
  26. package/dist/commonjs/v3/chat-tab-coordinator.js.map +1 -0
  27. package/dist/commonjs/v3/chat-tab-coordinator.test.d.ts +1 -0
  28. package/dist/commonjs/v3/chat-tab-coordinator.test.js +140 -0
  29. package/dist/commonjs/v3/chat-tab-coordinator.test.js.map +1 -0
  30. package/dist/commonjs/v3/chat.d.ts +437 -0
  31. package/dist/commonjs/v3/chat.js +968 -0
  32. package/dist/commonjs/v3/chat.js.map +1 -0
  33. package/dist/commonjs/v3/chat.test.d.ts +1 -0
  34. package/dist/commonjs/v3/chat.test.js +1180 -0
  35. package/dist/commonjs/v3/chat.test.js.map +1 -0
  36. package/dist/commonjs/v3/createStartSessionAction.test.d.ts +1 -0
  37. package/dist/commonjs/v3/createStartSessionAction.test.js +113 -0
  38. package/dist/commonjs/v3/createStartSessionAction.test.js.map +1 -0
  39. package/dist/commonjs/v3/deployments.d.ts +26 -0
  40. package/dist/commonjs/v3/deployments.js +37 -0
  41. package/dist/commonjs/v3/deployments.js.map +1 -0
  42. package/dist/commonjs/v3/index.d.ts +6 -3
  43. package/dist/commonjs/v3/index.js +7 -1
  44. package/dist/commonjs/v3/index.js.map +1 -1
  45. package/dist/commonjs/v3/runs.d.ts +22 -7
  46. package/dist/commonjs/v3/runs.js +1 -0
  47. package/dist/commonjs/v3/runs.js.map +1 -1
  48. package/dist/commonjs/v3/sessions.d.ts +228 -0
  49. package/dist/commonjs/v3/sessions.js +664 -0
  50. package/dist/commonjs/v3/sessions.js.map +1 -0
  51. package/dist/commonjs/v3/sessions.test.d.ts +1 -0
  52. package/dist/commonjs/v3/sessions.test.js +154 -0
  53. package/dist/commonjs/v3/sessions.test.js.map +1 -0
  54. package/dist/commonjs/v3/shared.d.ts +24 -2
  55. package/dist/commonjs/v3/shared.js +189 -1
  56. package/dist/commonjs/v3/shared.js.map +1 -1
  57. package/dist/commonjs/v3/skill.d.ts +99 -0
  58. package/dist/commonjs/v3/skill.js +155 -0
  59. package/dist/commonjs/v3/skill.js.map +1 -0
  60. package/dist/commonjs/v3/skills.d.ts +2 -0
  61. package/dist/commonjs/v3/skills.js +6 -0
  62. package/dist/commonjs/v3/skills.js.map +1 -0
  63. package/dist/commonjs/v3/streams.js +127 -19
  64. package/dist/commonjs/v3/streams.js.map +1 -1
  65. package/dist/commonjs/v3/tasks.d.ts +2 -1
  66. package/dist/commonjs/v3/tasks.js +1 -0
  67. package/dist/commonjs/v3/tasks.js.map +1 -1
  68. package/dist/commonjs/v3/test/index.d.ts +3 -0
  69. package/dist/commonjs/v3/test/index.js +18 -0
  70. package/dist/commonjs/v3/test/index.js.map +1 -0
  71. package/dist/commonjs/v3/test/mock-chat-agent.d.ts +259 -0
  72. package/dist/commonjs/v3/test/mock-chat-agent.js +468 -0
  73. package/dist/commonjs/v3/test/mock-chat-agent.js.map +1 -0
  74. package/dist/commonjs/v3/test/setup-catalog.d.ts +1 -0
  75. package/dist/commonjs/v3/test/setup-catalog.js +18 -0
  76. package/dist/commonjs/v3/test/setup-catalog.js.map +1 -0
  77. package/dist/commonjs/v3/test/test-session-handle.d.ts +53 -0
  78. package/dist/commonjs/v3/test/test-session-handle.js +256 -0
  79. package/dist/commonjs/v3/test/test-session-handle.js.map +1 -0
  80. package/dist/commonjs/version.js +1 -1
  81. package/dist/esm/v3/agentSkillsRuntime.d.ts +28 -0
  82. package/dist/esm/v3/agentSkillsRuntime.js +136 -0
  83. package/dist/esm/v3/agentSkillsRuntime.js.map +1 -0
  84. package/dist/esm/v3/ai-shared.d.ts +173 -0
  85. package/dist/esm/v3/ai-shared.js +22 -0
  86. package/dist/esm/v3/ai-shared.js.map +1 -0
  87. package/dist/esm/v3/ai.d.ts +2823 -5
  88. package/dist/esm/v3/ai.js +6187 -14
  89. package/dist/esm/v3/ai.js.map +1 -1
  90. package/dist/esm/v3/auth.d.ts +9 -0
  91. package/dist/esm/v3/auth.js.map +1 -1
  92. package/dist/esm/v3/chat-client.d.ts +301 -0
  93. package/dist/esm/v3/chat-client.js +619 -0
  94. package/dist/esm/v3/chat-client.js.map +1 -0
  95. package/dist/esm/v3/chat-react.d.ts +155 -0
  96. package/dist/esm/v3/chat-react.js +325 -0
  97. package/dist/esm/v3/chat-react.js.map +1 -0
  98. package/dist/esm/v3/chat-server.d.ts +206 -0
  99. package/dist/esm/v3/chat-server.js +734 -0
  100. package/dist/esm/v3/chat-server.js.map +1 -0
  101. package/dist/esm/v3/chat-server.test.d.ts +1 -0
  102. package/dist/esm/v3/chat-server.test.js +516 -0
  103. package/dist/esm/v3/chat-server.test.js.map +1 -0
  104. package/dist/esm/v3/chat-tab-coordinator.d.ts +65 -0
  105. package/dist/esm/v3/chat-tab-coordinator.js +231 -0
  106. package/dist/esm/v3/chat-tab-coordinator.js.map +1 -0
  107. package/dist/esm/v3/chat-tab-coordinator.test.d.ts +1 -0
  108. package/dist/esm/v3/chat-tab-coordinator.test.js +138 -0
  109. package/dist/esm/v3/chat-tab-coordinator.test.js.map +1 -0
  110. package/dist/esm/v3/chat.d.ts +437 -0
  111. package/dist/esm/v3/chat.js +961 -0
  112. package/dist/esm/v3/chat.js.map +1 -0
  113. package/dist/esm/v3/chat.test.d.ts +1 -0
  114. package/dist/esm/v3/chat.test.js +1178 -0
  115. package/dist/esm/v3/chat.test.js.map +1 -0
  116. package/dist/esm/v3/createStartSessionAction.test.d.ts +1 -0
  117. package/dist/esm/v3/createStartSessionAction.test.js +111 -0
  118. package/dist/esm/v3/createStartSessionAction.test.js.map +1 -0
  119. package/dist/esm/v3/deployments.d.ts +26 -0
  120. package/dist/esm/v3/deployments.js +34 -0
  121. package/dist/esm/v3/deployments.js.map +1 -0
  122. package/dist/esm/v3/index.d.ts +6 -3
  123. package/dist/esm/v3/index.js +4 -1
  124. package/dist/esm/v3/index.js.map +1 -1
  125. package/dist/esm/v3/runs.d.ts +15 -0
  126. package/dist/esm/v3/runs.js +1 -0
  127. package/dist/esm/v3/runs.js.map +1 -1
  128. package/dist/esm/v3/sessions.d.ts +228 -0
  129. package/dist/esm/v3/sessions.js +656 -0
  130. package/dist/esm/v3/sessions.js.map +1 -0
  131. package/dist/esm/v3/sessions.test.d.ts +1 -0
  132. package/dist/esm/v3/sessions.test.js +152 -0
  133. package/dist/esm/v3/sessions.test.js.map +1 -0
  134. package/dist/esm/v3/shared.d.ts +24 -2
  135. package/dist/esm/v3/shared.js +188 -1
  136. package/dist/esm/v3/shared.js.map +1 -1
  137. package/dist/esm/v3/skill.d.ts +99 -0
  138. package/dist/esm/v3/skill.js +128 -0
  139. package/dist/esm/v3/skill.js.map +1 -0
  140. package/dist/esm/v3/skills.d.ts +2 -0
  141. package/dist/esm/v3/skills.js +2 -0
  142. package/dist/esm/v3/skills.js.map +1 -0
  143. package/dist/esm/v3/streams.js +127 -20
  144. package/dist/esm/v3/streams.js.map +1 -1
  145. package/dist/esm/v3/tasks.d.ts +2 -1
  146. package/dist/esm/v3/tasks.js +2 -1
  147. package/dist/esm/v3/tasks.js.map +1 -1
  148. package/dist/esm/v3/test/index.d.ts +3 -0
  149. package/dist/esm/v3/test/index.js +13 -0
  150. package/dist/esm/v3/test/index.js.map +1 -0
  151. package/dist/esm/v3/test/mock-chat-agent.d.ts +259 -0
  152. package/dist/esm/v3/test/mock-chat-agent.js +465 -0
  153. package/dist/esm/v3/test/mock-chat-agent.js.map +1 -0
  154. package/dist/esm/v3/test/setup-catalog.d.ts +1 -0
  155. package/dist/esm/v3/test/setup-catalog.js +16 -0
  156. package/dist/esm/v3/test/setup-catalog.js.map +1 -0
  157. package/dist/esm/v3/test/test-session-handle.d.ts +53 -0
  158. package/dist/esm/v3/test/test-session-handle.js +251 -0
  159. package/dist/esm/v3/test/test-session-handle.js.map +1 -0
  160. package/dist/esm/version.js +1 -1
  161. package/package.json +87 -6
@@ -0,0 +1,664 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionInputChannel = exports.SessionOutputChannel = exports.SessionHandle = exports.sessions = void 0;
4
+ exports.__setSessionOpenImplForTests = __setSessionOpenImplForTests;
5
+ exports.__setSessionStartImplForTests = __setSessionStartImplForTests;
6
+ const v3_1 = require("@trigger.dev/core/v3");
7
+ const ioSerialization_1 = require("@trigger.dev/core/v3/utils/ioSerialization");
8
+ const api_1 = require("@opentelemetry/api");
9
+ const tracer_js_1 = require("./tracer.js");
10
+ exports.sessions = {
11
+ start: startSession,
12
+ retrieve: retrieveSession,
13
+ update: updateSession,
14
+ close: closeSession,
15
+ list: listSessions,
16
+ open,
17
+ };
18
+ let sessionOpenImpl;
19
+ function __setSessionOpenImplForTests(impl) {
20
+ sessionOpenImpl = impl;
21
+ }
22
+ let sessionStartImpl;
23
+ function __setSessionStartImplForTests(impl) {
24
+ sessionStartImpl = impl;
25
+ }
26
+ /**
27
+ * Start a {@link Session} — a durable, task-bound, bidirectional I/O
28
+ * primitive. The server creates the row (idempotent on `externalId`)
29
+ * and triggers the first run from `triggerConfig` in one round-trip.
30
+ * Returns the new run's id and a session-scoped public access token
31
+ * for browser-side use against `.in/append`, `.out` SSE, and
32
+ * `end-and-continue`.
33
+ *
34
+ * If a session with the same `(env, externalId)` already exists,
35
+ * returns the existing row plus the live (or freshly re-triggered) run.
36
+ * Two browser tabs of the same chat converge to one session.
37
+ */
38
+ function startSession(body, requestOptions) {
39
+ if (sessionStartImpl) {
40
+ const result = sessionStartImpl(body);
41
+ return Promise.resolve(result);
42
+ }
43
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
44
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
45
+ tracer: tracer_js_1.tracer,
46
+ name: "sessions.start()",
47
+ icon: "sessions",
48
+ attributes: sessionAttributes(body.externalId ?? body.type, {
49
+ type: body.type,
50
+ ...(body.externalId ? { externalId: body.externalId } : {}),
51
+ }),
52
+ }, requestOptions);
53
+ return apiClient.createSession(body, $requestOptions);
54
+ }
55
+ /**
56
+ * Retrieve a Session by `friendlyId` (`session_*`) or user-supplied
57
+ * `externalId`. The server disambiguates via the `session_` prefix.
58
+ */
59
+ function retrieveSession(sessionIdOrExternalId, requestOptions) {
60
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
61
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
62
+ tracer: tracer_js_1.tracer,
63
+ name: "sessions.retrieve()",
64
+ icon: "sessions",
65
+ attributes: sessionAttributes(sessionIdOrExternalId),
66
+ }, requestOptions);
67
+ return apiClient.retrieveSession(sessionIdOrExternalId, $requestOptions);
68
+ }
69
+ /** Update mutable fields on a Session (tags, metadata, externalId). */
70
+ function updateSession(sessionIdOrExternalId, body, requestOptions) {
71
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
72
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
73
+ tracer: tracer_js_1.tracer,
74
+ name: "sessions.update()",
75
+ icon: "sessions",
76
+ attributes: sessionAttributes(sessionIdOrExternalId),
77
+ }, requestOptions);
78
+ return apiClient.updateSession(sessionIdOrExternalId, body, $requestOptions);
79
+ }
80
+ /** Mark a Session as closed (terminal, idempotent). */
81
+ function closeSession(sessionIdOrExternalId, body, requestOptions) {
82
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
83
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
84
+ tracer: tracer_js_1.tracer,
85
+ name: "sessions.close()",
86
+ icon: "sessions",
87
+ attributes: sessionAttributes(sessionIdOrExternalId, {
88
+ ...(body?.reason ? { reason: body.reason } : {}),
89
+ }),
90
+ }, requestOptions);
91
+ return apiClient.closeSession(sessionIdOrExternalId, body, $requestOptions);
92
+ }
93
+ /**
94
+ * List Sessions in the current environment with filters + cursor pagination.
95
+ * Returns a {@link CursorPagePromise} so callers can iterate pages with
96
+ * `for await`.
97
+ */
98
+ function listSessions(options, requestOptions) {
99
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
100
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
101
+ tracer: tracer_js_1.tracer,
102
+ name: "sessions.list()",
103
+ icon: "sessions",
104
+ attributes: {
105
+ ...(options?.type ? { type: toAttr(options.type) } : {}),
106
+ ...(options?.tag ? { tag: toAttr(options.tag) } : {}),
107
+ ...(options?.status ? { status: toAttr(options.status) } : {}),
108
+ ...(options?.externalId ? { externalId: options.externalId } : {}),
109
+ },
110
+ }, requestOptions);
111
+ return apiClient.listSessions(options, $requestOptions);
112
+ }
113
+ /**
114
+ * Open a lightweight handle to a Session's realtime channels. Does not
115
+ * perform a network call on its own — each channel method hits the
116
+ * corresponding realtime endpoint.
117
+ */
118
+ function open(sessionIdOrExternalId) {
119
+ if (sessionOpenImpl)
120
+ return sessionOpenImpl(sessionIdOrExternalId);
121
+ return new SessionHandle(sessionIdOrExternalId);
122
+ }
123
+ class SessionHandle {
124
+ id;
125
+ /**
126
+ * Producer-to-consumer channel: the task writes records; external
127
+ * clients read them. Mirrors `streams.define` — `append` / `pipe` /
128
+ * `writer` / `read`.
129
+ */
130
+ out;
131
+ /**
132
+ * Consumer-to-producer channel: external clients call `.send()`; the
133
+ * task consumes via `.on` / `.once` / `.peek` / `.wait` /
134
+ * `.waitWithIdleTimeout`. Mirrors `streams.input` but keyed on the
135
+ * session so a conversation can survive across run boundaries.
136
+ */
137
+ in;
138
+ constructor(id, overrides) {
139
+ this.id = id;
140
+ this.out = overrides?.out ?? new SessionOutputChannel(id);
141
+ this.in = overrides?.in ?? new SessionInputChannel(id);
142
+ }
143
+ }
144
+ exports.SessionHandle = SessionHandle;
145
+ /**
146
+ * The `.out` side of a Session's bidirectional channel pair. Mirrors the
147
+ * consume-side of {@link streams.define}: `pipe` / `writer` / `append`
148
+ * for the task to produce records, `read` for external clients to
149
+ * consume via SSE. S2 credentials for direct writes are fetched
150
+ * internally by `pipe`/`writer` — there's no public `initialize()`.
151
+ */
152
+ class SessionOutputChannel {
153
+ sessionId;
154
+ // Cache of the in-flight / resolved `initializeSessionStream` PUT for
155
+ // this channel. Every `pipe()` / `writer()` call needs the same S2
156
+ // credentials, so we share a single promise instead of re-PUTing on
157
+ // every chunk. Hot-loop writers (per-chunk `chat.response.write` /
158
+ // direct `session.out.writer` calls) drop from N PUTs to 1 PUT for
159
+ // the lifetime of the channel. The S2 access token has a 1-day TTL
160
+ // server-side so reusing it across calls within a single run is safe.
161
+ // Evicts on failure (so the next call retries) and on `reset()`.
162
+ #initPromise;
163
+ constructor(sessionId) {
164
+ this.sessionId = sessionId;
165
+ }
166
+ /**
167
+ * Drop the cached `initializeSessionStream` response. Surfaces for
168
+ * tests and lifecycle hooks that need the next write to re-mint S2
169
+ * credentials. The cache also self-evicts on `initializeSession`
170
+ * rejection, so callers don't need to invoke this on failures.
171
+ *
172
+ * @internal
173
+ */
174
+ reset() {
175
+ this.#initPromise = undefined;
176
+ }
177
+ /**
178
+ * Append a single record. Routes through {@link writer} internally so
179
+ * subscribers receive the same parsed-object shape as multi-record
180
+ * writes — the server-side append endpoint wraps the body in a string,
181
+ * which would give SSE consumers a JSON-string instead of an object.
182
+ * Mirrors how `streams.define.append` delegates to `streams.writer`.
183
+ */
184
+ async append(value, options) {
185
+ const { waitUntilComplete } = this.writer({
186
+ ...options,
187
+ spanName: "sessions.append()",
188
+ execute: ({ write }) => {
189
+ write(value);
190
+ },
191
+ });
192
+ await waitUntilComplete();
193
+ }
194
+ /**
195
+ * Pipe an `AsyncIterable` / `ReadableStream` directly to S2. Fetches
196
+ * session S2 credentials internally and streams through
197
+ * {@link SessionStreamInstance}. Parallel to {@link streams.pipe} but
198
+ * session-scoped — no `target` option because the session is the target.
199
+ */
200
+ pipe(value, options) {
201
+ return this.#pipeInternal(value, options, "sessions.pipe()");
202
+ }
203
+ /**
204
+ * Mirror of {@link streams.writer}: runs `execute({ write, merge })`
205
+ * against an in-memory queue whose records are piped to S2. Returns
206
+ * `{ stream, waitUntilComplete }` so callers can observe the local
207
+ * stream and await completion. Span is collapsible via `options.spanName`
208
+ * / `options.collapsed`.
209
+ */
210
+ writer(options) {
211
+ let controller;
212
+ const ongoingStreamPromises = [];
213
+ const stream = new ReadableStream({
214
+ start(controllerArg) {
215
+ controller = controllerArg;
216
+ },
217
+ });
218
+ const safeEnqueue = (data) => {
219
+ try {
220
+ controller.enqueue(data);
221
+ }
222
+ catch {
223
+ // Suppress errors when the stream has been closed.
224
+ }
225
+ };
226
+ try {
227
+ const result = options.execute({
228
+ write(part) {
229
+ safeEnqueue(part);
230
+ },
231
+ merge(streamArg) {
232
+ ongoingStreamPromises.push((async () => {
233
+ const reader = streamArg.getReader();
234
+ while (true) {
235
+ const { done, value } = await reader.read();
236
+ if (done)
237
+ break;
238
+ safeEnqueue(value);
239
+ }
240
+ })().catch((error) => {
241
+ console.error(error);
242
+ }));
243
+ },
244
+ });
245
+ if (result) {
246
+ ongoingStreamPromises.push(result.catch((error) => {
247
+ console.error(error);
248
+ }));
249
+ }
250
+ }
251
+ catch (error) {
252
+ console.error(error);
253
+ }
254
+ const waitForStreams = new Promise((resolve, reject) => {
255
+ (async () => {
256
+ while (ongoingStreamPromises.length > 0) {
257
+ await ongoingStreamPromises.shift();
258
+ }
259
+ resolve();
260
+ })().catch(reject);
261
+ });
262
+ waitForStreams.finally(() => {
263
+ try {
264
+ controller.close();
265
+ }
266
+ catch {
267
+ // Already closed.
268
+ }
269
+ });
270
+ return this.#pipeInternal(stream, options, options.spanName ?? "sessions.writer()");
271
+ }
272
+ /**
273
+ * Subscribe to SSE records on `.out`. Returns an async-iterable stream —
274
+ * auto-retry, Last-Event-ID resume, and abort propagation come from the
275
+ * shared {@link SSEStreamSubscription} plumbing used by run-scoped
276
+ * realtime streams.
277
+ */
278
+ async read(options) {
279
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
280
+ return apiClient.subscribeToSessionStream(this.sessionId, "out", {
281
+ signal: options?.signal,
282
+ timeoutInSeconds: options?.timeoutInSeconds,
283
+ lastEventId: options?.lastEventId != null ? String(options.lastEventId) : undefined,
284
+ onPart: options?.onPart,
285
+ onControl: options?.onControl,
286
+ onComplete: options?.onComplete,
287
+ onError: options?.onError,
288
+ });
289
+ }
290
+ #pipeInternal(value, options, spanName) {
291
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
292
+ const collapsed = options?.collapsed;
293
+ const span = tracer_js_1.tracer.startSpan(spanName, {
294
+ attributes: {
295
+ session: this.sessionId,
296
+ io: "out",
297
+ [v3_1.SemanticInternalAttributes.ENTITY_TYPE]: "session-stream",
298
+ [v3_1.SemanticInternalAttributes.ENTITY_ID]: `${this.sessionId}:out`,
299
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "sessions",
300
+ ...(collapsed ? { [v3_1.SemanticInternalAttributes.COLLAPSED]: true } : {}),
301
+ ...(0, v3_1.accessoryAttributes)({
302
+ items: [{ text: `${this.sessionId}.out`, variant: "normal" }],
303
+ style: "codepath",
304
+ }),
305
+ },
306
+ });
307
+ const readableStreamSource = (0, v3_1.ensureReadableStream)(value);
308
+ const abortController = new AbortController();
309
+ // `AbortSignal.any` lands in Node 20.3; the SDK still supports Node
310
+ // 18.20+. On older runtimes fall back to wiring `options.signal` into
311
+ // `abortController` manually so caller-driven cancellation propagates.
312
+ let combinedSignal = abortController.signal;
313
+ // Set in the Node 18 fallback path so the caller's `signal.addEventListener`
314
+ // registration can be cleared once the stream finishes. Without this, a
315
+ // long-lived caller signal (e.g. one reused across many `writer()` calls)
316
+ // accumulates listeners on every completed turn.
317
+ let removeCallerAbortListener;
318
+ if (options?.signal) {
319
+ if (typeof AbortSignal.any === "function") {
320
+ combinedSignal = AbortSignal.any([options.signal, abortController.signal]);
321
+ }
322
+ else {
323
+ const callerSignal = options.signal;
324
+ if (callerSignal.aborted) {
325
+ abortController.abort(callerSignal.reason);
326
+ }
327
+ else {
328
+ const onCallerAbort = () => abortController.abort(callerSignal.reason);
329
+ callerSignal.addEventListener("abort", onCallerAbort, { once: true });
330
+ removeCallerAbortListener = () => callerSignal.removeEventListener("abort", onCallerAbort);
331
+ }
332
+ }
333
+ }
334
+ // Resolve the init promise eagerly so we can capture which one this
335
+ // writer uses for reactive invalidation below.
336
+ const writerInitPromise = (() => {
337
+ if (this.#initPromise) {
338
+ return this.#initPromise;
339
+ }
340
+ const fresh = apiClient.initializeSessionStream(this.sessionId, "out", options?.requestOptions);
341
+ this.#initPromise = fresh;
342
+ // Evict on failure so the next call retries instead of returning a
343
+ // poisoned cache entry forever.
344
+ fresh.catch((err) => {
345
+ if (this.#initPromise === fresh) {
346
+ this.#initPromise = undefined;
347
+ }
348
+ });
349
+ return fresh;
350
+ })();
351
+ try {
352
+ const instance = new v3_1.SessionStreamInstance({
353
+ apiClient,
354
+ baseUrl: v3_1.apiClientManager.baseURL ?? "",
355
+ sessionId: this.sessionId,
356
+ io: "out",
357
+ source: readableStreamSource,
358
+ signal: combinedSignal,
359
+ requestOptions: options?.requestOptions,
360
+ initializeSession: () => writerInitPromise,
361
+ });
362
+ // Single internal chain that handles span lifecycle AND reactive
363
+ // invalidation. On rejection we evict the cached init promise so
364
+ // the next pipe()/writer() re-PUTs and recovers (e.g. when a
365
+ // cached S2 access token expired mid-process). Compare by identity
366
+ // so a concurrent caller's fresh promise isn't accidentally cleared.
367
+ // Customer awaiters still observe the rejection via the returned
368
+ // `waitUntilComplete()`; this chain just keeps the cleanup path
369
+ // from surfacing as unhandled.
370
+ instance.wait().then(() => {
371
+ removeCallerAbortListener?.();
372
+ span.end();
373
+ }, () => {
374
+ removeCallerAbortListener?.();
375
+ if (this.#initPromise === writerInitPromise) {
376
+ this.#initPromise = undefined;
377
+ }
378
+ span.end();
379
+ });
380
+ return {
381
+ stream: instance.stream,
382
+ waitUntilComplete: async () => {
383
+ return instance.wait();
384
+ },
385
+ };
386
+ }
387
+ catch (error) {
388
+ removeCallerAbortListener?.();
389
+ if (error instanceof Error && error.name === "AbortError") {
390
+ span.end();
391
+ throw error;
392
+ }
393
+ if (error instanceof Error || typeof error === "string") {
394
+ span.recordException(error);
395
+ }
396
+ else {
397
+ span.recordException(String(error));
398
+ }
399
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR });
400
+ span.end();
401
+ throw error;
402
+ }
403
+ }
404
+ /**
405
+ * Write a single Trigger control record to `.out`. The record carries a
406
+ * `trigger-control` header valued with `subtype` plus any sibling
407
+ * `extraHeaders`; the body is empty. Control records are filtered out of
408
+ * the consumer-facing chunk stream by the SDK transport — readers route
409
+ * them via the `onControl` callback instead.
410
+ *
411
+ * The returned `lastEventId` is the S2 seq_num of the written record,
412
+ * useful for trim chains (e.g. trim back to the previous turn-complete).
413
+ */
414
+ async writeControl(subtype, extraHeaders) {
415
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
416
+ return (0, v3_1.writeSessionControlRecord)(apiClient, this.sessionId, "out", subtype, extraHeaders);
417
+ }
418
+ /**
419
+ * Append an S2 `trim` command record to `.out`. Records with seq_num
420
+ * less than `earliestSeqNum` are eventually removed from the stream.
421
+ *
422
+ * Idempotent and monotonic at S2's layer (`max(existing, min(provided,
423
+ * current_tail))`) — backward trims are silently no-ops for deletion
424
+ * but still consume a seq_num. Used by `chat.agent`'s turn loop to
425
+ * keep `session.out` bounded to roughly one turn at steady state.
426
+ */
427
+ async trimTo(earliestSeqNum) {
428
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
429
+ await (0, v3_1.trimSessionStream)(apiClient, this.sessionId, earliestSeqNum);
430
+ }
431
+ }
432
+ exports.SessionOutputChannel = SessionOutputChannel;
433
+ /**
434
+ * The `.in` side of a Session's bidirectional channel pair. Mirrors
435
+ * {@link streams.input} — consumer-side primitives for the task
436
+ * (`on`/`once`/`peek`/`wait`/`waitWithIdleTimeout`) plus `send` for
437
+ * external clients. Keyed on the session rather than the run so a
438
+ * conversation can survive across run boundaries.
439
+ */
440
+ class SessionInputChannel {
441
+ sessionId;
442
+ constructor(sessionId) {
443
+ this.sessionId = sessionId;
444
+ }
445
+ /**
446
+ * Send a single record to the channel. Called by external clients
447
+ * (browser, server action, another task) producing input for the run.
448
+ * Matches {@link streams.input.send} but session-scoped — the session
449
+ * is the address, no `runId` required.
450
+ */
451
+ async send(value, requestOptions) {
452
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
453
+ const body = typeof value === "string" ? value : JSON.stringify(value);
454
+ const $requestOptions = (0, v3_1.mergeRequestOptions)({
455
+ tracer: tracer_js_1.tracer,
456
+ name: `sessions.open(${this.sessionId}).in.send()`,
457
+ icon: "sessions",
458
+ attributes: sessionAttributes(this.sessionId, { io: "in" }),
459
+ }, requestOptions);
460
+ await apiClient.appendToSessionStream(this.sessionId, "in", body, $requestOptions);
461
+ }
462
+ /**
463
+ * Register a handler that fires for every record landing on `.in`.
464
+ * Handlers are flushed with any buffered records on attach and cleaned
465
+ * up automatically when the task run completes. Returns `{ off }` to
466
+ * unsubscribe early.
467
+ */
468
+ on(handler) {
469
+ return v3_1.sessionStreams.on(this.sessionId, "in", handler);
470
+ }
471
+ /**
472
+ * Wait for the next record on `.in` without suspending the run.
473
+ * Returns `{ ok: true, output }` on arrival or `{ ok: false, error }`
474
+ * when the timeout fires. Chain `.unwrap()` to get the data directly.
475
+ */
476
+ once(options) {
477
+ const ctx = v3_1.taskContext.ctx;
478
+ const runId = ctx?.run.id;
479
+ const innerPromise = v3_1.sessionStreams.once(this.sessionId, "in", options);
480
+ return new v3_1.InputStreamOncePromise((resolve, reject) => {
481
+ tracer_js_1.tracer
482
+ .startActiveSpan(options?.spanName ?? `sessions.open(${this.sessionId}).in.once()`, async () => {
483
+ const result = await innerPromise;
484
+ resolve(result);
485
+ }, {
486
+ attributes: {
487
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "sessions",
488
+ [v3_1.SemanticInternalAttributes.ENTITY_TYPE]: "session-stream",
489
+ ...(runId
490
+ ? { [v3_1.SemanticInternalAttributes.ENTITY_ID]: `${runId}:${this.sessionId}:in` }
491
+ : {}),
492
+ session: this.sessionId,
493
+ io: "in",
494
+ ...(0, v3_1.accessoryAttributes)({
495
+ items: [{ text: `${this.sessionId}.in`, variant: "normal" }],
496
+ style: "codepath",
497
+ }),
498
+ },
499
+ })
500
+ .catch(reject);
501
+ });
502
+ }
503
+ /** Non-blocking peek at the head of the `.in` buffer. */
504
+ peek() {
505
+ return v3_1.sessionStreams.peek(this.sessionId, "in");
506
+ }
507
+ /**
508
+ * The highest S2 sequence number of any record this channel has
509
+ * delivered to a `once()` / `wait()` consumer (or had shifted off its
510
+ * buffer into one). Distinct from "last received" — buffered-but-not-
511
+ * yet-consumed records don't count.
512
+ *
513
+ * Used by `chat.agent` to persist the `.in` resume cursor on each
514
+ * `turn-complete` control record, so the next worker boot can subscribe
515
+ * past already-processed user messages.
516
+ */
517
+ lastDispatchedSeqNum() {
518
+ return v3_1.sessionStreams.lastDispatchedSeqNum(this.sessionId, "in");
519
+ }
520
+ /**
521
+ * Suspend the current run until the next record arrives on `.in`.
522
+ * Unlike {@link once}, `wait()` frees compute while blocked — the
523
+ * run-engine waitpoint holds the run until the session append handler
524
+ * fires it. Only callable from inside `task.run()`.
525
+ */
526
+ wait(options) {
527
+ return new v3_1.ManualWaitpointPromise(async (resolve, reject) => {
528
+ try {
529
+ const ctx = v3_1.taskContext.ctx;
530
+ if (!ctx) {
531
+ throw new Error("session.in.wait() can only be used from inside a task.run()");
532
+ }
533
+ const apiClient = v3_1.apiClientManager.clientOrThrow();
534
+ const response = await apiClient.createSessionStreamWaitpoint(ctx.run.id, {
535
+ session: this.sessionId,
536
+ io: "in",
537
+ timeout: options?.timeout,
538
+ idempotencyKey: options?.idempotencyKey,
539
+ idempotencyKeyTTL: options?.idempotencyKeyTTL,
540
+ tags: options?.tags,
541
+ lastSeqNum: v3_1.sessionStreams.lastSeqNum(this.sessionId, "in"),
542
+ });
543
+ const result = await tracer_js_1.tracer.startActiveSpan(options?.spanName ?? `sessions.open(${this.sessionId}).in.wait()`, async (span) => {
544
+ const waitResponse = await apiClient.waitForWaitpointToken({
545
+ runFriendlyId: ctx.run.id,
546
+ waitpointFriendlyId: response.waitpointId,
547
+ });
548
+ if (!waitResponse.success) {
549
+ throw new Error("Failed to block on session stream waitpoint");
550
+ }
551
+ // Drop the SSE tail + buffer before suspending so the record
552
+ // delivered via the waitpoint path isn't re-buffered on resume.
553
+ v3_1.sessionStreams.disconnectStream(this.sessionId, "in");
554
+ const waitResult = await v3_1.runtime.waitUntil(response.waitpointId);
555
+ const data = waitResult.output !== undefined
556
+ ? await (0, ioSerialization_1.conditionallyImportAndParsePacket)({
557
+ data: waitResult.output,
558
+ dataType: waitResult.outputType ?? "application/json",
559
+ }, apiClient)
560
+ : undefined;
561
+ if (waitResult.ok) {
562
+ // Advance the seq counter so the SSE tail doesn't replay the
563
+ // record that was consumed via the waitpoint.
564
+ const prevSeq = v3_1.sessionStreams.lastSeqNum(this.sessionId, "in");
565
+ const nextSeq = (prevSeq ?? -1) + 1;
566
+ v3_1.sessionStreams.setLastSeqNum(this.sessionId, "in", nextSeq);
567
+ return { ok: true, output: data };
568
+ }
569
+ else {
570
+ const error = new v3_1.WaitpointTimeoutError(data?.message ?? "Timed out");
571
+ span.recordException(error);
572
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR });
573
+ return { ok: false, error };
574
+ }
575
+ }, {
576
+ attributes: {
577
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "wait",
578
+ [v3_1.SemanticInternalAttributes.ENTITY_TYPE]: "waitpoint",
579
+ [v3_1.SemanticInternalAttributes.ENTITY_ID]: response.waitpointId,
580
+ session: this.sessionId,
581
+ io: "in",
582
+ ...(0, v3_1.accessoryAttributes)({
583
+ items: [{ text: `${this.sessionId}.in`, variant: "normal" }],
584
+ style: "codepath",
585
+ }),
586
+ },
587
+ });
588
+ resolve(result);
589
+ }
590
+ catch (error) {
591
+ reject(error);
592
+ }
593
+ });
594
+ }
595
+ /**
596
+ * Wait for a record with an idle-then-suspend strategy. Keeps the run
597
+ * active (using compute) for `idleTimeoutInSeconds`, then suspends via
598
+ * {@link wait} if nothing arrives. If a record arrives during the idle
599
+ * phase the run responds without suspending.
600
+ */
601
+ async waitWithIdleTimeout(options) {
602
+ const self = this;
603
+ const spanName = options.spanName ?? `sessions.open(${this.sessionId}).in.waitWithIdleTimeout()`;
604
+ return tracer_js_1.tracer.startActiveSpan(spanName, async (span) => {
605
+ if (options.idleTimeoutInSeconds > 0) {
606
+ const warm = await v3_1.sessionStreams.once(self.sessionId, "in", {
607
+ timeoutMs: options.idleTimeoutInSeconds * 1000,
608
+ });
609
+ if (warm.ok) {
610
+ span.setAttribute("wait.resolved", "idle");
611
+ return { ok: true, output: warm.output };
612
+ }
613
+ }
614
+ if (options.skipSuspend) {
615
+ // Match the cold-phase `self.wait()` result shape below so any
616
+ // caller that does `throw result.error` gets a real error
617
+ // instead of `undefined`.
618
+ span.setAttribute("wait.resolved", "skipped");
619
+ return {
620
+ ok: false,
621
+ error: new v3_1.WaitpointTimeoutError("Idle timeout elapsed and skipSuspend is set"),
622
+ };
623
+ }
624
+ if (options.onSuspend) {
625
+ await options.onSuspend();
626
+ }
627
+ span.setAttribute("wait.resolved", "suspended");
628
+ const waitResult = await self.wait({
629
+ timeout: options.timeout,
630
+ spanName: "suspended",
631
+ });
632
+ if (waitResult.ok && options.onResume) {
633
+ await options.onResume();
634
+ }
635
+ return waitResult;
636
+ }, {
637
+ attributes: {
638
+ [v3_1.SemanticInternalAttributes.STYLE_ICON]: "sessions",
639
+ session: self.sessionId,
640
+ io: "in",
641
+ ...(0, v3_1.accessoryAttributes)({
642
+ items: [{ text: `${self.sessionId}.in`, variant: "normal" }],
643
+ style: "codepath",
644
+ }),
645
+ },
646
+ });
647
+ }
648
+ }
649
+ exports.SessionInputChannel = SessionInputChannel;
650
+ // ─── helpers ────────────────────────────────────────────────────────
651
+ function sessionAttributes(id, extra) {
652
+ return {
653
+ session: id,
654
+ ...(extra ?? {}),
655
+ ...(0, v3_1.accessoryAttributes)({
656
+ items: [{ text: id, variant: "normal" }],
657
+ style: "codepath",
658
+ }),
659
+ };
660
+ }
661
+ function toAttr(value) {
662
+ return Array.isArray(value) ? value.join(",") : value;
663
+ }
664
+ //# sourceMappingURL=sessions.js.map