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.
- package/CHANGELOG.md +72 -0
- package/dist/docs/public/advanced/hooks.mdx +8 -2
- package/dist/docs/public/advanced/runs-and-streaming.md +6 -3
- package/dist/docs/public/advanced/typescript-api.md +32 -0
- package/dist/docs/public/channels/custom.mdx +23 -0
- package/dist/docs/public/frontend/README.md +8 -4
- package/dist/docs/public/frontend/meta.json +9 -1
- package/dist/docs/public/frontend/nextjs.md +4 -4
- package/dist/docs/public/frontend/nuxt.md +168 -0
- package/dist/docs/public/frontend/sveltekit.md +177 -0
- package/dist/docs/public/frontend/use-ash-agent-svelte.md +185 -0
- package/dist/docs/public/frontend/use-ash-agent-vue.md +236 -0
- package/dist/docs/public/frontend/use-ash-agent.md +14 -14
- package/dist/docs/public/getting-started.mdx +2 -0
- package/dist/skills/ash-add-agent/SKILL.md +29 -17
- package/dist/skills/ash-add-next/SKILL.md +58 -8
- package/dist/src/channel/websocket-upgrade-server.d.ts +26 -0
- package/dist/src/channel/websocket-upgrade-server.js +1 -0
- package/dist/src/chunks/use-ash-agent-BQJLh7KU.js +1224 -0
- package/dist/src/chunks/use-ash-agent-CRWVA4i-.js +1192 -0
- package/dist/src/client/ash-agent-store.d.ts +61 -0
- package/dist/src/client/ash-agent-store.js +2 -0
- package/dist/src/client/index.d.ts +2 -0
- package/dist/src/client/index.js +1 -1
- package/dist/src/compiled/.vendor-stamp.json +9 -9
- package/dist/src/compiled/@ai-sdk/anthropic/_provider-utils.d.ts +1 -1
- package/dist/src/compiled/@ai-sdk/anthropic/index.d.ts +3 -3
- package/dist/src/compiled/@ai-sdk/anthropic/index.js +2 -2
- package/dist/src/compiled/@ai-sdk/google/index.d.ts +52 -2
- package/dist/src/compiled/@ai-sdk/google/index.js +6 -6
- package/dist/src/compiled/@ai-sdk/mcp/index.js +1 -1
- package/dist/src/compiled/@ai-sdk/openai/index.d.ts +32 -2
- package/dist/src/compiled/@ai-sdk/openai/index.js +2 -2
- package/dist/src/compiled/@ai-sdk/provider/index.d.ts +507 -1
- package/dist/src/compiled/@chat-adapter/slack/index.js +25 -25
- package/dist/src/compiled/@workflow/core/events-consumer.d.ts +8 -0
- package/dist/src/compiled/@workflow/core/index.js +2 -2
- package/dist/src/compiled/@workflow/core/runtime/constants.d.ts +1 -0
- package/dist/src/compiled/@workflow/core/runtime.js +29 -29
- package/dist/src/compiled/@workflow/core/version.d.ts +1 -1
- package/dist/src/compiled/@workflow/core/workflow.js +1 -1
- package/dist/src/compiled/@workflow/errors/error-codes.d.ts +2 -0
- package/dist/src/compiled/@workflow/errors/index.d.ts +14 -0
- package/dist/src/compiled/@workflow/errors/index.js +1 -1
- package/dist/src/compiled/@workflow/world/queue.d.ts +8 -0
- package/dist/src/compiled/_chunks/workflow/{dist-Chj-QcBs.js → dist-gEXVSMPU.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/dist-zpK2YVVA.js +3 -0
- package/dist/src/compiled/_chunks/workflow/resume-hook-BFK9mgsb.js +12 -0
- package/dist/src/compiled/_chunks/workflow/{sleep-Bg0t23kF.js → sleep-CeJckNg2.js} +1 -1
- package/dist/src/compiled/_chunks/workflow/{symbols-u476uwyR.js → symbols-BWCAoPHE.js} +1 -1
- package/dist/src/compiler/manifest.d.ts +12 -0
- package/dist/src/compiler/manifest.js +1 -1
- package/dist/src/compiler/normalize-connection.d.ts +10 -2
- package/dist/src/compiler/normalize-connection.js +1 -1
- package/dist/src/execution/sandbox/bindings/local.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -1
- package/dist/src/internal/application/package.d.ts +1 -0
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/connection.d.ts +9 -0
- package/dist/src/internal/authored-definition/connection.js +1 -1
- package/dist/src/internal/nitro/host/build-application.js +1 -1
- package/dist/src/internal/nitro/host/build-vercel-agent-summary.js +1 -1
- package/dist/src/internal/nitro/host/channel-routes.js +2 -2
- package/dist/src/internal/vercel-agent-summary.d.ts +6 -4
- package/dist/src/internal/workflow-bundle/ash-service-route-output.js +11 -1
- package/dist/src/packages/ash-scaffold/src/channels.js +1 -1
- package/dist/src/public/channels/auth.d.ts +1 -1
- package/dist/src/public/channels/index.d.ts +1 -0
- package/dist/src/public/channels/index.js +1 -1
- package/dist/src/public/connections/index.d.ts +3 -2
- package/dist/src/public/connections/index.js +1 -1
- package/dist/src/public/definitions/connections/mcp.d.ts +4 -12
- package/dist/src/public/definitions/connections/mcp.js +1 -1
- package/dist/src/public/definitions/connections/openapi.d.ts +100 -0
- package/dist/src/public/definitions/connections/openapi.js +1 -0
- package/dist/src/public/definitions/connections/protocol.d.ts +12 -0
- package/dist/src/public/definitions/connections/protocol.js +1 -0
- package/dist/src/public/next/index.d.ts +6 -6
- package/dist/src/public/next/index.js +1 -1
- package/dist/src/public/next/{vercel-json.d.ts → vercel-output-config.d.ts} +3 -3
- package/dist/src/public/next/vercel-output-config.js +1 -0
- package/dist/src/public/nuxt/dev-server.d.ts +24 -0
- package/dist/src/public/nuxt/dev-server.js +1 -0
- package/dist/src/public/nuxt/index.d.ts +1 -0
- package/dist/src/public/nuxt/index.js +1 -0
- package/dist/src/public/nuxt/module.d.ts +31 -0
- package/dist/src/public/nuxt/module.js +1 -0
- package/dist/src/public/nuxt/routing.d.ts +55 -0
- package/dist/src/public/nuxt/routing.js +1 -0
- package/dist/src/public/nuxt/vercel-json.d.ts +17 -0
- package/dist/src/public/{next → nuxt}/vercel-json.js +1 -1
- package/dist/src/public/sveltekit/dev-server.d.ts +24 -0
- package/dist/src/public/sveltekit/dev-server.js +1 -0
- package/dist/src/public/sveltekit/index.d.ts +39 -0
- package/dist/src/public/sveltekit/index.js +1 -0
- package/dist/src/public/sveltekit/routing.d.ts +32 -0
- package/dist/src/public/sveltekit/routing.js +1 -0
- package/dist/src/public/sveltekit/vercel-json.d.ts +17 -0
- package/dist/src/public/sveltekit/vercel-json.js +1 -0
- package/dist/src/react/use-ash-agent.d.ts +5 -27
- package/dist/src/react/use-ash-agent.js +1 -2
- package/dist/src/runtime/connections/openapi-client.d.ts +43 -0
- package/dist/src/runtime/connections/openapi-client.js +1 -0
- package/dist/src/runtime/connections/openapi-operations.d.ts +30 -0
- package/dist/src/runtime/connections/openapi-operations.js +1 -0
- package/dist/src/runtime/connections/openapi-schema.d.ts +39 -0
- package/dist/src/runtime/connections/openapi-schema.js +1 -0
- package/dist/src/runtime/connections/openapi-security.d.ts +41 -0
- package/dist/src/runtime/connections/openapi-security.js +1 -0
- package/dist/src/runtime/connections/openapi-spec.d.ts +20 -0
- package/dist/src/runtime/connections/openapi-spec.js +1 -0
- package/dist/src/runtime/connections/registry.d.ts +5 -7
- package/dist/src/runtime/connections/registry.js +1 -1
- package/dist/src/runtime/connections/types.d.ts +23 -0
- package/dist/src/runtime/resolve-connection.js +1 -1
- package/dist/src/runtime/types.d.ts +15 -1
- package/dist/src/shared/sandbox-session.d.ts +1 -1
- package/dist/src/shared/vercel-output-directory.d.ts +2 -0
- package/dist/src/shared/vercel-output-directory.js +1 -0
- package/dist/src/svelte/index.d.ts +3 -0
- package/dist/src/svelte/index.js +3 -0
- package/dist/src/svelte/use-ash-agent.d.ts +80 -0
- package/dist/src/svelte/use-ash-agent.js +3 -0
- package/dist/src/vue/index.d.ts +3 -0
- package/dist/src/vue/index.js +3 -0
- package/dist/src/vue/use-ash-agent.d.ts +78 -0
- package/dist/src/vue/use-ash-agent.js +3 -0
- package/package.json +59 -14
- package/dist/src/compiled/_chunks/workflow/dist-C4EHshZE.js +0 -3
- 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 };
|