stitchkit 0.90.7 → 0.91.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 +107 -0
- package/dist/agent-runtime-coding-tools.js +4 -4
- package/dist/agent-runtime-harness.js +6 -6
- package/dist/agent-runtime-sandbox.js +4 -4
- package/dist/agent-runtime.js +18 -17
- package/dist/application-grammy.js +1 -1
- package/dist/application.js +3 -3
- package/dist/cli.js +7 -7
- package/dist/contract/errors-factory.d.ts +12 -0
- package/dist/contract/errors-factory.d.ts.map +1 -1
- package/dist/contract/errors.d.ts +35 -1
- package/dist/contract/errors.d.ts.map +1 -1
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.d.ts.map +1 -1
- package/dist/contract/index.js +4 -2
- package/dist/{index-nrqg8tks.js → index-0a349c33.js} +3 -3
- package/dist/index-0j3vcp5m.js +28 -0
- package/dist/{index-vy2nxwg8.js → index-137jjs4v.js} +2 -2
- package/dist/{index-2hryh65w.js → index-13cjtmrw.js} +54 -13
- package/dist/{index-7gqx71yd.js → index-13e1k7v9.js} +9 -3
- package/dist/{index-1bnkzq95.js → index-1qvdwnp2.js} +10 -3
- package/dist/{index-scs3f1eg.js → index-2vtcmrj9.js} +8 -2
- package/dist/{index-xbppf54a.js → index-6kyksz9y.js} +12 -7
- package/dist/{index-aj9geeez.js → index-709nh7z7.js} +37 -17
- package/dist/{index-zkcqhxx8.js → index-a11g2kry.js} +1 -1
- package/dist/{index-1txvygak.js → index-b971qjes.js} +1 -1
- package/dist/{index-tgh3ksfh.js → index-bh70rk41.js} +3 -3
- package/dist/{index-bva395we.js → index-bv7mytab.js} +2 -2
- package/dist/{index-s208wab5.js → index-bx6yq2m4.js} +23 -495
- package/dist/{index-83fafqw8.js → index-f4p4b8f6.js} +10 -11
- package/dist/index-kw9t5ayn.js +507 -0
- package/dist/{index-8pjqv3zh.js → index-mqhjmadk.js} +43 -21
- package/dist/{index-3yqgj323.js → index-qjeyx5t2.js} +2 -2
- package/dist/{index-gte38nbm.js → index-qmy6zq75.js} +1 -1
- package/dist/{index-zsdgd1tz.js → index-qrbdq8jk.js} +1 -1
- package/dist/{index-fqg5mfk7.js → index-ya611vx3.js} +4 -4
- package/dist/{index-640tz39q.js → index-zsqy3xfn.js} +5 -5
- package/dist/index.js +8 -6
- package/dist/internal/canonical-json.d.ts +20 -0
- package/dist/internal/canonical-json.d.ts.map +1 -0
- package/dist/internal/errors.d.ts +13 -4
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/node.js +6 -6
- package/dist/observability/bounded-logger.d.ts +2 -0
- package/dist/observability/bounded-logger.d.ts.map +1 -1
- package/dist/observability/index.js +7 -4
- package/dist/observability/sanitize.d.ts +15 -0
- package/dist/observability/sanitize.d.ts.map +1 -1
- package/dist/primitives.js +2 -2
- package/dist/realtime/rejected-frame.d.ts +6 -0
- package/dist/realtime/rejected-frame.d.ts.map +1 -1
- package/dist/remote.js +5 -5
- package/dist/server/implement.d.ts +33 -7
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -8
- package/dist/testing/surface-manifest.d.ts +2 -1
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/testing.js +11 -27
- package/dist/tool-invoker.js +6 -6
- package/dist/tools/agent.d.ts +12 -0
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/cli-format.d.ts +1 -1
- package/dist/tools/connections/index.js +1 -1
- package/dist/tools/durability-context.d.ts +1 -1
- package/dist/tools/durability-context.d.ts.map +1 -1
- package/dist/tools/durability-port.d.ts +55 -0
- package/dist/tools/durability-port.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +59 -1
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/mcp-catalog.d.ts +68 -0
- package/dist/tools/mcp-catalog.d.ts.map +1 -0
- package/dist/tools/mcp.d.ts +3 -0
- package/dist/tools/mcp.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +2 -2
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-mcp.d.ts +3 -0
- package/dist/tools/native-mcp.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +1 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools.d.ts +5 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +99 -20
- package/dist/tracking.js +2 -2
- package/llms-full.txt +258 -8
- package/package.json +2 -2
- package/dist/index-7rkhw9ec.js +0 -34
|
@@ -0,0 +1,507 @@
|
|
|
1
|
+
// src/agent-runtime/durability-contract.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var DURABILITY_STEP_EVENT_KIND = "durability/step";
|
|
4
|
+
var DURABILITY_PARK_EVENT_KIND = "durability/park";
|
|
5
|
+
var DURABILITY_EVENT_EVENT_KIND = "durability/event";
|
|
6
|
+
var StepEventPayloadSchema = z.object({
|
|
7
|
+
runId: z.string().min(1),
|
|
8
|
+
stepName: z.string().min(1),
|
|
9
|
+
encoded: z.string()
|
|
10
|
+
});
|
|
11
|
+
var SleepParkSchema = z.object({
|
|
12
|
+
runId: z.string().min(1),
|
|
13
|
+
kind: z.literal("sleep"),
|
|
14
|
+
name: z.string().min(1),
|
|
15
|
+
deadline: z.number()
|
|
16
|
+
});
|
|
17
|
+
var WaitParkSchema = z.object({
|
|
18
|
+
runId: z.string().min(1),
|
|
19
|
+
kind: z.literal("wait"),
|
|
20
|
+
event: z.string().min(1),
|
|
21
|
+
id: z.string().min(1)
|
|
22
|
+
});
|
|
23
|
+
var ParkEventPayloadSchema = z.discriminatedUnion("kind", [
|
|
24
|
+
SleepParkSchema,
|
|
25
|
+
WaitParkSchema
|
|
26
|
+
]);
|
|
27
|
+
var DeliveredEventPayloadSchema = z.object({
|
|
28
|
+
runId: z.string().min(1),
|
|
29
|
+
event: z.string().min(1),
|
|
30
|
+
id: z.string().min(1),
|
|
31
|
+
encoded: z.string()
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
class StepResultDecodeError extends Error {
|
|
35
|
+
constructor(message, options) {
|
|
36
|
+
super(message, options);
|
|
37
|
+
this.name = "StepResultDecodeError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class StepResultNotSerializableError extends Error {
|
|
42
|
+
constructor(stepName, cause) {
|
|
43
|
+
super(`Step "${stepName}" returned a value that is not JSON-serializable`, {
|
|
44
|
+
cause
|
|
45
|
+
});
|
|
46
|
+
this.name = "StepResultNotSerializableError";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class StepAbortedError extends Error {
|
|
51
|
+
constructor(stepName) {
|
|
52
|
+
super(`Step "${stepName}" was aborted before it started`);
|
|
53
|
+
this.name = "StepAbortedError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class ParkRecordDecodeError extends Error {
|
|
58
|
+
constructor(message, options) {
|
|
59
|
+
super(message, options);
|
|
60
|
+
this.name = "ParkRecordDecodeError";
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class ParkAbortedError extends Error {
|
|
65
|
+
constructor(park) {
|
|
66
|
+
super(`Park "${park}" was aborted before it was satisfied`);
|
|
67
|
+
this.name = "ParkAbortedError";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/agent-runtime/durability-json.ts
|
|
72
|
+
import { z as z2 } from "zod";
|
|
73
|
+
function losslessJson(value, ancestors = new Set, depth = 0) {
|
|
74
|
+
if (depth > 100)
|
|
75
|
+
return false;
|
|
76
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
77
|
+
return true;
|
|
78
|
+
if (typeof value === "number")
|
|
79
|
+
return Number.isFinite(value) && !Object.is(value, -0);
|
|
80
|
+
if (typeof value !== "object" || ancestors.has(value))
|
|
81
|
+
return false;
|
|
82
|
+
const array = Array.isArray(value);
|
|
83
|
+
const prototype = Object.getPrototypeOf(value);
|
|
84
|
+
if (array ? prototype !== Array.prototype : prototype !== Object.prototype && prototype !== null)
|
|
85
|
+
return false;
|
|
86
|
+
ancestors.add(value);
|
|
87
|
+
try {
|
|
88
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
89
|
+
const keys = Reflect.ownKeys(descriptors);
|
|
90
|
+
if (array && keys.length !== value.length + 1)
|
|
91
|
+
return false;
|
|
92
|
+
for (const key of keys) {
|
|
93
|
+
if (array && key === "length")
|
|
94
|
+
continue;
|
|
95
|
+
if (typeof key !== "string")
|
|
96
|
+
return false;
|
|
97
|
+
if (array && (!/^(0|[1-9][0-9]*)$/.test(key) || Number(key) >= value.length))
|
|
98
|
+
return false;
|
|
99
|
+
const descriptor = descriptors[key];
|
|
100
|
+
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
101
|
+
return false;
|
|
102
|
+
if (!losslessJson(descriptor.value, ancestors, depth + 1))
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
} finally {
|
|
107
|
+
ancestors.delete(value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var DurableJsonSchema = z2.custom((value) => losslessJson(value), "Expected lossless JSON data (finite numbers, plain objects and dense arrays; no accessors)");
|
|
111
|
+
|
|
112
|
+
// src/agent-runtime/durability-ledger.ts
|
|
113
|
+
function stepKey(conversationId, runId, stepName) {
|
|
114
|
+
return JSON.stringify([conversationId, runId, stepName]);
|
|
115
|
+
}
|
|
116
|
+
function sleepParkKey(conversationId, runId, name) {
|
|
117
|
+
return JSON.stringify([conversationId, runId, "sleep", name]);
|
|
118
|
+
}
|
|
119
|
+
function waitParkKey(conversationId, runId, event, id) {
|
|
120
|
+
return JSON.stringify([conversationId, runId, "wait", event, id]);
|
|
121
|
+
}
|
|
122
|
+
function parkEventKey(conversationId, payload) {
|
|
123
|
+
return payload.kind === "sleep" ? sleepParkKey(conversationId, payload.runId, payload.name) : waitParkKey(conversationId, payload.runId, payload.event, payload.id);
|
|
124
|
+
}
|
|
125
|
+
function encodeStepResult(stepName, value) {
|
|
126
|
+
let encoded;
|
|
127
|
+
try {
|
|
128
|
+
encoded = JSON.stringify(DurableJsonSchema.parse(value));
|
|
129
|
+
} catch (cause) {
|
|
130
|
+
throw new StepResultNotSerializableError(stepName, cause);
|
|
131
|
+
}
|
|
132
|
+
if (encoded === undefined) {
|
|
133
|
+
throw new StepResultNotSerializableError(stepName, value);
|
|
134
|
+
}
|
|
135
|
+
return encoded;
|
|
136
|
+
}
|
|
137
|
+
function encodeDeliveredPayload(event, id, payload) {
|
|
138
|
+
let encoded;
|
|
139
|
+
try {
|
|
140
|
+
encoded = JSON.stringify(DurableJsonSchema.parse(payload === undefined ? null : payload));
|
|
141
|
+
} catch (cause) {
|
|
142
|
+
throw new TypeError(`Delivered event "${event}#${id}" payload is not JSON-serializable`, {
|
|
143
|
+
cause
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (encoded === undefined) {
|
|
147
|
+
throw new TypeError(`Delivered event "${event}#${id}" payload is not JSON-serializable`);
|
|
148
|
+
}
|
|
149
|
+
return encoded;
|
|
150
|
+
}
|
|
151
|
+
function decodeStepPayload(event) {
|
|
152
|
+
const parsed = StepEventPayloadSchema.safeParse(event.payload);
|
|
153
|
+
if (!parsed.success) {
|
|
154
|
+
throw new StepResultDecodeError(`Recorded step event ${event.eventId} in conversation ${event.conversationId} is malformed`, { cause: parsed.error });
|
|
155
|
+
}
|
|
156
|
+
return parsed.data;
|
|
157
|
+
}
|
|
158
|
+
function decodeRecordedResult(payload, event) {
|
|
159
|
+
try {
|
|
160
|
+
return DurableJsonSchema.parse(JSON.parse(payload.encoded));
|
|
161
|
+
} catch (cause) {
|
|
162
|
+
throw new StepResultDecodeError(`Recorded result for step "${payload.stepName}" (run ${payload.runId}, ` + `event ${event.eventId}) is not decodable JSON`, { cause });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function decodeParkPayload(event) {
|
|
166
|
+
const parsed = ParkEventPayloadSchema.safeParse(event.payload);
|
|
167
|
+
if (!parsed.success) {
|
|
168
|
+
throw new ParkRecordDecodeError(`Recorded park event ${event.eventId} in conversation ${event.conversationId} is malformed`, { cause: parsed.error });
|
|
169
|
+
}
|
|
170
|
+
return parsed.data;
|
|
171
|
+
}
|
|
172
|
+
function decodeDeliveredPayload(event) {
|
|
173
|
+
const parsed = DeliveredEventPayloadSchema.safeParse(event.payload);
|
|
174
|
+
if (!parsed.success) {
|
|
175
|
+
throw new ParkRecordDecodeError(`Delivered event ${event.eventId} in conversation ${event.conversationId} is malformed`, { cause: parsed.error });
|
|
176
|
+
}
|
|
177
|
+
return parsed.data;
|
|
178
|
+
}
|
|
179
|
+
function decodeDeliveredResult(payload, event) {
|
|
180
|
+
try {
|
|
181
|
+
return JSON.parse(payload.encoded);
|
|
182
|
+
} catch (cause) {
|
|
183
|
+
throw new ParkRecordDecodeError(`Delivered payload for "${payload.event}#${payload.id}" (run ${payload.runId}, ` + `event ${event.eventId}) is not decodable JSON`, { cause });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function readDurabilityLedger(store, conversationId, view) {
|
|
187
|
+
const { steps, parks, deliveries } = view;
|
|
188
|
+
let fromSeq = view.nextSeq;
|
|
189
|
+
for (;; ) {
|
|
190
|
+
const page = await store.readEvents({
|
|
191
|
+
conversationId,
|
|
192
|
+
...fromSeq !== undefined && { fromSeq },
|
|
193
|
+
limit: 1e4
|
|
194
|
+
});
|
|
195
|
+
for (const event of page.items) {
|
|
196
|
+
if (event.kind === DURABILITY_STEP_EVENT_KIND) {
|
|
197
|
+
const payload = decodeStepPayload(event);
|
|
198
|
+
steps.set(stepKey(event.conversationId, payload.runId, payload.stepName), decodeRecordedResult(payload, event));
|
|
199
|
+
} else if (event.kind === DURABILITY_PARK_EVENT_KIND) {
|
|
200
|
+
const payload = decodeParkPayload(event);
|
|
201
|
+
parks.set(parkEventKey(event.conversationId, payload), payload);
|
|
202
|
+
} else if (event.kind === DURABILITY_EVENT_EVENT_KIND) {
|
|
203
|
+
const payload = decodeDeliveredPayload(event);
|
|
204
|
+
const key = waitParkKey(event.conversationId, payload.runId, payload.event, payload.id);
|
|
205
|
+
if (!deliveries.has(key))
|
|
206
|
+
deliveries.set(key, decodeDeliveredResult(payload, event));
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const last = page.items.at(-1);
|
|
210
|
+
if (last)
|
|
211
|
+
view.nextSeq = last.seq + 1;
|
|
212
|
+
if (page.nextSeq === undefined)
|
|
213
|
+
break;
|
|
214
|
+
fromSeq = page.nextSeq;
|
|
215
|
+
}
|
|
216
|
+
return view;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/agent-runtime/durability-scheduler.ts
|
|
220
|
+
var systemClock = {
|
|
221
|
+
now: () => Date.now(),
|
|
222
|
+
schedule(callback, delayMs) {
|
|
223
|
+
const timer = setTimeout(callback, Math.min(delayMs, 2147483647));
|
|
224
|
+
timer.unref();
|
|
225
|
+
return { cancel: () => clearTimeout(timer) };
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
var stores = new WeakMap;
|
|
229
|
+
function notifyWaiters(parkWaiters, key) {
|
|
230
|
+
const waiters = parkWaiters.get(key);
|
|
231
|
+
if (!waiters)
|
|
232
|
+
return;
|
|
233
|
+
for (const wake of [...waiters])
|
|
234
|
+
wake();
|
|
235
|
+
}
|
|
236
|
+
function waitForTimer(clock, delayMs, signal) {
|
|
237
|
+
return new Promise((resolve) => {
|
|
238
|
+
let timer;
|
|
239
|
+
let settled = false;
|
|
240
|
+
const finish = (outcome) => {
|
|
241
|
+
if (settled)
|
|
242
|
+
return;
|
|
243
|
+
settled = true;
|
|
244
|
+
timer?.cancel();
|
|
245
|
+
signal?.removeEventListener("abort", onAbort);
|
|
246
|
+
resolve(outcome);
|
|
247
|
+
};
|
|
248
|
+
const onAbort = () => finish("aborted");
|
|
249
|
+
timer = clock.schedule(() => finish("elapsed"), delayMs);
|
|
250
|
+
if (signal) {
|
|
251
|
+
if (signal.aborted)
|
|
252
|
+
onAbort();
|
|
253
|
+
else
|
|
254
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
async function waitForDeadline(clock, deadline, park, signal) {
|
|
259
|
+
for (;; ) {
|
|
260
|
+
if (signal?.aborted)
|
|
261
|
+
throw new ParkAbortedError(park);
|
|
262
|
+
const remaining = deadline - clock.now();
|
|
263
|
+
if (remaining <= 0)
|
|
264
|
+
return;
|
|
265
|
+
const outcome = await waitForTimer(clock, remaining, signal);
|
|
266
|
+
if (outcome === "aborted")
|
|
267
|
+
throw new ParkAbortedError(park);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
function createParkWaiter(parkWaiters, key, signal) {
|
|
271
|
+
const waiters = parkWaiters.get(key) ?? new Set;
|
|
272
|
+
parkWaiters.set(key, waiters);
|
|
273
|
+
let settle;
|
|
274
|
+
const outcome = new Promise((resolve) => {
|
|
275
|
+
settle = resolve;
|
|
276
|
+
});
|
|
277
|
+
let settled = false;
|
|
278
|
+
const cleanup = () => {
|
|
279
|
+
waiters.delete(onWake);
|
|
280
|
+
if (waiters.size === 0)
|
|
281
|
+
parkWaiters.delete(key);
|
|
282
|
+
signal?.removeEventListener("abort", onAbort);
|
|
283
|
+
};
|
|
284
|
+
const finish = (result) => {
|
|
285
|
+
if (settled)
|
|
286
|
+
return;
|
|
287
|
+
settled = true;
|
|
288
|
+
cleanup();
|
|
289
|
+
settle(result);
|
|
290
|
+
};
|
|
291
|
+
const onWake = () => finish("woke");
|
|
292
|
+
const onAbort = () => finish("aborted");
|
|
293
|
+
waiters.add(onWake);
|
|
294
|
+
if (signal) {
|
|
295
|
+
if (signal.aborted)
|
|
296
|
+
onAbort();
|
|
297
|
+
else
|
|
298
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
299
|
+
}
|
|
300
|
+
return { outcome, cancel: cleanup };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/agent-runtime/durability.ts
|
|
304
|
+
function releaseSettled(registry, key, pending) {
|
|
305
|
+
const release = () => {
|
|
306
|
+
if (registry.get(key) === pending)
|
|
307
|
+
registry.delete(key);
|
|
308
|
+
};
|
|
309
|
+
pending.then(release, release);
|
|
310
|
+
}
|
|
311
|
+
function detachedResult(pending) {
|
|
312
|
+
return pending.then((value) => JSON.parse(JSON.stringify(value)));
|
|
313
|
+
}
|
|
314
|
+
function createLocalStepDurability(options) {
|
|
315
|
+
const { store, conversationId, runId } = options;
|
|
316
|
+
const inFlight = stores.get(store) ?? {
|
|
317
|
+
steps: new Map,
|
|
318
|
+
parks: new Map,
|
|
319
|
+
waiters: new Map
|
|
320
|
+
};
|
|
321
|
+
stores.set(store, inFlight);
|
|
322
|
+
const { steps: inFlightSteps, parks: inFlightParks, waiters: parkWaiters } = inFlight;
|
|
323
|
+
const clock = options.clock ?? systemClock;
|
|
324
|
+
const view = {
|
|
325
|
+
steps: new Map,
|
|
326
|
+
parks: new Map,
|
|
327
|
+
deliveries: new Map,
|
|
328
|
+
nextSeq: 1
|
|
329
|
+
};
|
|
330
|
+
let reading = Promise.resolve(view);
|
|
331
|
+
const readLedger = () => {
|
|
332
|
+
reading = reading.catch(() => view).then(() => readDurabilityLedger(store, conversationId, view));
|
|
333
|
+
return reading;
|
|
334
|
+
};
|
|
335
|
+
const readRecordedStep = async (name) => {
|
|
336
|
+
const key = stepKey(conversationId, runId, name);
|
|
337
|
+
const records = await readLedger();
|
|
338
|
+
const value = records.steps.get(key);
|
|
339
|
+
return value === undefined ? undefined : JSON.parse(JSON.stringify(value));
|
|
340
|
+
};
|
|
341
|
+
const execute = async (name, body, runOptions) => {
|
|
342
|
+
const recorded = await readRecordedStep(name);
|
|
343
|
+
if (recorded !== undefined) {
|
|
344
|
+
return recorded;
|
|
345
|
+
}
|
|
346
|
+
if (runOptions?.signal?.aborted || options.signal?.aborted)
|
|
347
|
+
throw new StepAbortedError(name);
|
|
348
|
+
const value = await body();
|
|
349
|
+
const encoded = encodeStepResult(name, value);
|
|
350
|
+
await store.appendEvent({
|
|
351
|
+
conversationId,
|
|
352
|
+
kind: DURABILITY_STEP_EVENT_KIND,
|
|
353
|
+
payload: { runId, stepName: name, encoded }
|
|
354
|
+
});
|
|
355
|
+
return JSON.parse(encoded);
|
|
356
|
+
};
|
|
357
|
+
const step = (name, body, runOptions) => {
|
|
358
|
+
const key = stepKey(conversationId, runId, name);
|
|
359
|
+
const existing = inFlightSteps.get(key);
|
|
360
|
+
if (existing) {
|
|
361
|
+
return detachedResult(existing);
|
|
362
|
+
}
|
|
363
|
+
const pending = execute(name, body, runOptions);
|
|
364
|
+
inFlightSteps.set(key, pending);
|
|
365
|
+
releaseSettled(inFlightSteps, key, pending);
|
|
366
|
+
return detachedResult(pending);
|
|
367
|
+
};
|
|
368
|
+
const executeSleep = async (name, seconds, runOptions) => {
|
|
369
|
+
const key = sleepParkKey(conversationId, runId, name);
|
|
370
|
+
const signal = options.signal && runOptions?.signal ? AbortSignal.any([options.signal, runOptions.signal]) : runOptions?.signal ?? options.signal;
|
|
371
|
+
const ledger = await readLedger();
|
|
372
|
+
const existing = ledger.parks.get(key);
|
|
373
|
+
let deadline;
|
|
374
|
+
if (existing) {
|
|
375
|
+
if (existing.kind !== "sleep") {
|
|
376
|
+
throw new ParkRecordDecodeError(`Recorded park "${name}" is not a sleep record`);
|
|
377
|
+
}
|
|
378
|
+
deadline = existing.deadline;
|
|
379
|
+
} else {
|
|
380
|
+
deadline = clock.now() + seconds * 1000;
|
|
381
|
+
if (!Number.isSafeInteger(Math.ceil(deadline)))
|
|
382
|
+
throw new TypeError("Sleep deadline exceeds the supported clock range");
|
|
383
|
+
await store.appendEvent({
|
|
384
|
+
conversationId,
|
|
385
|
+
kind: DURABILITY_PARK_EVENT_KIND,
|
|
386
|
+
payload: { runId, kind: "sleep", name, deadline }
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
if (signal?.aborted)
|
|
390
|
+
throw new ParkAbortedError(name);
|
|
391
|
+
await waitForDeadline(clock, deadline, name, signal);
|
|
392
|
+
};
|
|
393
|
+
const sleep = (input, runOptions) => {
|
|
394
|
+
const { seconds } = input;
|
|
395
|
+
if (!Number.isFinite(seconds) || seconds < 0) {
|
|
396
|
+
throw new TypeError(`sleep seconds must be a non-negative finite number, got ${String(seconds)}`);
|
|
397
|
+
}
|
|
398
|
+
const name = input.name ?? `sleep:${seconds}`;
|
|
399
|
+
const key = sleepParkKey(conversationId, runId, name);
|
|
400
|
+
const existing = inFlightParks.get(key);
|
|
401
|
+
if (existing) {
|
|
402
|
+
return existing;
|
|
403
|
+
}
|
|
404
|
+
const pending = executeSleep(name, seconds, runOptions);
|
|
405
|
+
inFlightParks.set(key, pending);
|
|
406
|
+
releaseSettled(inFlightParks, key, pending);
|
|
407
|
+
return pending;
|
|
408
|
+
};
|
|
409
|
+
const executeWait = async (eventName, id, runOptions) => {
|
|
410
|
+
const key = waitParkKey(conversationId, runId, eventName, id);
|
|
411
|
+
const park = `${eventName}#${id}`;
|
|
412
|
+
const signal = options.signal && runOptions?.signal ? AbortSignal.any([options.signal, runOptions.signal]) : runOptions?.signal ?? options.signal;
|
|
413
|
+
for (;; ) {
|
|
414
|
+
if (signal?.aborted)
|
|
415
|
+
throw new ParkAbortedError(park);
|
|
416
|
+
const waiter = createParkWaiter(parkWaiters, key, signal);
|
|
417
|
+
let unsubscribe;
|
|
418
|
+
try {
|
|
419
|
+
unsubscribe = options.subscribe?.(() => notifyWaiters(parkWaiters, key));
|
|
420
|
+
const ledger = await readLedger();
|
|
421
|
+
if (ledger.deliveries.has(key)) {
|
|
422
|
+
return JSON.parse(JSON.stringify(ledger.deliveries.get(key)));
|
|
423
|
+
}
|
|
424
|
+
if (!ledger.parks.has(key)) {
|
|
425
|
+
await store.appendEvent({
|
|
426
|
+
conversationId,
|
|
427
|
+
kind: DURABILITY_PARK_EVENT_KIND,
|
|
428
|
+
payload: { runId, kind: "wait", event: eventName, id }
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const outcome = await waiter.outcome;
|
|
432
|
+
if (outcome === "aborted")
|
|
433
|
+
throw new ParkAbortedError(park);
|
|
434
|
+
} finally {
|
|
435
|
+
waiter.cancel();
|
|
436
|
+
unsubscribe?.();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
const waitFor = (input, runOptions) => {
|
|
441
|
+
const key = waitParkKey(conversationId, runId, input.event, input.id);
|
|
442
|
+
const existing = inFlightParks.get(key);
|
|
443
|
+
if (existing) {
|
|
444
|
+
return detachedResult(existing);
|
|
445
|
+
}
|
|
446
|
+
const pending = executeWait(input.event, input.id, runOptions);
|
|
447
|
+
inFlightParks.set(key, pending);
|
|
448
|
+
releaseSettled(inFlightParks, key, pending);
|
|
449
|
+
return detachedResult(pending);
|
|
450
|
+
};
|
|
451
|
+
const deliver = async (input) => {
|
|
452
|
+
const key = waitParkKey(conversationId, runId, input.event, input.id);
|
|
453
|
+
const ledger = await readLedger();
|
|
454
|
+
if (!ledger.deliveries.has(key)) {
|
|
455
|
+
const encoded = encodeDeliveredPayload(input.event, input.id, input.payload);
|
|
456
|
+
await store.appendEvent({
|
|
457
|
+
conversationId,
|
|
458
|
+
kind: DURABILITY_EVENT_EVENT_KIND,
|
|
459
|
+
payload: { runId, event: input.event, id: input.id, encoded }
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
notifyWaiters(parkWaiters, key);
|
|
463
|
+
};
|
|
464
|
+
return {
|
|
465
|
+
step,
|
|
466
|
+
readRecordedStep,
|
|
467
|
+
hasRecordedStep: async (name) => await readRecordedStep(name) !== undefined,
|
|
468
|
+
sleep,
|
|
469
|
+
waitFor,
|
|
470
|
+
deliver
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/tools/agent-tool-error.ts
|
|
475
|
+
var AGENT_TOOL_ERROR_BRAND = Symbol.for("stitchkit.AgentToolError");
|
|
476
|
+
|
|
477
|
+
class AgentToolError extends Error {
|
|
478
|
+
output;
|
|
479
|
+
constructor(output, cause) {
|
|
480
|
+
let message = "Tool execution failed";
|
|
481
|
+
try {
|
|
482
|
+
message = JSON.stringify(output);
|
|
483
|
+
} catch {}
|
|
484
|
+
super(message, cause === undefined ? undefined : { cause });
|
|
485
|
+
this.name = "AgentToolError";
|
|
486
|
+
this.output = output;
|
|
487
|
+
Object.defineProperty(this, AGENT_TOOL_ERROR_BRAND, { value: true });
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function isAgentToolError(value) {
|
|
491
|
+
return typeof value === "object" && value !== null && AGENT_TOOL_ERROR_BRAND in value;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/tools/durability-context.ts
|
|
495
|
+
var contexts = new WeakMap;
|
|
496
|
+
function createToolDurabilityContext(factory) {
|
|
497
|
+
const context = {};
|
|
498
|
+
contexts.set(context, factory);
|
|
499
|
+
return context;
|
|
500
|
+
}
|
|
501
|
+
function resolveToolDurability(context, callId, signal) {
|
|
502
|
+
if (typeof context !== "object" || context === null)
|
|
503
|
+
return;
|
|
504
|
+
return contexts.get(context)?.(callId, signal);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
export { DURABILITY_STEP_EVENT_KIND, StepResultDecodeError, StepResultNotSerializableError, StepAbortedError, ParkRecordDecodeError, ParkAbortedError, createLocalStepDurability, AgentToolError, isAgentToolError, createToolDurabilityContext, resolveToolDurability };
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
formatZodError,
|
|
7
7
|
normalizeError,
|
|
8
8
|
validateDeclaredOutput
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-13cjtmrw.js";
|
|
10
10
|
import {
|
|
11
11
|
coerceJsonArgs
|
|
12
12
|
} from "./index-7zbps32p.js";
|
|
@@ -16,8 +16,9 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
AppError,
|
|
18
18
|
STITCH_ERROR_STATUS,
|
|
19
|
+
isRetryableStatus,
|
|
19
20
|
isStitchErrorCode
|
|
20
|
-
} from "./index-
|
|
21
|
+
} from "./index-2vtcmrj9.js";
|
|
21
22
|
import {
|
|
22
23
|
isUnsafeKey
|
|
23
24
|
} from "./index-kzfs85xp.js";
|
|
@@ -34,6 +35,12 @@ class ToolExecutionControlError extends Error {
|
|
|
34
35
|
this.reason = reason;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
38
|
+
function isPlainObject(value) {
|
|
39
|
+
if (!isRecord(value))
|
|
40
|
+
return false;
|
|
41
|
+
const proto = Object.getPrototypeOf(value);
|
|
42
|
+
return proto === Object.prototype || proto === null;
|
|
43
|
+
}
|
|
37
44
|
function isToolExecutionControlError(value) {
|
|
38
45
|
return value instanceof ToolExecutionControlError;
|
|
39
46
|
}
|
|
@@ -44,7 +51,8 @@ function toolResultFromError(err) {
|
|
|
44
51
|
ok: false,
|
|
45
52
|
code: appErr.code,
|
|
46
53
|
details: appErr.details ?? { message: appErr.message },
|
|
47
|
-
...appErr.hint && { hint: appErr.hint }
|
|
54
|
+
...appErr.hint && { hint: appErr.hint },
|
|
55
|
+
retryable: appErr.retryable ?? isRetryableStatus(appErr.status)
|
|
48
56
|
};
|
|
49
57
|
normalizedToolErrors.set(result, { normalized: appErr, cause: err });
|
|
50
58
|
return result;
|
|
@@ -56,7 +64,7 @@ function toolErrorFromResult(result) {
|
|
|
56
64
|
const details = isRecord(result.details) ? result.details : undefined;
|
|
57
65
|
const message = typeof details?.message === "string" ? details.message : result.code;
|
|
58
66
|
const status = isStitchErrorCode(result.code) ? STITCH_ERROR_STATUS[result.code] : 500;
|
|
59
|
-
return new AppError(result.code, message, status, details, result.hint);
|
|
67
|
+
return new AppError(result.code, message, status, details, result.hint, undefined, result.retryable);
|
|
60
68
|
}
|
|
61
69
|
function toolCauseFromResult(result) {
|
|
62
70
|
return normalizedToolErrors.get(result)?.cause;
|
|
@@ -92,7 +100,8 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
92
100
|
durationMs: Date.now() - startedAt,
|
|
93
101
|
context: hookContext,
|
|
94
102
|
endpoint: method,
|
|
95
|
-
...thrown !== undefined && { error: thrown }
|
|
103
|
+
...thrown !== undefined && { error: thrown },
|
|
104
|
+
...rewrittenArgsApplied && rewrittenArgs !== undefined && { effectiveArgs: rewrittenArgs }
|
|
96
105
|
});
|
|
97
106
|
} catch (hookError) {
|
|
98
107
|
try {
|
|
@@ -102,26 +111,32 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
102
111
|
return result;
|
|
103
112
|
};
|
|
104
113
|
let beforeRan = false;
|
|
114
|
+
let rewrittenArgs;
|
|
115
|
+
let rewrittenArgsApplied = false;
|
|
105
116
|
const runBefore = async () => {
|
|
106
117
|
if (beforeRan || !hooks?.beforeToolCall)
|
|
107
118
|
return null;
|
|
108
119
|
beforeRan = true;
|
|
120
|
+
let returned;
|
|
109
121
|
try {
|
|
110
|
-
await hooks.beforeToolCall({
|
|
122
|
+
returned = await hooks.beforeToolCall({
|
|
111
123
|
toolName,
|
|
112
124
|
args: rawArgs,
|
|
113
125
|
context: hookContext,
|
|
114
126
|
endpoint: method
|
|
115
127
|
});
|
|
116
|
-
return null;
|
|
117
128
|
} catch (err) {
|
|
118
|
-
return finish(toolResultFromError(err));
|
|
129
|
+
return { kind: "failure", result: await finish(toolResultFromError(err)) };
|
|
119
130
|
}
|
|
131
|
+
if (!isPlainObject(returned))
|
|
132
|
+
return null;
|
|
133
|
+
rewrittenArgs = returned;
|
|
134
|
+
return { kind: "args" };
|
|
120
135
|
};
|
|
121
136
|
const finishThrown = async (err) => {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
124
|
-
return
|
|
137
|
+
const before2 = await runBefore();
|
|
138
|
+
if (before2?.kind === "failure")
|
|
139
|
+
return before2.result;
|
|
125
140
|
if (hooks?.onToolError) {
|
|
126
141
|
try {
|
|
127
142
|
await hooks.onToolError({
|
|
@@ -140,9 +155,11 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
140
155
|
};
|
|
141
156
|
let callArgs = rawArgs;
|
|
142
157
|
let callContext = context;
|
|
158
|
+
let extensionKeys;
|
|
143
159
|
if (extension) {
|
|
144
|
-
const
|
|
145
|
-
|
|
160
|
+
const keys = new Set(Object.keys(extension.schema.shape));
|
|
161
|
+
extensionKeys = keys;
|
|
162
|
+
const extensionArgs = Object.fromEntries(Object.entries(rawArgs).filter(([key]) => keys.has(key)));
|
|
146
163
|
let parsed;
|
|
147
164
|
try {
|
|
148
165
|
parsed = extension.schema.safeParse(extensionArgs);
|
|
@@ -150,9 +167,9 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
150
167
|
return finishThrown(err);
|
|
151
168
|
}
|
|
152
169
|
if (!parsed.success) {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
return
|
|
170
|
+
const before2 = await runBefore();
|
|
171
|
+
if (before2?.kind === "failure")
|
|
172
|
+
return before2.result;
|
|
156
173
|
return finish({
|
|
157
174
|
ok: false,
|
|
158
175
|
code: "VALIDATION_ERROR",
|
|
@@ -165,17 +182,22 @@ async function runToolMethod(method, toolName, rawArgs, context, hooks, lifecycl
|
|
|
165
182
|
...context,
|
|
166
183
|
...resolved,
|
|
167
184
|
source: context.source,
|
|
168
|
-
...context.mcp !== undefined && { mcp: context.mcp }
|
|
185
|
+
...context.mcp !== undefined && { mcp: context.mcp },
|
|
186
|
+
...context.toolCallId !== undefined && { toolCallId: context.toolCallId }
|
|
169
187
|
};
|
|
170
188
|
hookContext = callContext;
|
|
171
|
-
callArgs = Object.fromEntries(Object.entries(rawArgs).filter(([key]) => !
|
|
189
|
+
callArgs = Object.fromEntries(Object.entries(rawArgs).filter(([key]) => !keys.has(key)));
|
|
172
190
|
} catch (err) {
|
|
173
191
|
return finishThrown(err);
|
|
174
192
|
}
|
|
175
193
|
}
|
|
176
|
-
const
|
|
177
|
-
if (
|
|
178
|
-
return
|
|
194
|
+
const before = await runBefore();
|
|
195
|
+
if (before?.kind === "failure")
|
|
196
|
+
return before.result;
|
|
197
|
+
if (before?.kind === "args" && rewrittenArgs) {
|
|
198
|
+
callArgs = extensionKeys ? Object.fromEntries(Object.entries(rewrittenArgs).filter(([key]) => !extensionKeys.has(key))) : rewrittenArgs;
|
|
199
|
+
rewrittenArgsApplied = true;
|
|
200
|
+
}
|
|
179
201
|
const paramKeys = new Set(objectShapeKeys(method.paramsSchema));
|
|
180
202
|
let paramArgs = {};
|
|
181
203
|
let inputArgs = {};
|
|
@@ -17,14 +17,14 @@ import {
|
|
|
17
17
|
missingSandboxRestrictions,
|
|
18
18
|
probeAgentProcessSandbox,
|
|
19
19
|
refuseMissingCodingPath
|
|
20
|
-
} from "./index-
|
|
20
|
+
} from "./index-qmy6zq75.js";
|
|
21
21
|
import {
|
|
22
22
|
defineRuntimeTool
|
|
23
23
|
} from "./index-8z9we758.js";
|
|
24
24
|
import {
|
|
25
25
|
conflict,
|
|
26
26
|
forbidden
|
|
27
|
-
} from "./index-
|
|
27
|
+
} from "./index-2vtcmrj9.js";
|
|
28
28
|
|
|
29
29
|
// src/agent-runtime/coding-tool-contract.ts
|
|
30
30
|
import { z } from "zod";
|