experimental-ash 0.18.0 → 0.18.2
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 +43 -2
- package/dist/docs/public/channels/README.md +75 -9
- package/dist/docs/public/schedules.md +13 -4
- package/dist/src/channel/adapter-context.d.ts +4 -0
- package/dist/src/channel/adapter-context.js +6 -0
- package/dist/src/channel/adapter.d.ts +10 -10
- package/dist/src/channel/cross-channel-receive.d.ts +1 -1
- package/dist/src/channel/cross-channel-receive.js +40 -0
- package/dist/src/channel/routes.d.ts +7 -0
- package/dist/src/channel/send.js +1 -1
- package/dist/src/channel/session.d.ts +47 -1
- package/dist/src/channel/session.js +46 -0
- package/dist/src/channel/types.d.ts +6 -5
- package/dist/src/chunks/client-CKsU8Li3.js +4 -0
- package/dist/src/chunks/{dev-authored-source-watcher-CG6kri3T.js → dev-authored-source-watcher-j7YWh2Gx.js} +1 -1
- package/dist/src/chunks/{host-CIU0NATc.js → host-DkTSR6YJ.js} +2 -2
- package/dist/src/chunks/{paths-CvbqpwTh.js → paths-Dwv0Eash.js} +22 -22
- package/dist/src/chunks/{prewarm-C_Vd0JR7.js → prewarm-CQYfka30.js} +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/dev/repl.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/client/client.js +2 -1
- package/dist/src/client/index.d.ts +3 -0
- package/dist/src/client/index.js +1 -0
- package/dist/src/client/message-reducer-types.d.ts +130 -0
- package/dist/src/client/message-reducer-types.js +1 -0
- package/dist/src/client/message-reducer.d.ts +14 -0
- package/dist/src/client/message-reducer.js +462 -0
- package/dist/src/client/open-stream.js +2 -4
- package/dist/src/client/reducer.d.ts +63 -0
- package/dist/src/client/reducer.js +1 -0
- package/dist/src/client/session.js +3 -5
- package/dist/src/client/url.d.ts +8 -0
- package/dist/src/client/url.js +34 -0
- package/dist/src/compiler/module-map.js +12 -0
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/execution/sandbox/bindings/vercel.d.ts +2 -2
- package/dist/src/execution/sandbox/bindings/vercel.js +1 -34
- package/dist/src/execution/workflow-entry.js +35 -31
- package/dist/src/execution/workflow-steps.d.ts +16 -0
- package/dist/src/execution/workflow-steps.js +32 -4
- package/dist/src/harness/attachment-staging.js +2 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/public/channels/slack/api.d.ts +13 -8
- package/dist/src/public/channels/slack/api.js +31 -17
- package/dist/src/public/channels/slack/index.d.ts +2 -2
- package/dist/src/public/channels/slack/index.js +1 -0
- package/dist/src/public/channels/slack/interactions.js +3 -3
- package/dist/src/public/channels/slack/slackChannel.d.ts +5 -3
- package/dist/src/public/channels/slack/slackChannel.js +26 -15
- package/dist/src/public/channels/twilio/api.d.ts +9 -0
- package/dist/src/public/channels/twilio/api.js +11 -0
- package/dist/src/public/channels/twilio/index.d.ts +1 -1
- package/dist/src/public/channels/twilio/index.js +1 -1
- package/dist/src/public/channels/twilio/twilioChannel.d.ts +2 -0
- package/dist/src/public/channels/twilio/twilioChannel.js +8 -11
- package/dist/src/public/definitions/defineChannel.d.ts +9 -1
- package/dist/src/public/definitions/defineChannel.js +7 -11
- package/dist/src/public/definitions/sandbox.d.ts +2 -3
- package/dist/src/public/sandbox/backends/vercel.d.ts +4 -4
- package/dist/src/public/sandbox/backends/vercel.js +2 -2
- package/dist/src/public/sandbox/index.d.ts +1 -1
- package/dist/src/public/sandbox/vercel-sandbox.d.ts +3 -28
- package/dist/src/react/index.d.ts +3 -0
- package/dist/src/react/index.js +3 -0
- package/dist/src/react/use-ash-agent.d.ts +79 -0
- package/dist/src/react/use-ash-agent.js +330 -0
- package/dist/src/runtime/types.d.ts +1 -2
- package/dist/src/shared/sandbox-backend.d.ts +4 -4
- package/dist/src/shared/sandbox-definition.d.ts +6 -6
- package/package.json +15 -2
- package/dist/src/chunks/client-BeZ_W7vl.js +0 -4
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
|
|
2
|
+
import { Client } from "#client/client.js";
|
|
3
|
+
import { defaultMessageReducer } from "#client/message-reducer.js";
|
|
4
|
+
/**
|
|
5
|
+
* React hook for driving an Ash session and projecting Ash events into UI data.
|
|
6
|
+
*/
|
|
7
|
+
export function useAshAgent(options = {}) {
|
|
8
|
+
const storeRef = useRef(undefined);
|
|
9
|
+
if (!storeRef.current) {
|
|
10
|
+
const reducer = options.reducer ?? defaultMessageReducer();
|
|
11
|
+
storeRef.current = new UseAshAgentStore({
|
|
12
|
+
auth: options.auth,
|
|
13
|
+
headers: options.headers,
|
|
14
|
+
host: options.host,
|
|
15
|
+
initialEvents: options.initialEvents,
|
|
16
|
+
initialSession: options.initialSession,
|
|
17
|
+
maxReconnectAttempts: options.maxReconnectAttempts,
|
|
18
|
+
optimistic: options.optimistic,
|
|
19
|
+
reducer,
|
|
20
|
+
session: options.session,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const store = storeRef.current;
|
|
24
|
+
store.setCallbacks({
|
|
25
|
+
onError: options.onError,
|
|
26
|
+
onEvent: options.onEvent,
|
|
27
|
+
onFinish: options.onFinish,
|
|
28
|
+
onSessionChange: options.onSessionChange,
|
|
29
|
+
});
|
|
30
|
+
const subscribe = useCallback((onStoreChange) => store.subscribe(onStoreChange), [store]);
|
|
31
|
+
const snapshot = useSyncExternalStore(subscribe, () => store.snapshot, () => store.snapshot);
|
|
32
|
+
const reset = useCallback(() => store.reset(), [store]);
|
|
33
|
+
const send = useCallback((input, sendOptions) => {
|
|
34
|
+
return store.send(input, sendOptions);
|
|
35
|
+
}, [store]);
|
|
36
|
+
const sendMessage = useCallback((message, sendOptions) => {
|
|
37
|
+
return store.sendMessage(message, sendOptions);
|
|
38
|
+
}, [store]);
|
|
39
|
+
const stop = useCallback(() => store.stop(), [store]);
|
|
40
|
+
return useMemo(() => ({
|
|
41
|
+
...snapshot,
|
|
42
|
+
reset,
|
|
43
|
+
send,
|
|
44
|
+
sendMessage,
|
|
45
|
+
stop,
|
|
46
|
+
}), [reset, send, sendMessage, snapshot, stop]);
|
|
47
|
+
}
|
|
48
|
+
class UseAshAgentStore {
|
|
49
|
+
#createSession;
|
|
50
|
+
#optimistic;
|
|
51
|
+
#reducer;
|
|
52
|
+
#subscribers = new Set();
|
|
53
|
+
#abortController;
|
|
54
|
+
#callbacks = {};
|
|
55
|
+
#data;
|
|
56
|
+
#error;
|
|
57
|
+
#events;
|
|
58
|
+
#operationId = 0;
|
|
59
|
+
#pendingMessageSubmission;
|
|
60
|
+
#projectionEvents;
|
|
61
|
+
#session;
|
|
62
|
+
#snapshot;
|
|
63
|
+
#status = "ready";
|
|
64
|
+
constructor(init) {
|
|
65
|
+
this.#createSession = init.session
|
|
66
|
+
? undefined
|
|
67
|
+
: () => new Client({
|
|
68
|
+
auth: init.auth,
|
|
69
|
+
headers: init.headers,
|
|
70
|
+
host: init.host ?? "",
|
|
71
|
+
maxReconnectAttempts: init.maxReconnectAttempts,
|
|
72
|
+
}).session(init.initialSession);
|
|
73
|
+
this.#events = [...(init.initialEvents ?? [])];
|
|
74
|
+
this.#projectionEvents = [...this.#events];
|
|
75
|
+
this.#optimistic = init.optimistic ?? true;
|
|
76
|
+
this.#reducer = init.reducer;
|
|
77
|
+
this.#session = init.session ?? this.#createOwnedSession();
|
|
78
|
+
this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
|
|
79
|
+
this.#snapshot = this.#createSnapshot();
|
|
80
|
+
}
|
|
81
|
+
get snapshot() {
|
|
82
|
+
return this.#snapshot;
|
|
83
|
+
}
|
|
84
|
+
setCallbacks(callbacks) {
|
|
85
|
+
this.#callbacks = callbacks;
|
|
86
|
+
}
|
|
87
|
+
subscribe(callback) {
|
|
88
|
+
this.#subscribers.add(callback);
|
|
89
|
+
return () => {
|
|
90
|
+
this.#subscribers.delete(callback);
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
async sendMessage(message, options) {
|
|
94
|
+
await this.send({ message }, options);
|
|
95
|
+
}
|
|
96
|
+
async send(input, options) {
|
|
97
|
+
if (this.#status === "streaming" || this.#status === "submitted") {
|
|
98
|
+
throw new Error("Ash session is already processing a turn.");
|
|
99
|
+
}
|
|
100
|
+
const operationId = this.#startOperation();
|
|
101
|
+
const abortController = new AbortController();
|
|
102
|
+
this.#abortController = abortController;
|
|
103
|
+
this.#error = undefined;
|
|
104
|
+
this.#status = "submitted";
|
|
105
|
+
this.#projectOptimisticMessage(input);
|
|
106
|
+
this.#projectInputResponses(input);
|
|
107
|
+
this.#publish();
|
|
108
|
+
try {
|
|
109
|
+
const response = await this.#session.send(input, {
|
|
110
|
+
...options,
|
|
111
|
+
signal: createAbortSignal(options?.signal, abortController.signal),
|
|
112
|
+
});
|
|
113
|
+
let sawEvent = false;
|
|
114
|
+
for await (const event of response) {
|
|
115
|
+
if (!this.#isCurrentOperation(operationId)) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (!sawEvent) {
|
|
119
|
+
sawEvent = true;
|
|
120
|
+
this.#status = "streaming";
|
|
121
|
+
}
|
|
122
|
+
this.#events = [...this.#events, event];
|
|
123
|
+
this.#applyServerEvent(event);
|
|
124
|
+
this.#callbacks.onEvent?.(event);
|
|
125
|
+
this.#applyTerminalStreamFailure(event);
|
|
126
|
+
this.#publish();
|
|
127
|
+
}
|
|
128
|
+
if (!this.#isCurrentOperation(operationId)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
this.#status = this.#error === undefined ? "ready" : "error";
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (!this.#isCurrentOperation(operationId)) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (isAbortError(error)) {
|
|
138
|
+
this.#status = "ready";
|
|
139
|
+
this.#failPendingMessageSubmission(toError(error));
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
this.#error = toError(error);
|
|
143
|
+
this.#status = "error";
|
|
144
|
+
this.#failPendingMessageSubmission(this.#error);
|
|
145
|
+
this.#callbacks.onError?.(this.#error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
if (this.#isCurrentOperation(operationId)) {
|
|
150
|
+
this.#abortController = undefined;
|
|
151
|
+
this.#callbacks.onSessionChange?.(this.#session.state);
|
|
152
|
+
this.#publish();
|
|
153
|
+
this.#callbacks.onFinish?.(this.#snapshot);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
stop() {
|
|
158
|
+
this.#abortController?.abort();
|
|
159
|
+
}
|
|
160
|
+
reset() {
|
|
161
|
+
this.#invalidateOperation();
|
|
162
|
+
this.stop();
|
|
163
|
+
this.#abortController = undefined;
|
|
164
|
+
this.#session = this.#createSession?.() ?? this.#session;
|
|
165
|
+
this.#events = [];
|
|
166
|
+
this.#pendingMessageSubmission = undefined;
|
|
167
|
+
this.#projectionEvents = [];
|
|
168
|
+
this.#data = this.#reducer.initial();
|
|
169
|
+
this.#error = undefined;
|
|
170
|
+
this.#status = "ready";
|
|
171
|
+
this.#callbacks.onSessionChange?.(this.#session.state);
|
|
172
|
+
this.#publish();
|
|
173
|
+
}
|
|
174
|
+
#createOwnedSession() {
|
|
175
|
+
if (!this.#createSession) {
|
|
176
|
+
throw new Error("Cannot create an owned Ash session from an external session.");
|
|
177
|
+
}
|
|
178
|
+
return this.#createSession();
|
|
179
|
+
}
|
|
180
|
+
#startOperation() {
|
|
181
|
+
this.#operationId += 1;
|
|
182
|
+
return this.#operationId;
|
|
183
|
+
}
|
|
184
|
+
#invalidateOperation() {
|
|
185
|
+
this.#operationId += 1;
|
|
186
|
+
}
|
|
187
|
+
#isCurrentOperation(operationId) {
|
|
188
|
+
return this.#operationId === operationId;
|
|
189
|
+
}
|
|
190
|
+
#projectOptimisticMessage(input) {
|
|
191
|
+
if (!this.#optimistic || input.message === undefined) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const id = createSubmissionId();
|
|
195
|
+
const pending = {
|
|
196
|
+
createdAt: Date.now(),
|
|
197
|
+
id,
|
|
198
|
+
message: input.message,
|
|
199
|
+
};
|
|
200
|
+
this.#pendingMessageSubmission = pending;
|
|
201
|
+
this.#appendProjectionEvent({
|
|
202
|
+
data: {
|
|
203
|
+
createdAt: pending.createdAt,
|
|
204
|
+
message: pending.message,
|
|
205
|
+
submissionId: pending.id,
|
|
206
|
+
},
|
|
207
|
+
type: "client.message.submitted",
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
#projectInputResponses(input) {
|
|
211
|
+
if (input.inputResponses === undefined || input.inputResponses.length === 0) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.#appendProjectionEvent({
|
|
215
|
+
data: {
|
|
216
|
+
createdAt: Date.now(),
|
|
217
|
+
responses: input.inputResponses,
|
|
218
|
+
},
|
|
219
|
+
type: "client.input.responded",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
#applyServerEvent(event) {
|
|
223
|
+
if (event.type === "message.received" && this.#pendingMessageSubmission !== undefined) {
|
|
224
|
+
const submissionId = this.#pendingMessageSubmission.id;
|
|
225
|
+
this.#pendingMessageSubmission = undefined;
|
|
226
|
+
this.#replaceProjectionEvent((candidate) => candidate.type === "client.message.submitted" &&
|
|
227
|
+
candidate.data.submissionId === submissionId, event);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
this.#appendProjectionEvent(event);
|
|
231
|
+
}
|
|
232
|
+
#applyTerminalStreamFailure(event) {
|
|
233
|
+
const error = toTerminalStreamFailureError(event);
|
|
234
|
+
if (error === undefined) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
this.#status = "error";
|
|
238
|
+
this.#failPendingMessageSubmission(error);
|
|
239
|
+
if (this.#error === undefined) {
|
|
240
|
+
this.#error = error;
|
|
241
|
+
this.#callbacks.onError?.(error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
#failPendingMessageSubmission(error) {
|
|
245
|
+
const pending = this.#pendingMessageSubmission;
|
|
246
|
+
if (pending === undefined) {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
this.#pendingMessageSubmission = undefined;
|
|
250
|
+
this.#replaceProjectionEvent((event) => event.type === "client.message.submitted" && event.data.submissionId === pending.id, {
|
|
251
|
+
data: {
|
|
252
|
+
createdAt: pending.createdAt,
|
|
253
|
+
error: {
|
|
254
|
+
message: error.message,
|
|
255
|
+
},
|
|
256
|
+
message: pending.message,
|
|
257
|
+
submissionId: pending.id,
|
|
258
|
+
},
|
|
259
|
+
type: "client.message.failed",
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
#appendProjectionEvent(event) {
|
|
263
|
+
this.#projectionEvents = [...this.#projectionEvents, event];
|
|
264
|
+
this.#data = this.#reducer.reduce(this.#data, event);
|
|
265
|
+
}
|
|
266
|
+
#replaceProjectionEvent(predicate, replacement) {
|
|
267
|
+
let replaced = false;
|
|
268
|
+
this.#projectionEvents = this.#projectionEvents.map((event) => {
|
|
269
|
+
if (!replaced && predicate(event)) {
|
|
270
|
+
replaced = true;
|
|
271
|
+
return replacement;
|
|
272
|
+
}
|
|
273
|
+
return event;
|
|
274
|
+
});
|
|
275
|
+
if (!replaced) {
|
|
276
|
+
this.#projectionEvents = [...this.#projectionEvents, replacement];
|
|
277
|
+
}
|
|
278
|
+
this.#data = this.#reduceProjectionEvents(this.#projectionEvents);
|
|
279
|
+
}
|
|
280
|
+
#reduceProjectionEvents(events) {
|
|
281
|
+
let data = this.#reducer.initial();
|
|
282
|
+
for (const event of events) {
|
|
283
|
+
data = this.#reducer.reduce(data, event);
|
|
284
|
+
}
|
|
285
|
+
return data;
|
|
286
|
+
}
|
|
287
|
+
#createSnapshot() {
|
|
288
|
+
return {
|
|
289
|
+
data: this.#data,
|
|
290
|
+
error: this.#error,
|
|
291
|
+
events: this.#events,
|
|
292
|
+
session: this.#session.state,
|
|
293
|
+
status: this.#status,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
#publish() {
|
|
297
|
+
this.#snapshot = this.#createSnapshot();
|
|
298
|
+
for (const subscriber of this.#subscribers) {
|
|
299
|
+
subscriber();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
let submissionSequence = 0;
|
|
304
|
+
function createSubmissionId() {
|
|
305
|
+
const randomUUID = globalThis.crypto?.randomUUID;
|
|
306
|
+
if (randomUUID !== undefined) {
|
|
307
|
+
return randomUUID.call(globalThis.crypto);
|
|
308
|
+
}
|
|
309
|
+
submissionSequence += 1;
|
|
310
|
+
return `submission_${submissionSequence.toString()}`;
|
|
311
|
+
}
|
|
312
|
+
function createAbortSignal(first, second) {
|
|
313
|
+
return first ? AbortSignal.any([first, second]) : second;
|
|
314
|
+
}
|
|
315
|
+
function isAbortError(error) {
|
|
316
|
+
return error instanceof Error && error.name === "AbortError";
|
|
317
|
+
}
|
|
318
|
+
function toError(error) {
|
|
319
|
+
if (error instanceof Error)
|
|
320
|
+
return error;
|
|
321
|
+
return new Error(String(error));
|
|
322
|
+
}
|
|
323
|
+
function toTerminalStreamFailureError(event) {
|
|
324
|
+
if (event.type !== "session.failed") {
|
|
325
|
+
return undefined;
|
|
326
|
+
}
|
|
327
|
+
const error = new Error(event.data.message);
|
|
328
|
+
error.name = event.data.code;
|
|
329
|
+
return error;
|
|
330
|
+
}
|
|
@@ -16,7 +16,6 @@ import type { InternalSkillDefinition } from "#shared/skill-definition.js";
|
|
|
16
16
|
import type { InternalAgentDefinition } from "#shared/agent-definition.js";
|
|
17
17
|
import type { InternalToolDefinitionWithExecuteFn } from "#shared/tool-definition.js";
|
|
18
18
|
import type { SandboxDefinition } from "#shared/sandbox-definition.js";
|
|
19
|
-
import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
20
19
|
/**
|
|
21
20
|
* Runtime-owned source ref describing one additive config module import.
|
|
22
21
|
*/
|
|
@@ -83,7 +82,7 @@ export interface ResolvedConnectionDefinition extends ResolvedModuleSourceRef {
|
|
|
83
82
|
* `vercelBackend()` and `localBackend()` based on the current
|
|
84
83
|
* environment).
|
|
85
84
|
*/
|
|
86
|
-
export type ResolvedSandboxDefinition = Readonly<SandboxDefinition
|
|
85
|
+
export type ResolvedSandboxDefinition = Readonly<SandboxDefinition> & ResolvedModuleSourceRef & {
|
|
87
86
|
readonly description?: string;
|
|
88
87
|
};
|
|
89
88
|
/**
|
|
@@ -7,9 +7,9 @@ import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
|
7
7
|
* runtime orchestrator can persist reconnect metadata and release
|
|
8
8
|
* resources.
|
|
9
9
|
*/
|
|
10
|
-
export interface SandboxBackendHandle<
|
|
10
|
+
export interface SandboxBackendHandle<SO = Record<string, never>> {
|
|
11
11
|
readonly session: SandboxSession;
|
|
12
|
-
readonly useSessionFn: SandboxSessionUseFn<
|
|
12
|
+
readonly useSessionFn: SandboxSessionUseFn<SO>;
|
|
13
13
|
captureState(): Promise<SandboxBackendSessionState>;
|
|
14
14
|
dispose(): Promise<void>;
|
|
15
15
|
}
|
|
@@ -97,7 +97,7 @@ export interface SandboxBackendPrewarmInput<BO = Record<string, never>> {
|
|
|
97
97
|
* Each backend owns the full template-then-session lifecycle internally;
|
|
98
98
|
* callers only need a single `create` call.
|
|
99
99
|
*/
|
|
100
|
-
export interface SandboxBackend<
|
|
100
|
+
export interface SandboxBackend<BO = Record<string, never>, SO = Record<string, never>> {
|
|
101
101
|
/**
|
|
102
102
|
* Stable identifier for this backend implementation.
|
|
103
103
|
*
|
|
@@ -113,7 +113,7 @@ export interface SandboxBackend<S extends SandboxSession = SandboxSession, BO =
|
|
|
113
113
|
* {@link SandboxTemplateNotProvisionedError} when the requested
|
|
114
114
|
* template is missing.
|
|
115
115
|
*/
|
|
116
|
-
create(input: SandboxBackendCreateInput): Promise<SandboxBackendHandle<
|
|
116
|
+
create(input: SandboxBackendCreateInput): Promise<SandboxBackendHandle<SO>>;
|
|
117
117
|
/**
|
|
118
118
|
* Build-time prewarm hook. Ash invokes this for every authored
|
|
119
119
|
* sandbox in the compiled graph before serving traffic so the backend
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { SandboxSession } from "#shared/sandbox-session.js";
|
|
2
2
|
import type { SandboxBackend } from "#shared/sandbox-backend.js";
|
|
3
3
|
export type SandboxBootstrapUseFn<O = Record<string, never>> = (options?: O) => Promise<SandboxSession>;
|
|
4
|
-
export type SandboxSessionUseFn<
|
|
4
|
+
export type SandboxSessionUseFn<O = Record<string, never>> = (options?: O) => Promise<SandboxSession>;
|
|
5
5
|
export interface SandboxBootstrapContext<O = Record<string, never>> {
|
|
6
6
|
readonly use: SandboxBootstrapUseFn<O>;
|
|
7
7
|
}
|
|
8
|
-
export interface SandboxSessionContext<
|
|
9
|
-
readonly use: SandboxSessionUseFn<
|
|
8
|
+
export interface SandboxSessionContext<O = Record<string, never>> {
|
|
9
|
+
readonly use: SandboxSessionUseFn<O>;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
12
|
* Public sandbox definition authored in `agent/sandbox.ts` (shorthand)
|
|
@@ -21,7 +21,7 @@ export interface SandboxSessionContext<S extends SandboxSession = SandboxSession
|
|
|
21
21
|
* `subagents/<name>/sandbox.ts` (or the folder form) and do not inherit
|
|
22
22
|
* their parent's sandbox (skill seeds differ per agent).
|
|
23
23
|
*/
|
|
24
|
-
export interface SandboxDefinition<
|
|
24
|
+
export interface SandboxDefinition<BO = Record<string, never>, SO = Record<string, never>> {
|
|
25
25
|
/**
|
|
26
26
|
* Backend that runs this sandbox.
|
|
27
27
|
*
|
|
@@ -31,7 +31,7 @@ export interface SandboxDefinition<S extends SandboxSession = SandboxSession, BO
|
|
|
31
31
|
* everywhere else. Set `backend` explicitly to pin the sandbox to a
|
|
32
32
|
* specific backend regardless of environment.
|
|
33
33
|
*/
|
|
34
|
-
backend: SandboxBackend<
|
|
34
|
+
backend: SandboxBackend<BO, SO>;
|
|
35
35
|
bootstrap?(input: SandboxBootstrapContext<BO>): Promise<void> | void;
|
|
36
|
-
onSession?(input: SandboxSessionContext<
|
|
36
|
+
onSession?(input: SandboxSessionContext<SO>): Promise<void> | void;
|
|
37
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "experimental-ash",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"bin": {
|
|
5
5
|
"ash": "./bin/ash.js",
|
|
6
6
|
"experimental-ash": "./bin/ash.js"
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
"import": "./dist/src/client/index.js",
|
|
37
37
|
"default": "./dist/src/client/index.js"
|
|
38
38
|
},
|
|
39
|
+
"./react": {
|
|
40
|
+
"types": "./dist/src/react/index.d.ts",
|
|
41
|
+
"import": "./dist/src/react/index.js",
|
|
42
|
+
"default": "./dist/src/react/index.js"
|
|
43
|
+
},
|
|
39
44
|
"./tools/approval": {
|
|
40
45
|
"types": "./dist/src/public/tools/approval/index.d.ts",
|
|
41
46
|
"import": "./dist/src/public/tools/approval/index.js",
|
|
@@ -160,6 +165,8 @@
|
|
|
160
165
|
"@chat-adapter/state-memory": "4.28.1",
|
|
161
166
|
"@standard-schema/spec": "1.1.0",
|
|
162
167
|
"@types/json-schema": "7.0.15",
|
|
168
|
+
"@types/react": "^19.2.14",
|
|
169
|
+
"@types/react-test-renderer": "19.1.0",
|
|
163
170
|
"@vercel/oidc": "3.4.0",
|
|
164
171
|
"@vercel/sandbox": "2.0.0-beta.14",
|
|
165
172
|
"@workflow/core": "5.0.0-beta.5",
|
|
@@ -175,6 +182,8 @@
|
|
|
175
182
|
"jsonc-parser": "3.3.1",
|
|
176
183
|
"just-bash": "2.14.5",
|
|
177
184
|
"picocolors": "^1.1.1",
|
|
185
|
+
"react": "^19.2.6",
|
|
186
|
+
"react-test-renderer": "19.2.6",
|
|
178
187
|
"turndown": "7.2.4",
|
|
179
188
|
"zod": "^4.4.3",
|
|
180
189
|
"zod-validation-error": "5.0.0"
|
|
@@ -182,7 +191,8 @@
|
|
|
182
191
|
"peerDependencies": {
|
|
183
192
|
"@opentelemetry/api": "^1.9.0",
|
|
184
193
|
"ai": "7.0.0-canary.138",
|
|
185
|
-
"braintrust": ">=3.0.0"
|
|
194
|
+
"braintrust": ">=3.0.0",
|
|
195
|
+
"react": "^19.2.6"
|
|
186
196
|
},
|
|
187
197
|
"peerDependenciesMeta": {
|
|
188
198
|
"@opentelemetry/api": {
|
|
@@ -190,6 +200,9 @@
|
|
|
190
200
|
},
|
|
191
201
|
"braintrust": {
|
|
192
202
|
"optional": true
|
|
203
|
+
},
|
|
204
|
+
"react": {
|
|
205
|
+
"optional": true
|
|
193
206
|
}
|
|
194
207
|
},
|
|
195
208
|
"engines": {
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{i as e,t}from"./chunk-8L7ocgPr.js";import{_ as n,f as r,g as i,l as a,p as o,v as s}from"./types-MZUhN0Zy.js";import{n as c,r as l,t as u}from"./token-util-CHjOk3A7.js";var d=class extends Error{status;body;constructor(e,t){super(t||`Server returned ${e}.`),this.name=`ClientError`,this.status=e,this.body=t}};function f(e){if(e instanceof DOMException)return e.name===`AbortError`;if(!(e instanceof Error))return!1;let t=`code`in e&&typeof e.code==`string`?e.code:void 0;return e.name===`AbortError`||e.message===`terminated`||t===`UND_ERR_SOCKET`||/abort|cancel|disconnect|premature close|socket|terminated/i.test(e.message)}async function*p(e){let t=e.getReader(),n=new TextDecoder,r=``;try{for(;;){let e=await t.read();if(e.done){r+=n.decode();break}e.value&&(r+=n.decode(e.value,{stream:!0}));let i=r.indexOf(`
|
|
2
|
-
`);for(;i!==-1;){let e=r.slice(0,i).trim();r=r.slice(i+1),e.length>0&&(yield JSON.parse(e)),i=r.indexOf(`
|
|
3
|
-
`)}}let e=r.trim();e.length>0&&(yield JSON.parse(e))}finally{t.releaseLock()}}async function*m(e){let t=e.startIndex,n=e.maxReconnectAttempts;for(;;){let r=new URL(s(e.sessionId),e.host);t>0&&r.searchParams.set(`startIndex`,String(t));let i=await e.resolveHeaders(),a=await fetch(r,{headers:i,signal:e.signal??null});if(!a.ok){let e=await a.text();throw new d(a.status,e)}if(!a.body)throw new d(a.status,`Response body is null.`);let o=!1;try{for await(let e of p(a.body))t+=1,yield e}catch(e){if(!f(e))throw e;o=!0}if(!o||n<=0)return;--n}}var h=t(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},c=e=>s(n({},`__esModule`,{value:!0}),e),l={};o(l,{SYMBOL_FOR_REQ_CONTEXT:()=>u,getContext:()=>d}),t.exports=c(l);let u=Symbol.for(`@vercel/request-context`);function d(){return globalThis[u]?.get?.()??{}}})),g=t(((t,n)=>{var r=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,o=Object.prototype.hasOwnProperty,s=(e,t)=>{for(var n in t)r(e,n,{get:t[n],enumerable:!0})},c=(e,t,n,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of a(t))!o.call(e,c)&&c!==n&&r(e,c,{get:()=>t[c],enumerable:!(s=i(t,c))||s.enumerable});return e},u=e=>c(r({},`__esModule`,{value:!0}),e),d={};s(d,{getVercelOidcToken:()=>m,getVercelOidcTokenSync:()=>g}),n.exports=u(d);var f=h(),p=l();async function m(t){let n=``,r;try{n=g()}catch(e){r=e}try{let[{getTokenPayload:r,isExpired:i},{refreshToken:a}]=await Promise.all([await import(`./token-util-CHjOk3A7.js`).then(t=>e(t.t())),await import(`./token-DtoyQZy2.js`).then(t=>e(t.default))]);(!n||i(r(n),t?.expirationBufferMs))&&(await a(t),n=g())}catch(e){let t=r instanceof Error?r.message:``;throw e instanceof Error&&(t=`${t}
|
|
4
|
-
${e.message}`),t?new p.VercelOidcTokenError(t):e}return n}function g(){let e=(0,f.getContext)().headers?.[`x-vercel-oidc-token`]??process.env.VERCEL_OIDC_TOKEN;if(!e)throw Error(`The 'x-vercel-oidc-token' header is missing from the request. Do you have the OIDC option enabled in the Vercel project settings?`);return e}})),_=t(((e,t)=>{var n=Object.defineProperty,r=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.prototype.hasOwnProperty,o=(e,t)=>{for(var r in t)n(e,r,{get:t[r],enumerable:!0})},s=(e,t,o,s)=>{if(t&&typeof t==`object`||typeof t==`function`)for(let c of i(t))!a.call(e,c)&&c!==o&&n(e,c,{get:()=>t[c],enumerable:!(s=r(t,c))||s.enumerable});return e},l=e=>s(n({},`__esModule`,{value:!0}),e),d={};o(d,{AccessTokenMissingError:()=>m.AccessTokenMissingError,RefreshAccessTokenFailedError:()=>m.RefreshAccessTokenFailedError,getContext:()=>p.getContext,getVercelOidcToken:()=>f.getVercelOidcToken,getVercelOidcTokenSync:()=>f.getVercelOidcTokenSync,getVercelToken:()=>_.getVercelToken}),t.exports=l(d);var f=g(),p=h(),m=c(),_=u()})),v=_();const y=`${i}/`,b=new Set([`localhost`,`127.0.0.1`,`0.0.0.0`,`::1`,`[::1]`]);function x(e){return b.has(e.hostname)}const S=`x-vercel-protection-bypass`,C=`x-vercel-trusted-oidc-idp-token`;function w(e){return e.pathname.endsWith(`/ash/v1`)||e.pathname.includes(y)}async function T(e){let t=E(e),n=await O(t,e.resourceUrl);return n!==null&&D(t,n),t}function E(e){let t=new Headers(j(e.headers)),n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&w(e.resourceUrl)&&t.set(S,n),t}function D(e,t){e.has(`authorization`)||e.set(`authorization`,`Bearer ${t}`),e.set(C,t)}async function O(e,t){return k(t)?e.get(`x-vercel-oidc-token`)?.trim()||await A():null}function k(e){return!(!w(e)||x(e))}async function A(){let e=process.env.VERCEL_OIDC_TOKEN?.trim();try{let e=(await(0,v.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{return e??null}return e??null}function j(e){if(e!==void 0)return e instanceof Headers?e:Array.isArray(e)?e.map(([e,t])=>[e,t]):e}function M(){return{streamIndex:0}}function N(e){let t=I(e.events),n=e.session.streamIndex+e.events.length;return t?.type===`session.waiting`?{continuationToken:e.continuationToken??e.session.continuationToken,sessionId:e.sessionId,streamIndex:n}:M()}function P(e){let t;for(let n of e)L(n)&&(t=n.data.message??void 0);return t}function F(e){let t=I(e);return t?.type===`session.waiting`?`waiting`:t?.type===`session.failed`?`failed`:`completed`}function I(e){for(let t=e.length-1;t>=0;t--){let n=e[t];if(n!==void 0&&a(n))return n}}function L(e){return e.type===`message.completed`&&e.data.finishReason!==`tool-calls`}var R=class{continuationToken;sessionId;#e=!1;#t;constructor(e){this.continuationToken=e.continuationToken,this.sessionId=e.sessionId,this.#t=e.createStream}async result(){let e=[];for await(let t of this)e.push(t);return{events:e,message:P(e),sessionId:this.sessionId,status:F(e)}}[Symbol.asyncIterator](){if(this.#e)throw Error(`MessageResponse has already been consumed.`);return this.#e=!0,this.#t()}},z=class{#e;#t;constructor(e,t){this.#e=e,this.#t=t}get state(){return this.#t}async sendMessage(e,t){return this.send({message:e},t)}async send(e,t){let n=this.#t,{continuationToken:r,sessionId:i}=await this.#n(e,n,t);return new R({continuationToken:r,createStream:()=>this.#r(i,r,n,t),sessionId:i})}openStream(e){let t=this.#t.sessionId;if(!t)throw Error(`Session has no session ID. Send a message first.`);return m({host:this.#e.host,maxReconnectAttempts:this.#e.maxReconnectAttempts,resolveHeaders:()=>this.#e.resolveHeaders(),sessionId:t,signal:e?.signal,startIndex:e?.startIndex??this.#t.streamIndex})}async#n(e,t,i){let a=t.sessionId?n(t.sessionId):r,o=new URL(a,this.#e.host),s=await this.#e.resolveHeaders(i?.headers);s.set(`content-type`,`application/json`);let c=B({input:e,session:t});if(c===null)throw Error(`Session.send requires a non-empty message, inputResponses, or both.`);let l=await fetch(o,{body:JSON.stringify(c),headers:s,method:`POST`,signal:i?.signal??null});if(!l.ok){let e=await l.text();throw new d(l.status,e)}let u=await l.json(),f=(typeof u.sessionId==`string`?u.sessionId:void 0)??l.headers.get(`x-ash-session-id`)?.trim()??t.sessionId;if(!f)throw Error(`Message route did not return a session id.`);return{continuationToken:typeof u.continuationToken==`string`?u.continuationToken:void 0,sessionId:f}}async*#r(e,t,n,r){let i=[];try{let t=n.sessionId===e?n.streamIndex:0,o=this.#e.maxReconnectAttempts;for(;;){let n=await this.#i(e,t,r?.signal),s=!1;try{for await(let e of p(n))if(i.push(e),t+=1,yield e,a(e)){s=!0;break}}catch(e){if(!f(e))throw e}if(s||o<=0)break;--o}}finally{this.#t=N({continuationToken:t,events:i,sessionId:e,session:n})}}async#i(e,t,n){let r=new URL(s(e),this.#e.host);t>0&&r.searchParams.set(`startIndex`,String(t));let i=await this.#e.resolveHeaders(),a=await fetch(r,{headers:i,signal:n??null});if(!a.ok){let e=await a.text();throw new d(a.status,e)}if(!a.body)throw new d(a.status,`Response body is null.`);return a.body}};function B(e){let t={};return e.input.message!==void 0&&(t.message=e.input.message),e.input.inputResponses!==void 0&&e.input.inputResponses.length>0&&(t.inputResponses=e.input.inputResponses),e.session.continuationToken!==void 0&&(t.continuationToken=e.session.continuationToken),Object.keys(t).length===0||e.session.continuationToken===void 0&&t.message===void 0||`continuationToken`in t&&Object.keys(t).length===1?null:t}var V=class{#e;#t;#n;#r;constructor(e){this.#n=e.host,this.#e=e.auth,this.#t=e.headers,this.#r=e.maxReconnectAttempts??3}async health(){let e=new URL(o,this.#n),t=await this.#i(),n=await fetch(e,{headers:t});if(!n.ok){let e=await n.text();throw new d(n.status,e)}return await n.json()}session(e){let t;return t=typeof e==`string`?{continuationToken:e,streamIndex:0}:e||M(),new z({host:this.#n,maxReconnectAttempts:this.#r,resolveHeaders:e=>this.#i(e)},t)}async#i(e){let t=new Headers,n=await U(this.#t);for(let[e,r]of Object.entries(n))t.set(e,r);if(e)for(let[n,r]of Object.entries(e))t.set(n,r);let r=await this.#a();return r&&t.set(`authorization`,r),t}async#a(){let e=this.#e;if(e){if(`bearer`in e){let t=(await H(e.bearer)).trim();return t.length===0?void 0:`Bearer ${t}`}if(`basic`in e){let t=await H(e.basic.password);return`Basic ${W(e.basic.username,t)}`}}}};async function H(e){return typeof e==`function`?e():e}async function U(e){return e===void 0?{}:typeof e==`function`?await e():e}function W(e,t){let n=new TextEncoder().encode(`${e}:${t}`),r=Array.from(n,e=>String.fromCodePoint(e)).join(``);return btoa(r)}export{_ as a,T as i,S as n,m as o,C as r,d as s,V as t};
|