experimental-ash 0.61.0 → 0.63.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 (130) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/dist/docs/public/advanced/hooks.mdx +8 -2
  3. package/dist/docs/public/advanced/runs-and-streaming.md +6 -3
  4. package/dist/docs/public/advanced/typescript-api.md +32 -0
  5. package/dist/docs/public/channels/custom.mdx +23 -0
  6. package/dist/docs/public/frontend/README.md +8 -4
  7. package/dist/docs/public/frontend/meta.json +9 -1
  8. package/dist/docs/public/frontend/nextjs.md +4 -4
  9. package/dist/docs/public/frontend/nuxt.md +168 -0
  10. package/dist/docs/public/frontend/sveltekit.md +177 -0
  11. package/dist/docs/public/frontend/use-ash-agent-svelte.md +185 -0
  12. package/dist/docs/public/frontend/use-ash-agent-vue.md +236 -0
  13. package/dist/docs/public/frontend/use-ash-agent.md +14 -14
  14. package/dist/docs/public/getting-started.mdx +2 -0
  15. package/dist/skills/ash-add-agent/SKILL.md +29 -17
  16. package/dist/skills/ash-add-next/SKILL.md +58 -8
  17. package/dist/src/channel/websocket-upgrade-server.d.ts +26 -0
  18. package/dist/src/channel/websocket-upgrade-server.js +1 -0
  19. package/dist/src/chunks/use-ash-agent-BQJLh7KU.js +1224 -0
  20. package/dist/src/chunks/use-ash-agent-CRWVA4i-.js +1192 -0
  21. package/dist/src/client/ash-agent-store.d.ts +61 -0
  22. package/dist/src/client/ash-agent-store.js +2 -0
  23. package/dist/src/client/index.d.ts +2 -0
  24. package/dist/src/client/index.js +1 -1
  25. package/dist/src/compiled/.vendor-stamp.json +9 -9
  26. package/dist/src/compiled/@ai-sdk/anthropic/_provider-utils.d.ts +1 -1
  27. package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +3 -3
  28. package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
  29. package/dist/src/compiled/@ai-sdk/google/index.d.ts +52 -2
  30. package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
  31. package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
  32. package/dist/src/compiled/@ai-sdk/openai/index.d.ts +32 -2
  33. package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
  34. package/dist/src/compiled/@ai-sdk/provider/index.d.ts +507 -1
  35. package/dist/src/compiled/@chat-adapter/slack/index.js +25 -25
  36. package/dist/src/compiled/@workflow/core/events-consumer.d.ts +8 -0
  37. package/dist/src/compiled/@workflow/core/index.js +2 -2
  38. package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +1 -0
  39. package/dist/src/compiled/@workflow/core/runtime.js +29 -29
  40. package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
  41. package/dist/src/compiled/@workflow/core/workflow.js +1 -1
  42. package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
  43. package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
  44. package/dist/src/compiled/@workflow/errors/index.js +1 -1
  45. package/dist/src/compiled/@workflow/world/queue.d.ts +8 -0
  46. package/dist/src/compiled/_chunks/workflow/{dist-Chj-QcBs.js → dist-gEXVSMPU.js} +1 -1
  47. package/dist/src/compiled/_chunks/workflow/dist-zpK2YVVA.js +3 -0
  48. package/dist/src/compiled/_chunks/workflow/resume-hook-BFK9mgsb.js +12 -0
  49. package/dist/src/compiled/_chunks/workflow/{sleep-Bg0t23kF.js → sleep-CeJckNg2.js} +1 -1
  50. package/dist/src/compiled/_chunks/workflow/{symbols-u476uwyR.js → symbols-BWCAoPHE.js} +1 -1
  51. package/dist/src/compiler/manifest.d.ts +12 -0
  52. package/dist/src/compiler/manifest.js +1 -1
  53. package/dist/src/compiler/normalize-connection.d.ts +10 -2
  54. package/dist/src/compiler/normalize-connection.js +1 -1
  55. package/dist/src/execution/sandbox/bindings/local.js +1 -1
  56. package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
  57. package/dist/src/internal/application/package.d.ts +1 -0
  58. package/dist/src/internal/application/package.js +1 -1
  59. package/dist/src/internal/authored-definition/connection.d.ts +9 -0
  60. package/dist/src/internal/authored-definition/connection.js +1 -1
  61. package/dist/src/internal/nitro/host/build-application.js +1 -1
  62. package/dist/src/internal/nitro/host/build-vercel-agent-summary.js +1 -1
  63. package/dist/src/internal/nitro/host/channel-routes.js +2 -2
  64. package/dist/src/internal/vercel-agent-summary.d.ts +6 -4
  65. package/dist/src/internal/workflow-bundle/ash-service-route-output.js +11 -1
  66. package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
  67. package/dist/src/public/channels/auth.d.ts +1 -1
  68. package/dist/src/public/channels/index.d.ts +1 -0
  69. package/dist/src/public/channels/index.js +1 -1
  70. package/dist/src/public/connections/index.d.ts +3 -2
  71. package/dist/src/public/connections/index.js +1 -1
  72. package/dist/src/public/definitions/connections/mcp.d.ts +4 -12
  73. package/dist/src/public/definitions/connections/mcp.js +1 -1
  74. package/dist/src/public/definitions/connections/openapi.d.ts +100 -0
  75. package/dist/src/public/definitions/connections/openapi.js +1 -0
  76. package/dist/src/public/definitions/connections/protocol.d.ts +12 -0
  77. package/dist/src/public/definitions/connections/protocol.js +1 -0
  78. package/dist/src/public/next/index.d.ts +6 -6
  79. package/dist/src/public/next/index.js +1 -1
  80. package/dist/src/public/next/{vercel-json.d.ts → vercel-output-config.d.ts} +3 -3
  81. package/dist/src/public/next/vercel-output-config.js +1 -0
  82. package/dist/src/public/nuxt/dev-server.d.ts +24 -0
  83. package/dist/src/public/nuxt/dev-server.js +1 -0
  84. package/dist/src/public/nuxt/index.d.ts +1 -0
  85. package/dist/src/public/nuxt/index.js +1 -0
  86. package/dist/src/public/nuxt/module.d.ts +31 -0
  87. package/dist/src/public/nuxt/module.js +1 -0
  88. package/dist/src/public/nuxt/routing.d.ts +55 -0
  89. package/dist/src/public/nuxt/routing.js +1 -0
  90. package/dist/src/public/nuxt/vercel-json.d.ts +17 -0
  91. package/dist/src/public/{next → nuxt}/vercel-json.js +1 -1
  92. package/dist/src/public/sveltekit/dev-server.d.ts +24 -0
  93. package/dist/src/public/sveltekit/dev-server.js +1 -0
  94. package/dist/src/public/sveltekit/index.d.ts +39 -0
  95. package/dist/src/public/sveltekit/index.js +1 -0
  96. package/dist/src/public/sveltekit/routing.d.ts +32 -0
  97. package/dist/src/public/sveltekit/routing.js +1 -0
  98. package/dist/src/public/sveltekit/vercel-json.d.ts +17 -0
  99. package/dist/src/public/sveltekit/vercel-json.js +1 -0
  100. package/dist/src/react/use-ash-agent.d.ts +5 -27
  101. package/dist/src/react/use-ash-agent.js +1 -2
  102. package/dist/src/runtime/connections/openapi-client.d.ts +43 -0
  103. package/dist/src/runtime/connections/openapi-client.js +1 -0
  104. package/dist/src/runtime/connections/openapi-operations.d.ts +30 -0
  105. package/dist/src/runtime/connections/openapi-operations.js +1 -0
  106. package/dist/src/runtime/connections/openapi-schema.d.ts +39 -0
  107. package/dist/src/runtime/connections/openapi-schema.js +1 -0
  108. package/dist/src/runtime/connections/openapi-security.d.ts +41 -0
  109. package/dist/src/runtime/connections/openapi-security.js +1 -0
  110. package/dist/src/runtime/connections/openapi-spec.d.ts +20 -0
  111. package/dist/src/runtime/connections/openapi-spec.js +1 -0
  112. package/dist/src/runtime/connections/registry.d.ts +5 -7
  113. package/dist/src/runtime/connections/registry.js +1 -1
  114. package/dist/src/runtime/connections/types.d.ts +23 -0
  115. package/dist/src/runtime/resolve-connection.js +1 -1
  116. package/dist/src/runtime/types.d.ts +15 -1
  117. package/dist/src/shared/sandbox-session.d.ts +1 -1
  118. package/dist/src/shared/vercel-output-directory.d.ts +2 -0
  119. package/dist/src/shared/vercel-output-directory.js +1 -0
  120. package/dist/src/svelte/index.d.ts +3 -0
  121. package/dist/src/svelte/index.js +3 -0
  122. package/dist/src/svelte/use-ash-agent.d.ts +80 -0
  123. package/dist/src/svelte/use-ash-agent.js +3 -0
  124. package/dist/src/vue/index.d.ts +3 -0
  125. package/dist/src/vue/index.js +3 -0
  126. package/dist/src/vue/use-ash-agent.d.ts +78 -0
  127. package/dist/src/vue/use-ash-agent.js +3 -0
  128. package/package.json +59 -14
  129. package/dist/src/compiled/_chunks/workflow/dist-C4EHshZE.js +0 -3
  130. package/dist/src/compiled/_chunks/workflow/resume-hook-BlALLgSA.js +0 -12
