stitchkit 0.68.4 → 0.68.5
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/README.md +4 -1
- package/dist/agent-runtime/event-schema.d.ts +1341 -0
- package/dist/agent-runtime/event-schema.d.ts.map +1 -0
- package/dist/agent-runtime/events.d.ts +2 -1347
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/run-execution.d.ts +2 -0
- package/dist/agent-runtime/run-execution.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +2 -0
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +4 -6
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime-browser.d.ts +4 -0
- package/dist/agent-runtime-browser.d.ts.map +1 -0
- package/dist/agent-runtime-browser.js +420 -0
- package/dist/agent-runtime.js +134 -87
- package/dist/browser/cancellation.d.ts.map +1 -1
- package/dist/{index-thcy3w8c.js → index-n34x0q5e.js} +6 -6
- package/dist/index.js +6 -6
- package/dist/remote.js +1 -1
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.js +27 -5
- package/llms-full.txt +48 -5
- package/package.json +6 -2
package/dist/agent-runtime.js
CHANGED
|
@@ -305,7 +305,7 @@ function createAgentSessionCoordinator() {
|
|
|
305
305
|
}
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
|
-
// src/agent-runtime/
|
|
308
|
+
// src/agent-runtime/event-schema.ts
|
|
309
309
|
import { z } from "zod";
|
|
310
310
|
var AgentAdmissionEventSchema = z.object({
|
|
311
311
|
type: z.literal("admission"),
|
|
@@ -422,6 +422,11 @@ function advanceAgentRuntimeEventCursor(rawCursor, event) {
|
|
|
422
422
|
cursor: { ...cursor, runtimeEpoch: event.runtimeEpoch, sequence: event.sequence }
|
|
423
423
|
};
|
|
424
424
|
}
|
|
425
|
+
function agentDurableEventId(type, runId, snapshotVersion) {
|
|
426
|
+
return `${runId}:${type}:${snapshotVersion}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// src/agent-runtime/events.ts
|
|
425
430
|
function createAgentRuntimeEventSink(config) {
|
|
426
431
|
const manager = createBoundedSinkManager({
|
|
427
432
|
write: config.write,
|
|
@@ -441,9 +446,6 @@ function createAgentRuntimeEventSink(config) {
|
|
|
441
446
|
close: () => manager.close()
|
|
442
447
|
};
|
|
443
448
|
}
|
|
444
|
-
function agentDurableEventId(type, runId, snapshotVersion) {
|
|
445
|
-
return `${runId}:${type}:${snapshotVersion}`;
|
|
446
|
-
}
|
|
447
449
|
// src/agent-runtime/history.ts
|
|
448
450
|
import { modelMessageSchema } from "ai";
|
|
449
451
|
function providerOptions(envelope) {
|
|
@@ -1493,6 +1495,7 @@ function createRunExecutor(dependencies) {
|
|
|
1493
1495
|
if (!absorbing?.assistant || !absorbing.run.terminalReason) {
|
|
1494
1496
|
throw new AgentRuntimeConflictError("absorbed run resolution");
|
|
1495
1497
|
}
|
|
1498
|
+
input.onAcquired?.();
|
|
1496
1499
|
return {
|
|
1497
1500
|
run: absorbing.run,
|
|
1498
1501
|
message: absorbing.assistant,
|
|
@@ -1511,6 +1514,7 @@ function createRunExecutor(dependencies) {
|
|
|
1511
1514
|
ownerId: runtimeEpoch
|
|
1512
1515
|
}), "run acquisition");
|
|
1513
1516
|
let run = findRun(acquired.runs, input.acceptedRun.id);
|
|
1517
|
+
input.onAcquired?.();
|
|
1514
1518
|
await publish({
|
|
1515
1519
|
type: "run-state",
|
|
1516
1520
|
eventId: agentDurableEventId("run-state", run.id, acquired.version),
|
|
@@ -2281,6 +2285,15 @@ function createAgentRuntime(config) {
|
|
|
2281
2285
|
const context = config.protocol.parseContext(rawInput.context);
|
|
2282
2286
|
const accepted = Promise.withResolvers();
|
|
2283
2287
|
const result = Promise.withResolvers();
|
|
2288
|
+
let acquisitionSettled = false;
|
|
2289
|
+
const resolveAcquisition = () => {
|
|
2290
|
+
acquisitionSettled = true;
|
|
2291
|
+
accepted.resolve();
|
|
2292
|
+
};
|
|
2293
|
+
const rejectAcquisition = (error) => {
|
|
2294
|
+
if (!acquisitionSettled)
|
|
2295
|
+
accepted.reject(error);
|
|
2296
|
+
};
|
|
2284
2297
|
const handedOff = beginAdmission();
|
|
2285
2298
|
(async () => {
|
|
2286
2299
|
try {
|
|
@@ -2294,7 +2307,6 @@ function createAgentRuntime(config) {
|
|
|
2294
2307
|
if (recoveredRun.state !== "queued") {
|
|
2295
2308
|
throw new Error("Only a queued recovered agent run can be resumed");
|
|
2296
2309
|
}
|
|
2297
|
-
accepted.resolve();
|
|
2298
2310
|
const ticket = coordinator.submit({
|
|
2299
2311
|
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
2300
2312
|
policy: "queue",
|
|
@@ -2304,16 +2316,20 @@ function createAgentRuntime(config) {
|
|
|
2304
2316
|
acceptedRun: recoveredRun,
|
|
2305
2317
|
context,
|
|
2306
2318
|
signal,
|
|
2307
|
-
key: rawInput.conversationKey ?? rawInput.conversationId
|
|
2319
|
+
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
2320
|
+
onAcquired: resolveAcquisition
|
|
2308
2321
|
})
|
|
2309
2322
|
})
|
|
2310
2323
|
});
|
|
2311
2324
|
ticket.accepted.catch(() => {
|
|
2312
2325
|
return;
|
|
2313
2326
|
});
|
|
2314
|
-
ticket.result.then(result.resolve,
|
|
2327
|
+
ticket.result.then(result.resolve, (error) => {
|
|
2328
|
+
rejectAcquisition(error);
|
|
2329
|
+
result.reject(error);
|
|
2330
|
+
});
|
|
2315
2331
|
} catch (error) {
|
|
2316
|
-
|
|
2332
|
+
rejectAcquisition(error);
|
|
2317
2333
|
result.reject(error);
|
|
2318
2334
|
} finally {
|
|
2319
2335
|
handedOff();
|
|
@@ -2599,101 +2615,129 @@ function createAgentRuntime(config) {
|
|
|
2599
2615
|
if (!Number.isSafeInteger(maxRuns) || maxRuns < 1) {
|
|
2600
2616
|
throw new TypeError("Recovery maxRuns must be a positive safe integer");
|
|
2601
2617
|
}
|
|
2602
|
-
const
|
|
2618
|
+
const recoverable = [];
|
|
2603
2619
|
let cursor;
|
|
2604
|
-
while (
|
|
2620
|
+
while (recoverable.length < maxRuns && !options.signal?.aborted && !admissionClosed) {
|
|
2605
2621
|
const page = await config.store.scanRecoverable({
|
|
2606
2622
|
...cursor && { cursor },
|
|
2607
|
-
limit: Math.min(pageSize, maxRuns -
|
|
2623
|
+
limit: Math.min(pageSize, maxRuns - recoverable.length)
|
|
2608
2624
|
});
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2625
|
+
recoverable.push(...page.items);
|
|
2626
|
+
cursor = page.nextCursor;
|
|
2627
|
+
if (!cursor || page.items.length === 0)
|
|
2628
|
+
break;
|
|
2629
|
+
}
|
|
2630
|
+
const grouped = new Map;
|
|
2631
|
+
for (const item of recoverable) {
|
|
2632
|
+
const items = grouped.get(item.conversationId) ?? [];
|
|
2633
|
+
items.push(item);
|
|
2634
|
+
grouped.set(item.conversationId, items);
|
|
2635
|
+
}
|
|
2636
|
+
const ordered = [];
|
|
2637
|
+
for (const [conversationId, items] of grouped) {
|
|
2638
|
+
const active = await config.store.listActiveRuns(conversationId);
|
|
2639
|
+
const position = new Map(active.map((run, index) => [run.id, index]));
|
|
2640
|
+
ordered.push(...items.sort((left, right) => {
|
|
2641
|
+
const leftPosition = position.get(left.run.id);
|
|
2642
|
+
const rightPosition = position.get(right.run.id);
|
|
2643
|
+
if (leftPosition === undefined && rightPosition === undefined)
|
|
2644
|
+
return 0;
|
|
2645
|
+
if (leftPosition === undefined)
|
|
2646
|
+
return 1;
|
|
2647
|
+
if (rightPosition === undefined)
|
|
2648
|
+
return -1;
|
|
2649
|
+
return leftPosition - rightPosition;
|
|
2650
|
+
}));
|
|
2651
|
+
}
|
|
2652
|
+
const outcomes = [];
|
|
2653
|
+
const scheduledRuns = new Map;
|
|
2654
|
+
for (const item of ordered) {
|
|
2655
|
+
if (options.signal?.aborted)
|
|
2656
|
+
break;
|
|
2657
|
+
if (admissionClosed)
|
|
2658
|
+
break;
|
|
2659
|
+
const releaseAdmission = beginAdmission();
|
|
2660
|
+
try {
|
|
2661
|
+
const active = await config.store.listActiveRuns(item.conversationId);
|
|
2662
|
+
const position = active.findIndex((run) => run.id === item.run.id);
|
|
2663
|
+
const scheduled = scheduledRuns.get(item.conversationId) ?? new Set;
|
|
2664
|
+
const blockedByUnscheduledPredecessor = position > 0 && active.slice(0, position).some((run) => !scheduled.has(run.id));
|
|
2665
|
+
if (blockedByUnscheduledPredecessor) {
|
|
2666
|
+
outcomes.push({
|
|
2667
|
+
conversationId: item.conversationId,
|
|
2668
|
+
runId: item.run.id,
|
|
2669
|
+
outcome: "skipped"
|
|
2670
|
+
});
|
|
2671
|
+
continue;
|
|
2672
|
+
}
|
|
2673
|
+
const decision = await options.decide?.(item) ?? (item.run.state === "queued" ? { action: "resume" } : { action: "skip" });
|
|
2612
2674
|
if (admissionClosed)
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
if (item.run.state === "queued") {
|
|
2617
|
-
const active = await config.store.listActiveRuns(item.conversationId);
|
|
2618
|
-
const blockedByAcquiredPredecessor = active.some((run) => run.id !== item.run.id && (run.state === "running" || run.state === "interrupt_requested"));
|
|
2619
|
-
if (blockedByAcquiredPredecessor) {
|
|
2620
|
-
outcomes.push({
|
|
2621
|
-
conversationId: item.conversationId,
|
|
2622
|
-
runId: item.run.id,
|
|
2623
|
-
outcome: "skipped"
|
|
2624
|
-
});
|
|
2625
|
-
continue;
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
const decision = await options.decide?.(item) ?? (item.run.state === "queued" ? { action: "resume" } : { action: "skip" });
|
|
2629
|
-
if (admissionClosed)
|
|
2630
|
-
throw closedError();
|
|
2631
|
-
if (decision.action === "skip") {
|
|
2632
|
-
outcomes.push({
|
|
2633
|
-
conversationId: item.conversationId,
|
|
2634
|
-
runId: item.run.id,
|
|
2635
|
-
outcome: "skipped"
|
|
2636
|
-
});
|
|
2637
|
-
continue;
|
|
2638
|
-
}
|
|
2639
|
-
if (decision.action === "abandon") {
|
|
2640
|
-
const abandoned = await config.store.recoverRun({
|
|
2641
|
-
conversationId: item.conversationId,
|
|
2642
|
-
runId: item.run.id,
|
|
2643
|
-
expectedRevision: item.run.revision,
|
|
2644
|
-
action: "abandon"
|
|
2645
|
-
});
|
|
2646
|
-
if (abandoned.outcome !== "applied") {
|
|
2647
|
-
throw new AgentRuntimeConflictError("recovery abandon");
|
|
2648
|
-
}
|
|
2649
|
-
outcomes.push({
|
|
2650
|
-
conversationId: item.conversationId,
|
|
2651
|
-
runId: item.run.id,
|
|
2652
|
-
outcome: "abandoned"
|
|
2653
|
-
});
|
|
2654
|
-
continue;
|
|
2655
|
-
}
|
|
2656
|
-
if (decision.action === "requeue") {
|
|
2657
|
-
const requeued = await config.store.recoverRun({
|
|
2658
|
-
conversationId: item.conversationId,
|
|
2659
|
-
runId: item.run.id,
|
|
2660
|
-
expectedRevision: item.run.revision,
|
|
2661
|
-
action: "requeue",
|
|
2662
|
-
replaySafe: true
|
|
2663
|
-
});
|
|
2664
|
-
if (requeued.outcome !== "applied") {
|
|
2665
|
-
throw new AgentRuntimeConflictError("recovery requeue");
|
|
2666
|
-
}
|
|
2667
|
-
}
|
|
2668
|
-
const context = await options.resolveContext(item);
|
|
2669
|
-
const resumed = resume({
|
|
2675
|
+
throw closedError();
|
|
2676
|
+
if (decision.action === "skip") {
|
|
2677
|
+
outcomes.push({
|
|
2670
2678
|
conversationId: item.conversationId,
|
|
2671
2679
|
runId: item.run.id,
|
|
2672
|
-
|
|
2680
|
+
outcome: "skipped"
|
|
2673
2681
|
});
|
|
2674
|
-
|
|
2675
|
-
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
if (decision.action === "abandon") {
|
|
2685
|
+
const abandoned = await config.store.recoverRun({
|
|
2686
|
+
conversationId: item.conversationId,
|
|
2687
|
+
runId: item.run.id,
|
|
2688
|
+
expectedRevision: item.run.revision,
|
|
2689
|
+
action: "abandon"
|
|
2676
2690
|
});
|
|
2677
|
-
|
|
2691
|
+
if (abandoned.outcome !== "applied") {
|
|
2692
|
+
throw new AgentRuntimeConflictError("recovery abandon");
|
|
2693
|
+
}
|
|
2678
2694
|
outcomes.push({
|
|
2679
2695
|
conversationId: item.conversationId,
|
|
2680
2696
|
runId: item.run.id,
|
|
2681
|
-
outcome:
|
|
2697
|
+
outcome: "abandoned"
|
|
2682
2698
|
});
|
|
2683
|
-
|
|
2684
|
-
|
|
2699
|
+
continue;
|
|
2700
|
+
}
|
|
2701
|
+
if (decision.action === "requeue") {
|
|
2702
|
+
const requeued = await config.store.recoverRun({
|
|
2685
2703
|
conversationId: item.conversationId,
|
|
2686
2704
|
runId: item.run.id,
|
|
2687
|
-
|
|
2688
|
-
|
|
2705
|
+
expectedRevision: item.run.revision,
|
|
2706
|
+
action: "requeue",
|
|
2707
|
+
replaySafe: true
|
|
2689
2708
|
});
|
|
2690
|
-
|
|
2691
|
-
|
|
2709
|
+
if (requeued.outcome !== "applied") {
|
|
2710
|
+
throw new AgentRuntimeConflictError("recovery requeue");
|
|
2711
|
+
}
|
|
2692
2712
|
}
|
|
2713
|
+
const context = await options.resolveContext(item);
|
|
2714
|
+
const resumed = resume({
|
|
2715
|
+
conversationId: item.conversationId,
|
|
2716
|
+
runId: item.run.id,
|
|
2717
|
+
context
|
|
2718
|
+
});
|
|
2719
|
+
resumed.result.catch(() => {
|
|
2720
|
+
return;
|
|
2721
|
+
});
|
|
2722
|
+
await resumed.accepted;
|
|
2723
|
+
scheduled.add(item.run.id);
|
|
2724
|
+
scheduledRuns.set(item.conversationId, scheduled);
|
|
2725
|
+
outcomes.push({
|
|
2726
|
+
conversationId: item.conversationId,
|
|
2727
|
+
runId: item.run.id,
|
|
2728
|
+
outcome: decision.action === "requeue" ? "requeued" : "resumed",
|
|
2729
|
+
result: resumed.result
|
|
2730
|
+
});
|
|
2731
|
+
} catch (error) {
|
|
2732
|
+
outcomes.push({
|
|
2733
|
+
conversationId: item.conversationId,
|
|
2734
|
+
runId: item.run.id,
|
|
2735
|
+
outcome: "failed",
|
|
2736
|
+
error
|
|
2737
|
+
});
|
|
2738
|
+
} finally {
|
|
2739
|
+
releaseAdmission();
|
|
2693
2740
|
}
|
|
2694
|
-
cursor = page.nextCursor;
|
|
2695
|
-
if (!cursor || page.items.length === 0)
|
|
2696
|
-
break;
|
|
2697
2741
|
}
|
|
2698
2742
|
return outcomes;
|
|
2699
2743
|
},
|
|
@@ -3276,7 +3320,10 @@ function createAgentRuntimeStore(driver) {
|
|
|
3276
3320
|
});
|
|
3277
3321
|
});
|
|
3278
3322
|
const listActiveRuns = (conversationId) => driver.transaction(async (transaction) => {
|
|
3279
|
-
const records = await
|
|
3323
|
+
const [records, messages] = await Promise.all([
|
|
3324
|
+
driver.runs.listActive(transaction, conversationId),
|
|
3325
|
+
driver.history.load(transaction, conversationId)
|
|
3326
|
+
]);
|
|
3280
3327
|
const runs = records.map((record) => AgentStoredRunSchema.parse(record).run);
|
|
3281
3328
|
for (const run of runs) {
|
|
3282
3329
|
if (run.conversationId !== conversationId) {
|
|
@@ -3286,7 +3333,7 @@ function createAgentRuntimeStore(driver) {
|
|
|
3286
3333
|
throw new TypeError("Active run listing returned a terminal run");
|
|
3287
3334
|
}
|
|
3288
3335
|
}
|
|
3289
|
-
return
|
|
3336
|
+
return orderRuns(messages, runs);
|
|
3290
3337
|
});
|
|
3291
3338
|
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
3292
3339
|
const conversationId = operationConversationId(operation);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../../src/browser/cancellation.ts"],"names":[],"mappings":"AAAA,KAAK,iBAAiB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9C,4EAA4E;AAC5E,qBAAa,wBAAyB,SAAQ,KAAK;aACrB,KAAK,EAAE,iBAAiB;IAApD,YAA4B,KAAK,EAAE,iBAAiB,EAGnD;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;AAED,gFAAgF;AAChF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,mBAAmB,
|
|
1
|
+
{"version":3,"file":"cancellation.d.ts","sourceRoot":"","sources":["../../src/browser/cancellation.ts"],"names":[],"mappings":"AAAA,KAAK,iBAAiB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9C,4EAA4E;AAC5E,qBAAa,wBAAyB,SAAQ,KAAK;aACrB,KAAK,EAAE,iBAAiB;IAApD,YAA4B,KAAK,EAAE,iBAAiB,EAGnD;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,GAAG,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrE;AAED,gFAAgF;AAChF,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,GAC5B,mBAAmB,CAwCrB"}
|
|
@@ -28,34 +28,34 @@ function createRequestCancellation(caller, timeoutMs) {
|
|
|
28
28
|
if (!caller && timeoutMs === undefined) {
|
|
29
29
|
return { signal: undefined, run: (operation) => operation() };
|
|
30
30
|
}
|
|
31
|
-
const
|
|
31
|
+
const timeoutController = timeoutMs === undefined ? undefined : new AbortController;
|
|
32
|
+
const signal = caller && timeoutController ? AbortSignal.any([caller, timeoutController.signal]) : caller ?? timeoutController?.signal;
|
|
32
33
|
let cause;
|
|
33
34
|
let timer;
|
|
34
35
|
const abortFromCaller = () => {
|
|
35
36
|
if (cause)
|
|
36
37
|
return;
|
|
37
38
|
cause = "caller";
|
|
38
|
-
controller.abort(caller?.reason);
|
|
39
39
|
};
|
|
40
40
|
if (caller?.aborted)
|
|
41
41
|
abortFromCaller();
|
|
42
42
|
else
|
|
43
43
|
caller?.addEventListener("abort", abortFromCaller, { once: true });
|
|
44
|
-
if (timeoutMs !== undefined) {
|
|
44
|
+
if (timeoutMs !== undefined && timeoutController) {
|
|
45
45
|
timer = setTimeout(() => {
|
|
46
46
|
if (cause)
|
|
47
47
|
return;
|
|
48
48
|
cause = "timeout";
|
|
49
|
-
|
|
49
|
+
timeoutController.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
50
50
|
}, timeoutMs);
|
|
51
51
|
}
|
|
52
52
|
return {
|
|
53
|
-
signal
|
|
53
|
+
signal,
|
|
54
54
|
async run(operation) {
|
|
55
55
|
try {
|
|
56
56
|
if (cause === "caller")
|
|
57
57
|
throw cancellationError(cause);
|
|
58
|
-
return await operation(
|
|
58
|
+
return await operation(signal);
|
|
59
59
|
} catch (error) {
|
|
60
60
|
if (cause)
|
|
61
61
|
throw cancellationError(cause);
|
package/dist/index.js
CHANGED
|
@@ -42,34 +42,34 @@ function createRequestCancellation(caller, timeoutMs) {
|
|
|
42
42
|
if (!caller && timeoutMs === undefined) {
|
|
43
43
|
return { signal: undefined, run: (operation) => operation() };
|
|
44
44
|
}
|
|
45
|
-
const
|
|
45
|
+
const timeoutController = timeoutMs === undefined ? undefined : new AbortController;
|
|
46
|
+
const signal = caller && timeoutController ? AbortSignal.any([caller, timeoutController.signal]) : caller ?? timeoutController?.signal;
|
|
46
47
|
let cause;
|
|
47
48
|
let timer;
|
|
48
49
|
const abortFromCaller = () => {
|
|
49
50
|
if (cause)
|
|
50
51
|
return;
|
|
51
52
|
cause = "caller";
|
|
52
|
-
controller.abort(caller?.reason);
|
|
53
53
|
};
|
|
54
54
|
if (caller?.aborted)
|
|
55
55
|
abortFromCaller();
|
|
56
56
|
else
|
|
57
57
|
caller?.addEventListener("abort", abortFromCaller, { once: true });
|
|
58
|
-
if (timeoutMs !== undefined) {
|
|
58
|
+
if (timeoutMs !== undefined && timeoutController) {
|
|
59
59
|
timer = setTimeout(() => {
|
|
60
60
|
if (cause)
|
|
61
61
|
return;
|
|
62
62
|
cause = "timeout";
|
|
63
|
-
|
|
63
|
+
timeoutController.abort(new DOMException("Request timed out", "TimeoutError"));
|
|
64
64
|
}, timeoutMs);
|
|
65
65
|
}
|
|
66
66
|
return {
|
|
67
|
-
signal
|
|
67
|
+
signal,
|
|
68
68
|
async run(operation) {
|
|
69
69
|
try {
|
|
70
70
|
if (cause === "caller")
|
|
71
71
|
throw cancellationError(cause);
|
|
72
|
-
return await operation(
|
|
72
|
+
return await operation(signal);
|
|
73
73
|
} catch (error) {
|
|
74
74
|
if (cause)
|
|
75
75
|
throw cancellationError(cause);
|
package/dist/remote.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,WAAW,CACT,OAAO,EAAE,4BAA4B,GACpC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"agent-store-conformance.d.ts","sourceRoot":"","sources":["../../src/testing/agent-store-conformance.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,WAAW,CACT,OAAO,EAAE,4BAA4B,GACpC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAClD;;;;;;;;OAQG;IACH,OAAO,CAAC,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAwCD,gFAAgF;AAChF,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,2BAA2B,GAClC,OAAO,CAAC,IAAI,CAAC,CAiCf"}
|
package/dist/testing.js
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
import {
|
|
26
26
|
createClient,
|
|
27
27
|
createClients
|
|
28
|
-
} from "./index-
|
|
28
|
+
} from "./index-n34x0q5e.js";
|
|
29
29
|
import"./index-r5s4wqb5.js";
|
|
30
30
|
import {
|
|
31
31
|
joinRoutePath
|
|
@@ -145,7 +145,13 @@ function requireOutcome(actual, expected) {
|
|
|
145
145
|
async function runAgentStoreConformance(config) {
|
|
146
146
|
const run = `conformance-${crypto.randomUUID()}`;
|
|
147
147
|
const context = {
|
|
148
|
-
conversationIds: [
|
|
148
|
+
conversationIds: [
|
|
149
|
+
run,
|
|
150
|
+
`${run}-recovery`,
|
|
151
|
+
`${run}-absorb`,
|
|
152
|
+
`${run}-causal-history`,
|
|
153
|
+
`${run}-causal-active`
|
|
154
|
+
]
|
|
149
155
|
};
|
|
150
156
|
const store = await config.createStore(context);
|
|
151
157
|
let failure;
|
|
@@ -169,10 +175,11 @@ async function conformanceScenario(store, conversationIds) {
|
|
|
169
175
|
conversationId,
|
|
170
176
|
recoveryConversationId,
|
|
171
177
|
absorbConversationId,
|
|
172
|
-
causalHistoryConversationId
|
|
178
|
+
causalHistoryConversationId,
|
|
179
|
+
causalActiveConversationId
|
|
173
180
|
] = conversationIds;
|
|
174
|
-
if (!conversationId || !recoveryConversationId || !absorbConversationId || !causalHistoryConversationId) {
|
|
175
|
-
throw new Error("Agent store conformance requires
|
|
181
|
+
if (!conversationId || !recoveryConversationId || !absorbConversationId || !causalHistoryConversationId || !causalActiveConversationId) {
|
|
182
|
+
throw new Error("Agent store conformance requires five conversation identities");
|
|
176
183
|
}
|
|
177
184
|
const absentConversationId = `${conversationId}-absent`;
|
|
178
185
|
const firstInput = userMessage(conversationId, "input-1");
|
|
@@ -554,8 +561,23 @@ async function conformanceScenario(store, conversationIds) {
|
|
|
554
561
|
throw new Error("Abandon recovery did not atomically terminalize its assistant record");
|
|
555
562
|
}
|
|
556
563
|
await assertCausalHistoryOrder(store, causalHistoryConversationId);
|
|
564
|
+
await assertActiveRunCausalOrder(store, causalActiveConversationId);
|
|
557
565
|
await assertAbsorptionIsAtomic(store, absorbConversationId);
|
|
558
566
|
}
|
|
567
|
+
async function assertActiveRunCausalOrder(store, conversationId) {
|
|
568
|
+
for (const id of ["z-causal-run", "a-causal-run"]) {
|
|
569
|
+
const input = userMessage(conversationId, `${id}-input`);
|
|
570
|
+
requireOutcome(await store.acceptInputAndAssignRun({
|
|
571
|
+
idempotencyKey: id,
|
|
572
|
+
input,
|
|
573
|
+
run: queuedRun(conversationId, input.id, id)
|
|
574
|
+
}), "applied");
|
|
575
|
+
}
|
|
576
|
+
const active = await store.listActiveRuns(conversationId);
|
|
577
|
+
if (active.map((run) => run.id).join(",") !== "z-causal-run,a-causal-run") {
|
|
578
|
+
throw new Error("listActiveRuns must preserve causal history order for timestamp ties");
|
|
579
|
+
}
|
|
580
|
+
}
|
|
559
581
|
async function assertCausalHistoryOrder(store, conversationId) {
|
|
560
582
|
const leadInput = userMessage(conversationId, "causal-input-1");
|
|
561
583
|
const leadRun = queuedRun(conversationId, leadInput.id, "causal-run-1");
|
package/llms-full.txt
CHANGED
|
@@ -61,6 +61,7 @@ own, recorded as an ADR.
|
|
|
61
61
|
| `stitchkit/react` | browser | stable | `createCursorQuery`, `createCacheBridge` |
|
|
62
62
|
| `stitchkit/agent-runtime` | server | evolving<br>_redefined in 9 of the 13 minors since 0.56.2, most recently 0.66.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
|
|
63
63
|
| `stitchkit/agent-runtime/openrouter` | server | evolving | isolated OpenRouter language-model adapter |
|
|
64
|
+
| `stitchkit/agent-runtime/browser` | browser + server | evolving | canonical agent records, events and reconnect cursor without execution or sinks |
|
|
64
65
|
| `stitchkit/application` | server | evolving<br>_redefined in 3 of the 13 minors since 0.56.2, most recently 0.67.0_ | managed resource graph, readiness, admission, schedules and bounded shutdown |
|
|
65
66
|
| `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
|
|
66
67
|
| `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
|
|
@@ -2428,6 +2429,13 @@ wire `end` frame and, when declared, at least one matching terminal item; EOF is
|
|
|
2428
2429
|
converge on the request operation. See the
|
|
2429
2430
|
[server half](./server.md#contract-first-streams). → ADR 0117.
|
|
2430
2431
|
|
|
2432
|
+
The request deadline bounds the wait for response headers. Once headers arrive,
|
|
2433
|
+
that timer is cleared, while the caller signal remains attached to the response
|
|
2434
|
+
body until it ends or is cancelled. This is the same for a Fetch-config client,
|
|
2435
|
+
`createHttpClient`, the Bun-only `unix` convenience and an injected portable
|
|
2436
|
+
Unix transport. Cancelling a quiet stream therefore releases its server source
|
|
2437
|
+
and transport connection instead of only settling the local iterator.
|
|
2438
|
+
|
|
2431
2439
|
An established NDJSON protocol may keep its item schema as the complete wire
|
|
2432
2440
|
frame. This mode requires a terminal item because an unwrapped response has no
|
|
2433
2441
|
separate safe error/end envelope:
|
|
@@ -3742,6 +3750,21 @@ If the application already owns that loop, continue importing `mountAgent` from
|
|
|
3742
3750
|
this entrypoint uses the tool executor from it, but pulls no MCP peer, so
|
|
3743
3751
|
choosing `mountAgent` alone costs you nothing from here.
|
|
3744
3752
|
|
|
3753
|
+
UI and shared DTO code must import canonical records and delivery validation from
|
|
3754
|
+
`stitchkit/agent-runtime/browser`, not from the server runtime barrel:
|
|
3755
|
+
|
|
3756
|
+
```ts
|
|
3757
|
+
import {
|
|
3758
|
+
AgentRunSchema,
|
|
3759
|
+
AgentRuntimeEventSchema,
|
|
3760
|
+
advanceAgentRuntimeEventCursor,
|
|
3761
|
+
} from 'stitchkit/agent-runtime/browser'
|
|
3762
|
+
```
|
|
3763
|
+
|
|
3764
|
+
This browser-safe entrypoint re-exports the same schemas and inferred types used
|
|
3765
|
+
by the runtime. It intentionally excludes model construction, execution,
|
|
3766
|
+
persistence, event sinks and every Node context dependency.
|
|
3767
|
+
|
|
3745
3768
|
## Install
|
|
3746
3769
|
|
|
3747
3770
|
```sh
|
|
@@ -4190,12 +4213,18 @@ checkpoint/terminal CAS and tool context, so a distributed adapter can reject an
|
|
|
4190
4213
|
an owner label is reused. Lease expiry and renewal remain application-owned.
|
|
4191
4214
|
|
|
4192
4215
|
On startup, `runtime.recover({ resolveContext })` consumes bounded lightweight
|
|
4193
|
-
pages
|
|
4216
|
+
pages, then restores each conversation's canonical causal order before any run
|
|
4217
|
+
acquires. Scan identifiers and page boundaries therefore never become queue
|
|
4218
|
+
order. Its safe default resumes queued runs and reports acquired or
|
|
4194
4219
|
`interrupt_requested` runs as skipped. A policy may requeue acquired work only
|
|
4195
4220
|
with explicit replay-safe evidence, or abandon it only with stale-owner
|
|
4196
|
-
evidence. Each attempted run returns its own outcome/error; `
|
|
4197
|
-
`
|
|
4198
|
-
|
|
4221
|
+
evidence. Each attempted run returns its own outcome/error; a `resumed` or
|
|
4222
|
+
`requeued` outcome also exposes the terminal `result` promise. The outcome is
|
|
4223
|
+
reported only after durable acquisition, so a lost acquisition is `failed`
|
|
4224
|
+
rather than a successful-looking handoff. `pageSize`, `maxRuns`, and `signal`
|
|
4225
|
+
bound the pass. `runtime.resume(...)` remains available for one known queued
|
|
4226
|
+
record; its `accepted` promise likewise means that the recovered run acquired
|
|
4227
|
+
durable ownership, while `result` carries terminal completion.
|
|
4199
4228
|
|
|
4200
4229
|
Canonical records currently write `schemaVersion: 1`. A durable adapter owns
|
|
4201
4230
|
read-time migration of older rows: migrate to the current shape at its storage
|
|
@@ -11516,7 +11545,7 @@ Server-only optional application runtime. See the
|
|
|
11516
11545
|
| `AgentRuntimeAdmission` | _type_ | canonical committed input, assigned run, pending assistant projection, compatibility IDs and snapshot version |
|
|
11517
11546
|
| `AgentAdmissionEventSchema` | schema | post-commit admission projection; removes store rereads but does not imply exactly-once delivery |
|
|
11518
11547
|
| `AgentRunMetricsSchema` | schema | optional provenance-aware usage and timings; `partial` says the provider never reported the run finished, so the figure beside it is not a confirmed total |
|
|
11519
|
-
| `AgentRuntimeRecoverOptions` | _type_ | bounded paged startup recovery with context resolver and explicit evidence policy |
|
|
11548
|
+
| `AgentRuntimeRecoverOptions` | _type_ | bounded paged startup recovery with causal per-conversation scheduling, context resolver and explicit evidence policy |
|
|
11520
11549
|
| `AgentRuntimeConflictError` | class | thrown when a store mutation loses to a concurrent writer — catchable by type from `stitchkit/agent-runtime` |
|
|
11521
11550
|
| `AgentSessionCloseOptions` | _type_ | `gracePeriodMs` for natural settlement, then abort, then `forceTimeoutMs` for bounded settlement after it |
|
|
11522
11551
|
| `AgentSessionCloseResult` | _type_ | what `close()` achieved: `settled`, or `timedOut` with `remaining` runs still in flight. Only omitting `forceTimeoutMs` guarantees nothing is in flight on return |
|
|
@@ -11629,6 +11658,20 @@ Managed effects and operator telemetry additionally export `AgentToolFenceConfig
|
|
|
11629
11658
|
may accompany checkpoint/terminal writes and tool context; internal causes are redacted unless an
|
|
11630
11659
|
operator-only observability sink explicitly opts in.
|
|
11631
11660
|
|
|
11661
|
+
## `stitchkit/agent-runtime/browser`
|
|
11662
|
+
|
|
11663
|
+
Browser-safe canonical agent data. It re-exports the run, message, part, usage,
|
|
11664
|
+
terminal and provider-envelope schemas/types listed under
|
|
11665
|
+
`stitchkit/agent-runtime`, together with all runtime delivery event schemas,
|
|
11666
|
+
`AgentRuntimeEventCursorSchema`, `advanceAgentRuntimeEventCursor` and
|
|
11667
|
+
`agentDurableEventId`. It imports no model provider, executor, store, event sink
|
|
11668
|
+
or Node context module.
|
|
11669
|
+
|
|
11670
|
+
Use this entrypoint from client components and shared DTO packages. The full
|
|
11671
|
+
`stitchkit/agent-runtime` entrypoint remains server-only.
|
|
11672
|
+
|
|
11673
|
+
---
|
|
11674
|
+
|
|
11632
11675
|
## `stitchkit/agent-runtime/openrouter`
|
|
11633
11676
|
|
|
11634
11677
|
| Export | Kind | Summary |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchkit",
|
|
3
|
-
"version": "0.68.
|
|
3
|
+
"version": "0.68.5",
|
|
4
4
|
"description": "Contract-first backend framework — one defineContract() into an HTTP API, MCP tools, AI-agent tools and a typed client. Bun and Node.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -84,6 +84,10 @@
|
|
|
84
84
|
"types": "./dist/agent-runtime.d.ts",
|
|
85
85
|
"import": "./dist/agent-runtime.js"
|
|
86
86
|
},
|
|
87
|
+
"./agent-runtime/browser": {
|
|
88
|
+
"types": "./dist/agent-runtime-browser.d.ts",
|
|
89
|
+
"import": "./dist/agent-runtime-browser.js"
|
|
90
|
+
},
|
|
87
91
|
"./agent-runtime/openrouter": {
|
|
88
92
|
"types": "./dist/agent-runtime-openrouter.d.ts",
|
|
89
93
|
"import": "./dist/agent-runtime-openrouter.js"
|
|
@@ -122,7 +126,7 @@
|
|
|
122
126
|
},
|
|
123
127
|
"scripts": {
|
|
124
128
|
"check": "bun x tsc --noEmit",
|
|
125
|
-
"build:browser": "bun build src/index.ts src/react.ts src/contract/index.ts src/declaration.ts --outdir dist --target node --packages external --splitting --root src",
|
|
129
|
+
"build:browser": "bun build src/index.ts src/react.ts src/contract/index.ts src/declaration.ts src/agent-runtime-browser.ts --outdir dist --target node --packages external --splitting --root src",
|
|
126
130
|
"build:server": "bun build src/server/index.ts src/node.ts src/tools.ts src/tool-invoker.ts src/cli.ts src/remote.ts src/files.ts src/testing.ts src/observability/index.ts src/agent-runtime.ts src/agent-runtime-openrouter.ts src/application.ts src/application-grammy.ts src/application-opentelemetry.ts --outdir dist --target node --packages external --splitting --root src",
|
|
127
131
|
"build:js": "bun run build:browser && bun run build:server && bun scripts/preserve-webpack-ignore.mjs",
|
|
128
132
|
"build:types": "bun x tsc -p tsconfig.build.json --emitDeclarationOnly && bun scripts/rewrite-declaration-specifiers.mjs",
|