@@ -0,0 +1,1224 @@
1
+ import { createSubscriber } from "svelte/reactivity";
2
+
3
+ //#region src/protocol/routes.ts
4
+ const ASH_ROUTE_PREFIX = "/ash/v1";
5
+ const ASH_HEALTH_ROUTE_PATH = `${ASH_ROUTE_PREFIX}/health`;
6
+ const ASH_INFO_ROUTE_PATH = `${ASH_ROUTE_PREFIX}/info`;
7
+ const ASH_CREATE_SESSION_ROUTE_PATH = `${ASH_ROUTE_PREFIX}/session`;
8
+ const ASH_CONTINUE_SESSION_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/session/:sessionId`;
9
+ const ASH_MESSAGE_STREAM_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/session/:sessionId/stream`;
10
+ const ASH_DEV_DISPATCH_SCHEDULE_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/dev/schedules/:scheduleId`;
11
+ const ASH_CONNECTION_CALLBACK_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/connections/:name/callback/:token`;
12
+ const ASH_CALLBACK_ROUTE_PATTERN = `${ASH_ROUTE_PREFIX}/callback/:token`;
13
+ function createAshMessageStreamRoutePath(sessionId) {
14
+ return `${ASH_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}/stream`;
15
+ }
16
+ function createAshContinueSessionRoutePath(sessionId) {
17
+ return `${ASH_ROUTE_PREFIX}/session/${encodeURIComponent(sessionId)}`;
18
+ }
19
+
20
+ //#endregion
21
+ //#region src/client/client-error.ts
22
+ var ClientError = class extends Error {
23
+ status;
24
+ body;
25
+ constructor(status, body) {
26
+ super(body || `Server returned ${status}.`);
27
+ this.name = "ClientError";
28
+ this.status = status;
29
+ this.body = body;
30
+ }
31
+ };
32
+
33
+ //#endregion
34
+ //#region src/protocol/message.ts
35
+ const ASH_SESSION_ID_HEADER = "x-ash-session-id";
36
+ const textEncoder = new TextEncoder();
37
+ function isCurrentTurnBoundaryEvent(event) {
38
+ return event.type === "session.completed" || event.type === "session.failed" || event.type === "session.waiting";
39
+ }
40
+
41
+ //#endregion
42
+ //#region src/shared/json.ts
43
+ const INVALID_JSON_VALUE_CANDIDATE = Symbol("invalid-json-value-candidate");
44
+ const JSON_VALUE_ERROR_MESSAGE = "Expected a JSON-serializable value.";
45
+ const JSON_OBJECT_ERROR_MESSAGE = "Expected a JSON-serializable object.";
46
+ function parseJsonValue(value) {
47
+ const normalized = normalizeJsonValueCandidate(value);
48
+ if (normalized === INVALID_JSON_VALUE_CANDIDATE) throw new TypeError(JSON_VALUE_ERROR_MESSAGE);
49
+ return normalized;
50
+ }
51
+ function parseJsonObject(value) {
52
+ const normalized = parseJsonValue(value);
53
+ if (!isJsonObjectValue(normalized)) throw new TypeError(JSON_OBJECT_ERROR_MESSAGE);
54
+ return normalized;
55
+ }
56
+ function normalizeJsonValueCandidate(value, seen = /* @__PURE__ */ new WeakSet()) {
57
+ if (value === null || typeof value === "boolean" || typeof value === "string") return value;
58
+ if (typeof value === "number") return Number.isFinite(value) ? value : INVALID_JSON_VALUE_CANDIDATE;
59
+ if (Array.isArray(value)) {
60
+ const normalizedItems = [];
61
+ for (const item of value) {
62
+ const normalizedItem = normalizeJsonValueCandidate(item, seen);
63
+ if (normalizedItem === INVALID_JSON_VALUE_CANDIDATE) return INVALID_JSON_VALUE_CANDIDATE;
64
+ normalizedItems.push(normalizedItem);
65
+ }
66
+ return normalizedItems;
67
+ }
68
+ if (typeof value !== "object" || value === void 0) return INVALID_JSON_VALUE_CANDIDATE;
69
+ if (!isPlainObject(value)) return INVALID_JSON_VALUE_CANDIDATE;
70
+ if (seen.has(value)) return INVALID_JSON_VALUE_CANDIDATE;
71
+ seen.add(value);
72
+ const normalized = {};
73
+ for (const [key, entry] of Object.entries(value)) {
74
+ if (entry === void 0) continue;
75
+ const normalizedEntry = normalizeJsonValueCandidate(entry, seen);
76
+ if (normalizedEntry === INVALID_JSON_VALUE_CANDIDATE) return INVALID_JSON_VALUE_CANDIDATE;
77
+ normalized[key] = normalizedEntry;
78
+ }
79
+ seen.delete(value);
80
+ return normalized;
81
+ }
82
+ function isJsonObjectValue(value) {
83
+ return value !== null && !Array.isArray(value) && typeof value === "object";
84
+ }
85
+ function isPlainObject(value) {
86
+ const prototype = Object.getPrototypeOf(value);
87
+ return prototype === null || prototype === Object.prototype;
88
+ }
89
+
90
+ //#endregion
91
+ //#region src/shared/json-schema.ts
92
+ const STANDARD_JSON_SCHEMA_TARGET = "draft-07";
93
+ function normalizeJsonSchemaDefinition(value, direction = "input") {
94
+ if (isStandardSchema(value)) return parseJsonObject(value["~standard"].jsonSchema[direction]({ target: STANDARD_JSON_SCHEMA_TARGET }));
95
+ return parseJsonObject(value);
96
+ }
97
+ function isStandardSchema(value) {
98
+ return value !== null && typeof value === "object" && "~standard" in value;
99
+ }
100
+
101
+ //#endregion
102
+ //#region src/client/output-schema.ts
103
+ function normalizeOutputSchemaForRequest(schema) {
104
+ return schema === void 0 ? void 0 : normalizeJsonSchemaDefinition(schema, "output");
105
+ }
106
+ function extractCompletedResult(events) {
107
+ let result;
108
+ for (const event of events) if (isResultCompletedEvent(event)) result = event.data.result;
109
+ return result;
110
+ }
111
+ function isResultCompletedEvent(event) {
112
+ return event.type === "result.completed";
113
+ }
114
+
115
+ //#endregion
116
+ //#region src/client/session-utils.ts
117
+ function createInitialSessionState() {
118
+ return { streamIndex: 0 };
119
+ }
120
+ function advanceSession(input) {
121
+ const boundaryEvent = findBoundaryEvent(input.events);
122
+ const streamIndex = input.session.streamIndex + input.events.length;
123
+ if (boundaryEvent?.type === "session.waiting") return {
124
+ continuationToken: input.continuationToken ?? input.session.continuationToken,
125
+ sessionId: input.sessionId,
126
+ streamIndex
127
+ };
128
+ return createInitialSessionState();
129
+ }
130
+ function extractCompletedMessage(events) {
131
+ let lastMessage;
132
+ for (const event of events) if (isFinalMessageCompleted(event)) lastMessage = event.data.message ?? void 0;
133
+ return lastMessage;
134
+ }
135
+ function deriveResultStatus(events) {
136
+ const boundary = findBoundaryEvent(events);
137
+ if (boundary?.type === "session.waiting") return "waiting";
138
+ if (boundary?.type === "session.failed") return "failed";
139
+ return "completed";
140
+ }
141
+ function findBoundaryEvent(events) {
142
+ for (let i = events.length - 1; i >= 0; i--) {
143
+ const event = events[i];
144
+ if (event !== void 0 && isCurrentTurnBoundaryEvent(event)) return event;
145
+ }
146
+ }
147
+ function isFinalMessageCompleted(event) {
148
+ return event.type === "message.completed" && event.data.finishReason !== "tool-calls";
149
+ }
150
+
151
+ //#endregion
152
+ //#region src/client/message-response.ts
153
+ var MessageResponse = class {
154
+ continuationToken;
155
+ sessionId;
156
+ #consumed = false;
157
+ #createStream;
158
+ constructor(input) {
159
+ this.continuationToken = input.continuationToken;
160
+ this.sessionId = input.sessionId;
161
+ this.#createStream = input.createStream;
162
+ }
163
+ async result() {
164
+ const events = [];
165
+ for await (const event of this) events.push(event);
166
+ return {
167
+ data: extractCompletedResult(events),
168
+ events,
169
+ message: extractCompletedMessage(events),
170
+ sessionId: this.sessionId,
171
+ status: deriveResultStatus(events)
172
+ };
173
+ }
174
+ [Symbol.asyncIterator]() {
175
+ if (this.#consumed) throw new Error("MessageResponse has already been consumed.");
176
+ this.#consumed = true;
177
+ return this.#createStream();
178
+ }
179
+ };
180
+
181
+ //#endregion
182
+ //#region src/client/ndjson.ts
183
+ function isStreamDisconnectError(error) {
184
+ if (error instanceof DOMException) return error.name === "AbortError";
185
+ if (!(error instanceof Error)) return false;
186
+ const errorCode = "code" in error && typeof error.code === "string" ? error.code : void 0;
187
+ return error.name === "AbortError" || error.message === "terminated" || errorCode === "UND_ERR_SOCKET" || /abort|cancel|disconnect|premature close|socket|terminated/i.test(error.message);
188
+ }
189
+ async function* readNdjsonStream(body) {
190
+ const reader = body.getReader();
191
+ const decoder = new TextDecoder();
192
+ let buffer = "";
193
+ try {
194
+ while (true) {
195
+ const result = await reader.read();
196
+ if (result.done) {
197
+ buffer += decoder.decode();
198
+ break;
199
+ }
200
+ if (result.value) buffer += decoder.decode(result.value, { stream: true });
201
+ let newlineIndex = buffer.indexOf("\n");
202
+ while (newlineIndex !== -1) {
203
+ const line = buffer.slice(0, newlineIndex).trim();
204
+ buffer = buffer.slice(newlineIndex + 1);
205
+ if (line.length > 0) yield JSON.parse(line);
206
+ newlineIndex = buffer.indexOf("\n");
207
+ }
208
+ }
209
+ const trailing = buffer.trim();
210
+ if (trailing.length > 0) yield JSON.parse(trailing);
211
+ } finally {
212
+ reader.releaseLock();
213
+ }
214
+ }
215
+
216
+ //#endregion
217
+ //#region src/client/url.ts
218
+ function createClientUrl(host, routePath, searchParams) {
219
+ const normalizedRoute = routePath.startsWith("/") ? routePath : `/${routePath}`;
220
+ const search = formatSearch(searchParams);
221
+ if (isAbsoluteUrl(host)) {
222
+ const url = new URL(host);
223
+ url.pathname = `${trimTrailingSlash(url.pathname)}${normalizedRoute}`;
224
+ url.search = search;
225
+ url.hash = "";
226
+ return url.toString();
227
+ }
228
+ return `${trimTrailingSlash(host)}${normalizedRoute}${search}`;
229
+ }
230
+ function isAbsoluteUrl(value) {
231
+ return /^[a-z][a-z\d+\-.]*:/i.test(value);
232
+ }
233
+ function trimTrailingSlash(value) {
234
+ if (value === "/") return "";
235
+ return value.endsWith("/") ? value.slice(0, -1) : value;
236
+ }
237
+ function formatSearch(searchParams) {
238
+ if (!searchParams || Object.keys(searchParams).length === 0) return "";
239
+ return `?${new URLSearchParams(searchParams).toString()}`;
240
+ }
241
+
242
+ //#endregion
243
+ //#region src/client/open-stream.ts
244
+ async function* openStreamIterable(input) {
245
+ let startIndex = input.startIndex;
246
+ let remainingReconnectAttempts = input.maxReconnectAttempts;
247
+ while (true) {
248
+ const url = createClientUrl(input.host, createAshMessageStreamRoutePath(input.sessionId), startIndex > 0 ? { startIndex: String(startIndex) } : void 0);
249
+ const headers = await input.resolveHeaders();
250
+ const response = await fetch(url, {
251
+ headers,
252
+ signal: input.signal ?? null
253
+ });
254
+ if (!response.ok) {
255
+ const body = await response.text();
256
+ throw new ClientError(response.status, body);
257
+ }
258
+ if (!response.body) throw new ClientError(response.status, "Response body is null.");
259
+ let disconnected = false;
260
+ try {
261
+ for await (const event of readNdjsonStream(response.body)) {
262
+ startIndex += 1;
263
+ yield event;
264
+ }
265
+ } catch (error) {
266
+ if (!isStreamDisconnectError(error)) throw error;
267
+ disconnected = true;
268
+ }
269
+ if (!disconnected || remainingReconnectAttempts <= 0) return;
270
+ remainingReconnectAttempts -= 1;
271
+ }
272
+ }
273
+
274
+ //#endregion
275
+ //#region src/client/session.ts
276
+ var ClientSession = class {
277
+ #context;
278
+ #state;
279
+ constructor(context, state) {
280
+ this.#context = context;
281
+ this.#state = state;
282
+ }
283
+ get state() {
284
+ return this.#state;
285
+ }
286
+ async sendMessage(message, options) {
287
+ return this.send({ message }, options);
288
+ }
289
+ async send(input, options) {
290
+ const state = this.#state;
291
+ const { continuationToken, sessionId } = await this.#postTurn(input, state, options);
292
+ return new MessageResponse({
293
+ continuationToken,
294
+ createStream: () => this.#createEventStream(sessionId, continuationToken, state, options),
295
+ sessionId
296
+ });
297
+ }
298
+ openStream(options) {
299
+ const sessionId = this.#state.sessionId;
300
+ if (!sessionId) throw new Error("Session has no session ID. Send a message first.");
301
+ return openStreamIterable({
302
+ host: this.#context.host,
303
+ maxReconnectAttempts: this.#context.maxReconnectAttempts,
304
+ resolveHeaders: () => this.#context.resolveHeaders(),
305
+ sessionId,
306
+ signal: options?.signal,
307
+ startIndex: options?.startIndex ?? this.#state.streamIndex
308
+ });
309
+ }
310
+ async #postTurn(input, session, options) {
311
+ const routePath = session.sessionId ? createAshContinueSessionRoutePath(session.sessionId) : ASH_CREATE_SESSION_ROUTE_PATH;
312
+ const url = createClientUrl(this.#context.host, routePath);
313
+ const headers = await this.#context.resolveHeaders(options?.headers);
314
+ headers.set("content-type", "application/json");
315
+ const body = createHandleMessageBody({
316
+ input,
317
+ outputSchema: normalizeOutputSchemaForRequest(options?.outputSchema),
318
+ session
319
+ });
320
+ if (body === null) throw new Error("Session.send requires a non-empty message, inputResponses, or both.");
321
+ const response = await fetch(url, {
322
+ body: JSON.stringify(body),
323
+ headers,
324
+ method: "POST",
325
+ signal: options?.signal ?? null
326
+ });
327
+ if (!response.ok) {
328
+ const responseBody = await response.text();
329
+ throw new ClientError(response.status, responseBody);
330
+ }
331
+ const payload = await response.json();
332
+ const sessionId = (typeof payload.sessionId === "string" ? payload.sessionId : void 0) ?? response.headers.get("x-ash-session-id")?.trim() ?? session.sessionId;
333
+ if (!sessionId) throw new Error("Message route did not return a session id.");
334
+ return {
335
+ continuationToken: typeof payload.continuationToken === "string" ? payload.continuationToken : void 0,
336
+ sessionId
337
+ };
338
+ }
339
+ async *#createEventStream(sessionId, continuationToken, initialState, options) {
340
+ const events = [];
341
+ try {
342
+ let currentStreamIndex = initialState.sessionId === sessionId ? initialState.streamIndex : 0;
343
+ let remainingReconnectAttempts = this.#context.maxReconnectAttempts;
344
+ while (true) {
345
+ const body = await this.#openStreamBody(sessionId, currentStreamIndex, options?.signal);
346
+ let foundBoundary = false;
347
+ try {
348
+ for await (const event of readNdjsonStream(body)) {
349
+ events.push(event);
350
+ currentStreamIndex += 1;
351
+ yield event;
352
+ if (isCurrentTurnBoundaryEvent(event)) {
353
+ foundBoundary = true;
354
+ break;
355
+ }
356
+ }
357
+ } catch (error) {
358
+ if (!isStreamDisconnectError(error)) throw error;
359
+ }
360
+ if (foundBoundary) break;
361
+ if (remainingReconnectAttempts <= 0) break;
362
+ remainingReconnectAttempts -= 1;
363
+ }
364
+ } finally {
365
+ this.#state = advanceSession({
366
+ continuationToken,
367
+ events,
368
+ sessionId,
369
+ session: initialState
370
+ });
371
+ }
372
+ }
373
+ async #openStreamBody(sessionId, startIndex, signal) {
374
+ const url = createClientUrl(this.#context.host, createAshMessageStreamRoutePath(sessionId), startIndex > 0 ? { startIndex: String(startIndex) } : void 0);
375
+ const headers = await this.#context.resolveHeaders();
376
+ const response = await fetch(url, {
377
+ headers,
378
+ signal: signal ?? null
379
+ });
380
+ if (!response.ok) {
381
+ const responseBody = await response.text();
382
+ throw new ClientError(response.status, responseBody);
383
+ }
384
+ if (!response.body) throw new ClientError(response.status, "Response body is null.");
385
+ return response.body;
386
+ }
387
+ };
388
+ function createHandleMessageBody(input) {
389
+ const body = {};
390
+ if (input.input.message !== void 0) body.message = input.input.message;
391
+ if (input.input.inputResponses !== void 0 && input.input.inputResponses.length > 0) body.inputResponses = input.input.inputResponses;
392
+ if (input.input.clientContext !== void 0) body.clientContext = input.input.clientContext;
393
+ if (input.outputSchema !== void 0) body.outputSchema = input.outputSchema;
394
+ if (input.session.continuationToken !== void 0) body.continuationToken = input.session.continuationToken;
395
+ if (Object.keys(body).length === 0) return null;
396
+ if (input.session.continuationToken === void 0 && body.message === void 0) return null;
397
+ if (input.session.continuationToken !== void 0 && body.message === void 0 && body.inputResponses === void 0) return null;
398
+ return body;
399
+ }
400
+
401
+ //#endregion
402
+ //#region src/client/client.ts
403
+ var Client = class {
404
+ #auth;
405
+ #headers;
406
+ #host;
407
+ #maxReconnectAttempts;
408
+ constructor(options) {
409
+ this.#host = options.host;
410
+ this.#auth = options.auth;
411
+ this.#headers = options.headers;
412
+ this.#maxReconnectAttempts = options.maxReconnectAttempts ?? 3;
413
+ }
414
+ async health() {
415
+ const url = createClientUrl(this.#host, ASH_HEALTH_ROUTE_PATH);
416
+ const headers = await this.#resolveHeaders();
417
+ const response = await fetch(url, { headers });
418
+ if (!response.ok) {
419
+ const body = await response.text();
420
+ throw new ClientError(response.status, body);
421
+ }
422
+ return await response.json();
423
+ }
424
+ session(state) {
425
+ let resolved;
426
+ if (typeof state === "string") resolved = {
427
+ continuationToken: state,
428
+ streamIndex: 0
429
+ };
430
+ else if (state) resolved = state;
431
+ else resolved = createInitialSessionState();
432
+ return new ClientSession({
433
+ host: this.#host,
434
+ maxReconnectAttempts: this.#maxReconnectAttempts,
435
+ resolveHeaders: (perRequest) => this.#resolveHeaders(perRequest)
436
+ }, resolved);
437
+ }
438
+ async #resolveHeaders(perRequest) {
439
+ const headers = new Headers();
440
+ const baseHeaders = await resolveHeadersValue(this.#headers);
441
+ for (const [key, value] of Object.entries(baseHeaders)) headers.set(key, value);
442
+ if (perRequest) for (const [key, value] of Object.entries(perRequest)) headers.set(key, value);
443
+ const authorization = await this.#resolveAuthorizationHeader();
444
+ if (authorization) headers.set("authorization", authorization);
445
+ return headers;
446
+ }
447
+ async #resolveAuthorizationHeader() {
448
+ const auth = this.#auth;
449
+ if (!auth) return void 0;
450
+ if ("bearer" in auth) {
451
+ const token = (await resolveTokenValue(auth.bearer)).trim();
452
+ if (token.length === 0) return void 0;
453
+ return `Bearer ${token}`;
454
+ }
455
+ if ("basic" in auth) {
456
+ const password = await resolveTokenValue(auth.basic.password);
457
+ return `Basic ${encodeBasicCredentials(auth.basic.username, password)}`;
458
+ }
459
+ }
460
+ };
461
+ async function resolveTokenValue(value) {
462
+ return typeof value === "function" ? value() : value;
463
+ }
464
+ async function resolveHeadersValue(value) {
465
+ if (value === void 0) return {};
466
+ return typeof value === "function" ? await value() : value;
467
+ }
468
+ function encodeBasicCredentials(username, password) {
469
+ const bytes = new TextEncoder().encode(`${username}:${password}`);
470
+ const binaryString = Array.from(bytes, (byte) => String.fromCodePoint(byte)).join("");
471
+ return btoa(binaryString);
472
+ }
473
+
474
+ //#endregion
475
+ //#region src/shared/guards.ts
476
+ function isObject(value) {
477
+ return typeof value === "object" && value !== null && !Array.isArray(value);
478
+ }
479
+
480
+ //#endregion
481
+ //#region src/shared/errors.ts
482
+ function toErrorMessage(error) {
483
+ if (error instanceof Error) return error.message;
484
+ if (typeof error === "string") return error;
485
+ if (error === null || error === void 0) return String(error);
486
+ if (isObject(error)) {
487
+ if (typeof error.message === "string" && error.message.length > 0) return error.message;
488
+ return safeJsonStringify(error);
489
+ }
490
+ return String(error);
491
+ }
492
+ function toError(raw) {
493
+ if (raw instanceof Error) return raw;
494
+ const error = new Error(toErrorMessage(raw));
495
+ if (!isObject(raw)) return error;
496
+ if (typeof raw.name === "string" && raw.name.length > 0) error.name = raw.name;
497
+ if (typeof raw.stack === "string" && raw.stack.length > 0) error.stack = raw.stack;
498
+ if ("cause" in raw && raw.cause !== void 0 && raw.cause !== raw) error.cause = raw.cause;
499
+ return error;
500
+ }
501
+ function safeJsonStringify(value) {
502
+ try {
503
+ return JSON.stringify(value) ?? String(value);
504
+ } catch {
505
+ return String(value);
506
+ }
507
+ }
508
+
509
+ //#endregion
510
+ //#region src/client/ash-agent-store.ts
511
+ var AshAgentStore = class {
512
+ #createSession;
513
+ #optimistic;
514
+ #reducer;
515
+ #subscribers = /* @__PURE__ */ new Set();
516
+ #abortController;
517
+ #callbacks = {};
518
+ #data;
519
+ #error;
520
+ #events;
521
+ #operationId = 0;
522
+ #pendingMessageSubmission;
523
+ #projectionEvents;
524
+ #session;
525
+ #snapshot;
526
+ #status = "ready";
527
+ constructor(init) {
528
+ this.#createSession = init.session ? void 0 : () => new Client({
529
+ auth: init.auth,
530
+ headers: init.headers,
531
+ host: init.host ?? "",
532
+ maxReconnectAttempts: init.maxReconnectAttempts
533
+ }).session(init.initialSession);
534
+ this.#events = [...init.initialEvents ?? []];
535
+ this.#projectionEvents = [...this.#events];
536
+ this.#optimistic = init.optimistic ?? true;
537
+ this.#reducer = init.reducer;
538
+ this.#session = init.session ?? this.#createOwnedSession();
539
+ this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
540
+ this.#snapshot = this.#createSnapshot();
541
+ }
542
+ get snapshot() {
543
+ return this.#snapshot;
544
+ }
545
+ setCallbacks(callbacks) {
546
+ this.#callbacks = callbacks;
547
+ }
548
+ subscribe(callback) {
549
+ this.#subscribers.add(callback);
550
+ return () => {
551
+ this.#subscribers.delete(callback);
552
+ };
553
+ }
554
+ async sendMessage(message, options) {
555
+ await this.send({ message }, options);
556
+ }
557
+ async send(input, options) {
558
+ if (this.#status === "streaming" || this.#status === "submitted") throw new Error("Ash session is already processing a turn.");
559
+ const operationId = this.#startOperation();
560
+ const abortController = new AbortController();
561
+ this.#abortController = abortController;
562
+ this.#error = void 0;
563
+ this.#status = "submitted";
564
+ this.#publish();
565
+ try {
566
+ const preparedInput = await this.#callbacks.prepareSend?.(input) ?? input;
567
+ if (!this.#isCurrentOperation(operationId)) return;
568
+ this.#projectOptimisticMessage(preparedInput);
569
+ this.#projectInputResponses(preparedInput);
570
+ this.#publish();
571
+ const response = await this.#session.send(preparedInput, {
572
+ ...options,
573
+ signal: createAbortSignal(options?.signal, abortController.signal)
574
+ });
575
+ let sawEvent = false;
576
+ for await (const event of response) {
577
+ if (!this.#isCurrentOperation(operationId)) return;
578
+ if (!sawEvent) {
579
+ sawEvent = true;
580
+ this.#status = "streaming";
581
+ }
582
+ this.#events = [...this.#events, event];
583
+ this.#applyServerEvent(event);
584
+ this.#callbacks.onEvent?.(event);
585
+ this.#applyTerminalStreamFailure(event);
586
+ this.#publish();
587
+ }
588
+ if (!this.#isCurrentOperation(operationId)) return;
589
+ this.#status = this.#error === void 0 ? "ready" : "error";
590
+ } catch (error) {
591
+ if (!this.#isCurrentOperation(operationId)) return;
592
+ if (isAbortError(error)) {
593
+ this.#status = "ready";
594
+ this.#failPendingMessageSubmission(toError(error));
595
+ } else {
596
+ this.#error = toError(error);
597
+ this.#status = "error";
598
+ this.#failPendingMessageSubmission(this.#error);
599
+ this.#callbacks.onError?.(this.#error);
600
+ }
601
+ } finally {
602
+ if (this.#isCurrentOperation(operationId)) {
603
+ this.#abortController = void 0;
604
+ this.#callbacks.onSessionChange?.(this.#session.state);
605
+ this.#publish();
606
+ this.#callbacks.onFinish?.(this.#snapshot);
607
+ }
608
+ }
609
+ }
610
+ stop() {
611
+ this.#abortController?.abort();
612
+ }
613
+ reset() {
614
+ this.#invalidateOperation();
615
+ this.stop();
616
+ this.#abortController = void 0;
617
+ this.#session = this.#createSession?.() ?? this.#session;
618
+ this.#events = [];
619
+ this.#pendingMessageSubmission = void 0;
620
+ this.#projectionEvents = [];
621
+ this.#data = this.#reducer.initial();
622
+ this.#error = void 0;
623
+ this.#status = "ready";
624
+ this.#callbacks.onSessionChange?.(this.#session.state);
625
+ this.#publish();
626
+ }
627
+ #createOwnedSession() {
628
+ if (!this.#createSession) throw new Error("Cannot create an owned Ash session from an external session.");
629
+ return this.#createSession();
630
+ }
631
+ #startOperation() {
632
+ this.#operationId += 1;
633
+ return this.#operationId;
634
+ }
635
+ #invalidateOperation() {
636
+ this.#operationId += 1;
637
+ }
638
+ #isCurrentOperation(operationId) {
639
+ return this.#operationId === operationId;
640
+ }
641
+ #projectOptimisticMessage(input) {
642
+ if (!this.#optimistic || input.message === void 0) return;
643
+ const id = createSubmissionId();
644
+ const pending = {
645
+ createdAt: Date.now(),
646
+ id,
647
+ message: summarizeUserContent(input.message)
648
+ };
649
+ this.#pendingMessageSubmission = pending;
650
+ this.#appendProjectionEvent({
651
+ data: {
652
+ createdAt: pending.createdAt,
653
+ message: pending.message,
654
+ submissionId: pending.id
655
+ },
656
+ type: "client.message.submitted"
657
+ });
658
+ }
659
+ #projectInputResponses(input) {
660
+ if (input.inputResponses === void 0 || input.inputResponses.length === 0) return;
661
+ this.#appendProjectionEvent({
662
+ data: {
663
+ createdAt: Date.now(),
664
+ responses: input.inputResponses
665
+ },
666
+ type: "client.input.responded"
667
+ });
668
+ }
669
+ #applyServerEvent(event) {
670
+ if (event.type === "message.received" && this.#pendingMessageSubmission !== void 0) {
671
+ const submissionId = this.#pendingMessageSubmission.id;
672
+ this.#pendingMessageSubmission = void 0;
673
+ this.#replaceProjectionEvent((candidate) => candidate.type === "client.message.submitted" && candidate.data.submissionId === submissionId, event);
674
+ return;
675
+ }
676
+ this.#appendProjectionEvent(event);
677
+ }
678
+ #applyTerminalStreamFailure(event) {
679
+ const error = toTerminalStreamFailureError(event);
680
+ if (error === void 0) return;
681
+ this.#status = "error";
682
+ this.#failPendingMessageSubmission(error);
683
+ if (this.#error === void 0) {
684
+ this.#error = error;
685
+ this.#callbacks.onError?.(error);
686
+ }
687
+ }
688
+ #failPendingMessageSubmission(error) {
689
+ const pending = this.#pendingMessageSubmission;
690
+ if (pending === void 0) return;
691
+ this.#pendingMessageSubmission = void 0;
692
+ this.#replaceProjectionEvent((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
693
+ data: {
694
+ createdAt: pending.createdAt,
695
+ error: { message: error.message },
696
+ message: pending.message,
697
+ submissionId: pending.id
698
+ },
699
+ type: "client.message.failed"
700
+ });
701
+ }
702
+ #appendProjectionEvent(event) {
703
+ this.#projectionEvents = [...this.#projectionEvents, event];
704
+ this.#data = this.#reducer.reduce(this.#data, event);
705
+ }
706
+ #replaceProjectionEvent(predicate, replacement) {
707
+ let replaced = false;
708
+ this.#projectionEvents = this.#projectionEvents.map((event) => {
709
+ if (!replaced && predicate(event)) {
710
+ replaced = true;
711
+ return replacement;
712
+ }
713
+ return event;
714
+ });
715
+ if (!replaced) this.#projectionEvents = [...this.#projectionEvents, replacement];
716
+ this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
717
+ }
718
+ #reduceProjectionEvents(events) {
719
+ let data = this.#reducer.initial();
720
+ for (const event of events) data = this.#reducer.reduce(data, event);
721
+ return data;
722
+ }
723
+ #createSnapshot() {
724
+ return {
725
+ data: this.#data,
726
+ error: this.#error,
727
+ events: this.#events,
728
+ session: this.#session.state,
729
+ status: this.#status
730
+ };
731
+ }
732
+ #publish() {
733
+ this.#snapshot = this.#createSnapshot();
734
+ for (const subscriber of this.#subscribers) subscriber();
735
+ }
736
+ };
737
+ let submissionSequence = 0;
738
+ function createSubmissionId() {
739
+ const randomUUID = globalThis.crypto?.randomUUID;
740
+ if (randomUUID !== void 0) return randomUUID.call(globalThis.crypto);
741
+ submissionSequence += 1;
742
+ return `submission_${submissionSequence.toString()}`;
743
+ }
744
+ function createAbortSignal(first, second) {
745
+ return first ? AbortSignal.any([first, second]) : second;
746
+ }
747
+ function summarizeUserContent(message) {
748
+ if (typeof message === "string") return message;
749
+ const parts = [];
750
+ for (const part of message) {
751
+ if (part.type === "text") {
752
+ parts.push(part.text);
753
+ continue;
754
+ }
755
+ if (part.type === "file") parts.push(part.filename ? `[file: ${part.filename}]` : "[file]");
756
+ }
757
+ return parts.join("\n");
758
+ }
759
+ function isAbortError(error) {
760
+ return error instanceof Error && error.name === "AbortError";
761
+ }
762
+ function toTerminalStreamFailureError(event) {
763
+ if (event.type !== "session.failed") return;
764
+ const error = new Error(event.data.message);
765
+ error.name = event.data.code;
766
+ return error;
767
+ }
768
+
769
+ //#endregion
770
+ //#region src/client/message-reducer.ts
771
+ function defaultMessageReducer() {
772
+ return {
773
+ initial() {
774
+ return { messages: [] };
775
+ },
776
+ reduce(data, event) {
777
+ return reduceMessageData(data, event);
778
+ }
779
+ };
780
+ }
781
+ function reduceMessageData(data, event) {
782
+ switch (event.type) {
783
+ case "client.message.submitted": return upsertMessage(data, {
784
+ id: optimisticUserMessageId(event.data.submissionId),
785
+ metadata: {
786
+ optimistic: true,
787
+ status: "submitted"
788
+ },
789
+ parts: [{
790
+ type: "text",
791
+ text: event.data.message
792
+ }],
793
+ role: "user"
794
+ });
795
+ case "client.message.failed": return upsertMessage(data, {
796
+ id: optimisticUserMessageId(event.data.submissionId),
797
+ metadata: {
798
+ optimistic: true,
799
+ status: "failed"
800
+ },
801
+ parts: [{
802
+ type: "text",
803
+ text: event.data.message
804
+ }],
805
+ role: "user"
806
+ });
807
+ case "client.input.responded": {
808
+ let next = data;
809
+ for (const response of event.data.responses) next = respondToInputRequest(next, response);
810
+ return next;
811
+ }
812
+ case "message.received": return upsertMessage(data, {
813
+ id: `${event.data.turnId}:user`,
814
+ metadata: {
815
+ status: "complete",
816
+ turnId: event.data.turnId
817
+ },
818
+ parts: [{
819
+ type: "text",
820
+ text: event.data.message,
821
+ state: "done"
822
+ }],
823
+ role: "user"
824
+ });
825
+ case "step.started": return updateAssistantMessage(data, event.data.turnId, (message) => ensureStepStartPart(message, event.data.stepIndex));
826
+ case "reasoning.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
827
+ state: "streaming",
828
+ stepIndex: event.data.stepIndex,
829
+ text: event.data.reasoningSoFar,
830
+ type: "reasoning"
831
+ }));
832
+ case "reasoning.completed": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
833
+ state: "done",
834
+ stepIndex: event.data.stepIndex,
835
+ text: event.data.reasoning,
836
+ type: "reasoning"
837
+ }));
838
+ case "actions.requested": {
839
+ let next = data;
840
+ for (const action of event.data.actions) {
841
+ const descriptor = normalizeActionRequest(action);
842
+ next = updateAssistantMessage(next, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
843
+ input: "input" in action ? action.input : void 0,
844
+ state: "input-available",
845
+ stepIndex: event.data.stepIndex,
846
+ toolCallId: action.callId,
847
+ toolMetadata: createToolMetadata(descriptor),
848
+ toolName: descriptor.toolName,
849
+ type: "dynamic-tool"
850
+ }));
851
+ }
852
+ return next;
853
+ }
854
+ case "input.requested": {
855
+ let next = data;
856
+ for (const request of event.data.requests) {
857
+ const descriptor = normalizeActionRequest(request.action);
858
+ next = updateAssistantMessage(next, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
859
+ approval: { id: request.requestId },
860
+ input: request.action.input,
861
+ state: "approval-requested",
862
+ stepIndex: event.data.stepIndex,
863
+ toolCallId: request.action.callId,
864
+ toolMetadata: createToolMetadata(descriptor, { inputRequest: toMessageInputRequest(request) }),
865
+ toolName: descriptor.toolName,
866
+ type: "dynamic-tool"
867
+ }));
868
+ }
869
+ return next;
870
+ }
871
+ case "action.result": {
872
+ const descriptor = normalizeActionResult(event.data.result);
873
+ const existing = findToolPart(data, event.data.result.callId);
874
+ const denied = event.data.error?.code === "TOOL_EXECUTION_DENIED";
875
+ const failed = event.data.status === "failed" && !denied;
876
+ const approvalId = existing?.approval?.id ?? event.data.result.callId;
877
+ const toolMetadata = mergeToolMetadata(existing?.toolMetadata, createToolMetadata(descriptor));
878
+ const resultPartBase = {
879
+ input: existing?.input,
880
+ stepIndex: event.data.stepIndex,
881
+ toolCallId: event.data.result.callId,
882
+ toolMetadata,
883
+ toolName: existing?.toolName ?? descriptor.toolName,
884
+ type: "dynamic-tool"
885
+ };
886
+ let nextPart;
887
+ if (denied) nextPart = {
888
+ ...resultPartBase,
889
+ approval: {
890
+ approved: false,
891
+ id: approvalId,
892
+ reason: event.data.error?.message
893
+ },
894
+ state: "output-denied"
895
+ };
896
+ else if (failed) nextPart = {
897
+ ...resultPartBase,
898
+ approval: approvedApproval(existing),
899
+ errorText: event.data.error?.message ?? stringifyUnknown(event.data.result.output),
900
+ state: "output-error"
901
+ };
902
+ else nextPart = {
903
+ ...resultPartBase,
904
+ approval: approvedApproval(existing),
905
+ output: event.data.result.output,
906
+ state: "output-available"
907
+ };
908
+ if (existing !== void 0) return updateToolPart(data, event.data.result.callId, nextPart);
909
+ return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), nextPart));
910
+ }
911
+ case "message.appended": return updateAssistantMessage(data, event.data.turnId, (message) => upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
912
+ state: "streaming",
913
+ stepIndex: event.data.stepIndex,
914
+ text: event.data.messageSoFar,
915
+ type: "text"
916
+ }));
917
+ case "message.completed": return updateAssistantMessage(data, event.data.turnId, (message) => {
918
+ if (event.data.message === null) return completeExistingTextPart(message);
919
+ return upsertPart(ensureStepStartPart(message, event.data.stepIndex), {
920
+ state: "done",
921
+ stepIndex: event.data.stepIndex,
922
+ text: event.data.message,
923
+ type: "text"
924
+ });
925
+ });
926
+ case "result.completed": return updateAssistantMetadata(data, event.data.turnId, { result: event.data.result });
927
+ case "turn.completed": return updateAssistantMetadata(data, event.data.turnId, { status: "complete" });
928
+ case "turn.failed":
929
+ case "session.failed": return data;
930
+ default: return data;
931
+ }
932
+ }
933
+ function respondToInputRequest(data, response) {
934
+ const existing = findToolPartByApprovalId(data, response.requestId);
935
+ if (!existing) return data;
936
+ const approval = { id: response.requestId };
937
+ if (response.text !== void 0) approval.reason = response.text;
938
+ return updateToolPart(data, existing.toolCallId, {
939
+ approval,
940
+ input: existing.input,
941
+ state: "approval-responded",
942
+ stepIndex: existing.stepIndex,
943
+ toolCallId: existing.toolCallId,
944
+ toolMetadata: mergeToolMetadata(existing.toolMetadata, { ash: {
945
+ inputResponse: response,
946
+ kind: existing.toolMetadata?.ash?.kind ?? "unknown",
947
+ name: existing.toolMetadata?.ash?.name ?? existing.toolName
948
+ } }),
949
+ toolName: existing.toolName,
950
+ type: "dynamic-tool"
951
+ });
952
+ }
953
+ function updateAssistantMessage(data, turnId, update) {
954
+ return upsertMessage(data, update(data.messages.find((message) => message.role === "assistant" && message.metadata?.turnId === turnId) ?? createAssistantMessage(turnId)));
955
+ }
956
+ function updateAssistantMetadata(data, turnId, metadata) {
957
+ return updateAssistantMessage(data, turnId, (message) => ({
958
+ ...message,
959
+ metadata: {
960
+ ...message.metadata,
961
+ ...metadata
962
+ }
963
+ }));
964
+ }
965
+ function createAssistantMessage(turnId) {
966
+ return {
967
+ id: `${turnId}:assistant`,
968
+ metadata: {
969
+ status: "streaming",
970
+ turnId
971
+ },
972
+ parts: [],
973
+ role: "assistant"
974
+ };
975
+ }
976
+ function ensureStepStartPart(message, stepIndex) {
977
+ const stepStartCount = message.parts.filter((part) => part.type === "step-start").length;
978
+ if (stepStartCount > stepIndex) return message;
979
+ const missingCount = stepIndex - stepStartCount + 1;
980
+ return {
981
+ ...message,
982
+ parts: [...message.parts, ...Array.from({ length: missingCount }, () => ({ type: "step-start" }))]
983
+ };
984
+ }
985
+ function upsertPart(message, next) {
986
+ const index = message.parts.findIndex((part) => partKey(part) === partKey(next));
987
+ const parts = index === -1 ? [...message.parts, next] : [
988
+ ...message.parts.slice(0, index),
989
+ next,
990
+ ...message.parts.slice(index + 1)
991
+ ];
992
+ return {
993
+ ...message,
994
+ metadata: {
995
+ ...message.metadata,
996
+ status: next.type === "text" && next.state === "done" ? "complete" : "streaming"
997
+ },
998
+ parts
999
+ };
1000
+ }
1001
+ function completeExistingTextPart(message) {
1002
+ const index = findLastIndex(message.parts, (part) => part.type === "text");
1003
+ if (index === -1) return message;
1004
+ const existing = message.parts[index];
1005
+ if (existing?.type !== "text") return message;
1006
+ return {
1007
+ ...message,
1008
+ metadata: {
1009
+ ...message.metadata,
1010
+ status: "complete"
1011
+ },
1012
+ parts: [
1013
+ ...message.parts.slice(0, index),
1014
+ {
1015
+ ...existing,
1016
+ state: "done"
1017
+ },
1018
+ ...message.parts.slice(index + 1)
1019
+ ]
1020
+ };
1021
+ }
1022
+ function updateToolPart(data, toolCallId, next) {
1023
+ const message = data.messages.find((candidate) => candidate.role === "assistant" && candidate.parts.some((part) => part.type === "dynamic-tool" && part.toolCallId === toolCallId));
1024
+ if (!message) return data;
1025
+ return upsertMessage(data, upsertPart(message, next));
1026
+ }
1027
+ function findToolPart(data, toolCallId) {
1028
+ for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.toolCallId === toolCallId) return part;
1029
+ }
1030
+ function findToolPartByApprovalId(data, approvalId) {
1031
+ for (const message of data.messages) for (const part of message.parts) if (part.type === "dynamic-tool" && part.approval?.id === approvalId) return part;
1032
+ }
1033
+ function partKey(part) {
1034
+ switch (part.type) {
1035
+ case "text": return `text:${part.stepIndex ?? 0}`;
1036
+ case "reasoning": return `reasoning:${part.stepIndex ?? 0}`;
1037
+ case "step-start": return "step-start";
1038
+ case "dynamic-tool": return `dynamic-tool:${part.toolCallId}`;
1039
+ }
1040
+ }
1041
+ function upsertMessage(data, next) {
1042
+ const index = data.messages.findIndex((message) => message.id === next.id);
1043
+ if (index === -1) return { messages: [...data.messages, next] };
1044
+ return { messages: [
1045
+ ...data.messages.slice(0, index),
1046
+ next,
1047
+ ...data.messages.slice(index + 1)
1048
+ ] };
1049
+ }
1050
+ function toMessageInputRequest(request) {
1051
+ return {
1052
+ allowFreeform: request.allowFreeform,
1053
+ display: request.display,
1054
+ options: request.options,
1055
+ prompt: request.prompt,
1056
+ requestId: request.requestId
1057
+ };
1058
+ }
1059
+ function createToolMetadata(descriptor, extra) {
1060
+ return { ash: {
1061
+ inputRequest: extra?.inputRequest,
1062
+ kind: descriptor.kind,
1063
+ name: descriptor.name
1064
+ } };
1065
+ }
1066
+ function mergeToolMetadata(current, next) {
1067
+ const kind = next.ash?.kind ?? current?.ash?.kind ?? "unknown";
1068
+ const name = next.ash?.name ?? current?.ash?.name ?? "unknown";
1069
+ return { ash: {
1070
+ ...current?.ash,
1071
+ ...next.ash,
1072
+ inputRequest: next.ash?.inputRequest ?? current?.ash?.inputRequest,
1073
+ inputResponse: next.ash?.inputResponse ?? current?.ash?.inputResponse,
1074
+ kind,
1075
+ name
1076
+ } };
1077
+ }
1078
+ function approvedApproval(part) {
1079
+ if (!part?.approval?.id) return;
1080
+ return {
1081
+ approved: true,
1082
+ id: part.approval.id,
1083
+ isAutomatic: part.approval.isAutomatic,
1084
+ reason: part.approval.reason
1085
+ };
1086
+ }
1087
+ function normalizeActionRequest(action) {
1088
+ switch (action.kind) {
1089
+ case "load-skill": return {
1090
+ kind: "load-skill",
1091
+ name: "load_skill",
1092
+ toolName: "ash:load-skill"
1093
+ };
1094
+ case "tool-call": return {
1095
+ kind: "tool-call",
1096
+ name: action.toolName,
1097
+ toolName: action.toolName
1098
+ };
1099
+ case "subagent-call": return {
1100
+ kind: "subagent-call",
1101
+ name: action.subagentName,
1102
+ toolName: `ash:subagent:${action.subagentName}`
1103
+ };
1104
+ case "remote-agent-call": return {
1105
+ kind: "subagent-call",
1106
+ name: action.remoteAgentName,
1107
+ toolName: `ash:subagent:${action.remoteAgentName}`
1108
+ };
1109
+ }
1110
+ }
1111
+ function normalizeActionResult(result) {
1112
+ switch (result.kind) {
1113
+ case "load-skill-result": return {
1114
+ kind: "load-skill",
1115
+ name: result.name ?? "load_skill",
1116
+ toolName: "ash:load-skill"
1117
+ };
1118
+ case "tool-result": return {
1119
+ kind: "tool-call",
1120
+ name: result.toolName,
1121
+ toolName: result.toolName
1122
+ };
1123
+ case "subagent-result": return {
1124
+ kind: "subagent-call",
1125
+ name: result.subagentName,
1126
+ toolName: `ash:subagent:${result.subagentName}`
1127
+ };
1128
+ }
1129
+ }
1130
+ function optimisticUserMessageId(submissionId) {
1131
+ return `optimistic:${submissionId}:user`;
1132
+ }
1133
+ function stringifyUnknown(value) {
1134
+ if (typeof value === "string") return value;
1135
+ try {
1136
+ return JSON.stringify(value);
1137
+ } catch {
1138
+ return "Action failed.";
1139
+ }
1140
+ }
1141
+ function findLastIndex(items, predicate) {
1142
+ for (let index = items.length - 1; index >= 0; index -= 1) if (predicate(items[index])) return index;
1143
+ return -1;
1144
+ }
1145
+
1146
+ //#endregion
1147
+ //#region src/svelte/use-ash-agent.ts
1148
+ var SvelteAshAgent = class {
1149
+ #snapshot;
1150
+ #store;
1151
+ #subscribe;
1152
+ constructor(store) {
1153
+ this.#store = store;
1154
+ this.#snapshot = store.snapshot;
1155
+ this.#subscribe = createSubscriber((update) => {
1156
+ if (!("window" in globalThis)) return;
1157
+ const unsubscribe = store.subscribe(() => {
1158
+ this.#snapshot = store.snapshot;
1159
+ update();
1160
+ });
1161
+ return () => {
1162
+ unsubscribe();
1163
+ store.stop();
1164
+ };
1165
+ });
1166
+ }
1167
+ get data() {
1168
+ this.#subscribe();
1169
+ return this.#snapshot.data;
1170
+ }
1171
+ get error() {
1172
+ this.#subscribe();
1173
+ return this.#snapshot.error;
1174
+ }
1175
+ get events() {
1176
+ this.#subscribe();
1177
+ return this.#snapshot.events;
1178
+ }
1179
+ get session() {
1180
+ this.#subscribe();
1181
+ return this.#snapshot.session;
1182
+ }
1183
+ get status() {
1184
+ this.#subscribe();
1185
+ return this.#snapshot.status;
1186
+ }
1187
+ reset = () => {
1188
+ this.#store.reset();
1189
+ };
1190
+ send = (input, options) => {
1191
+ return this.#store.send(input, options);
1192
+ };
1193
+ sendMessage = (message, options) => {
1194
+ return this.#store.sendMessage(message, options);
1195
+ };
1196
+ stop = () => {
1197
+ this.#store.stop();
1198
+ };
1199
+ };
1200
+ function useAshAgent(options = {}) {
1201
+ const reducer = options.reducer ?? defaultMessageReducer();
1202
+ const store = new AshAgentStore({
1203
+ auth: options.auth,
1204
+ headers: options.headers,
1205
+ host: options.host,
1206
+ initialEvents: options.initialEvents,
1207
+ initialSession: options.initialSession,
1208
+ maxReconnectAttempts: options.maxReconnectAttempts,
1209
+ optimistic: options.optimistic,
1210
+ reducer,
1211
+ session: options.session
1212
+ });
1213
+ store.setCallbacks({
1214
+ onError: options.onError,
1215
+ onEvent: options.onEvent,
1216
+ onFinish: options.onFinish,
1217
+ onSessionChange: options.onSessionChange,
1218
+ prepareSend: options.prepareSend
1219
+ });
1220
+ return new SvelteAshAgent(store);
1221
+ }
1222
+
1223
+ //#endregion
1224
+ export { defaultMessageReducer as n, useAshAgent as t };