stitchkit 0.100.0 → 0.100.1
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 +80 -1
- package/dist/agent-runtime/invocation-attempt.d.ts +6 -0
- package/dist/agent-runtime/invocation-attempt.d.ts.map +1 -1
- package/dist/agent-runtime/invocation-payload.d.ts +2 -0
- package/dist/agent-runtime/invocation-payload.d.ts.map +1 -1
- package/dist/agent-runtime/invocation-read.d.ts.map +1 -1
- package/dist/agent-runtime/invocations.d.ts +6 -0
- package/dist/agent-runtime/invocations.d.ts.map +1 -1
- package/dist/agent-runtime/projections.d.ts.map +1 -1
- package/dist/agent-runtime/schedule-dispatch.d.ts +9 -2
- package/dist/agent-runtime/schedule-dispatch.d.ts.map +1 -1
- package/dist/agent-runtime/schedule-records.d.ts +8 -2
- package/dist/agent-runtime/schedule-records.d.ts.map +1 -1
- package/dist/agent-runtime/schedules.d.ts +5 -0
- package/dist/agent-runtime/schedules.d.ts.map +1 -1
- package/dist/agent-runtime/sqlite-events.d.ts.map +1 -1
- package/dist/agent-runtime/store-create.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver-contract.d.ts +9 -0
- package/dist/agent-runtime/store-driver-contract.d.ts.map +1 -1
- package/dist/agent-runtime/store-event-once.d.ts +9 -1
- package/dist/agent-runtime/store-event-once.d.ts.map +1 -1
- package/dist/agent-runtime/store-memory.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +6 -0
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/application/change-subscription.d.ts.map +1 -1
- package/dist/application/notification-delivery.d.ts +24 -1
- package/dist/application/notification-delivery.d.ts.map +1 -1
- package/dist/application/notification-outbox-state.d.ts +4 -0
- package/dist/application/notification-outbox-state.d.ts.map +1 -1
- package/dist/application/notification-outbox.d.ts.map +1 -1
- package/dist/application/watch-hub-core.d.ts +14 -79
- package/dist/application/watch-hub-core.d.ts.map +1 -1
- package/dist/application/watch-hub-source.d.ts +98 -0
- package/dist/application/watch-hub-source.d.ts.map +1 -0
- package/dist/browser/session-credentials.d.ts +6 -0
- package/dist/browser/session-credentials.d.ts.map +1 -1
- package/dist/browser/session.d.ts +3 -1
- package/dist/browser/session.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/harness.js +1 -1
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +2 -2
- package/dist/entrypoints/agent-runtime/sqlite/node.js +2 -2
- package/dist/entrypoints/agent-runtime/testing.js +2 -2
- package/dist/entrypoints/agent-runtime.js +53 -19
- package/dist/entrypoints/application/diagnostic-journal.js +1 -1
- package/dist/entrypoints/application.d.ts +1 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +275 -189
- package/dist/entrypoints/files.js +8 -2
- package/dist/entrypoints/index.js +33 -6
- package/dist/entrypoints/live.js +16 -12
- package/dist/entrypoints/node.js +1 -1
- package/dist/entrypoints/server.js +4 -4
- package/dist/{index-s84he4v7.js → index-10x2hbx0.js} +4 -1
- package/dist/{index-js7gqrb1.js → index-c6bxbsnc.js} +5 -1
- package/dist/{index-y9qn426m.js → index-eaysmdtq.js} +10 -1
- package/dist/{index-xwhjjw2x.js → index-f4fx2tza.js} +119 -49
- package/dist/{index-p3vsv1fb.js → index-hadmp193.js} +14 -5
- package/dist/{index-0b1cnze2.js → index-vqr7f7v8.js} +31 -15
- package/dist/internal/coalesced-task.d.ts +2 -0
- package/dist/internal/coalesced-task.d.ts.map +1 -1
- package/dist/internal/process-instance.d.ts.map +1 -1
- package/dist/internal/with-exclusive-lock.d.ts.map +1 -1
- package/dist/server/realtime.d.ts.map +1 -1
- package/llms/agent-runtime.2.txt +13 -5
- package/llms/agent-runtime.3.txt +9 -4
- package/llms/agent-runtime.txt +8 -3
- package/llms/application.2.txt +21 -5
- package/llms/live.txt +2 -1
- package/llms/server.3.txt +22 -0
- package/llms/stitchkit.3.txt +9 -5
- package/llms/upgrading-0.74.0-0.100.0.txt +15 -3
- package/llms-full.txt +99 -26
- package/llms.txt +5 -5
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
attemptExclusiveLock
|
|
3
|
-
} from "../index-
|
|
3
|
+
} from "../index-c6bxbsnc.js";
|
|
4
4
|
import"../index-zjjyynm3.js";
|
|
5
5
|
import {
|
|
6
6
|
ManagedFileError,
|
|
@@ -47,6 +47,12 @@ function describeHolder(holder) {
|
|
|
47
47
|
const machine = holder.machine === undefined ? "" : `, machine ${holder.machine}`;
|
|
48
48
|
return `pid ${holder.pid} on ${holder.host}${machine} since ${holder.acquiredAt}`;
|
|
49
49
|
}
|
|
50
|
+
function describeEvidence(diagnosis) {
|
|
51
|
+
if (!diagnosis)
|
|
52
|
+
return "";
|
|
53
|
+
const identity = diagnosis.identity === undefined ? "" : `, identity ${diagnosis.identity}`;
|
|
54
|
+
return ` (${diagnosis.attribution}, liveness ${diagnosis.liveness}${identity})`;
|
|
55
|
+
}
|
|
50
56
|
function pause(ms, signal) {
|
|
51
57
|
return new Promise((resolve) => {
|
|
52
58
|
const done = () => {
|
|
@@ -94,7 +100,7 @@ async function withExclusiveLock(path, run, options = {}) {
|
|
|
94
100
|
const remaining = deadline - performance.now();
|
|
95
101
|
if (remaining <= 0) {
|
|
96
102
|
const holder = attempt.diagnosis?.owner ?? null;
|
|
97
|
-
throw new ExclusiveLockError("LOCK_TIMEOUT", label, holder, `[stitchkit] the lock on "${label}" is held by ${describeHolder(holder)}; gave up after ${timeoutMs} ms`, { cause: attempt.error });
|
|
103
|
+
throw new ExclusiveLockError("LOCK_TIMEOUT", label, holder, `[stitchkit] the lock on "${label}" is held by ${describeHolder(holder)}${describeEvidence(attempt.diagnosis)}; gave up after ${timeoutMs} ms`, { cause: attempt.error });
|
|
98
104
|
}
|
|
99
105
|
await pause(Math.min(delay * (0.5 + Math.random() / 2), remaining), signal);
|
|
100
106
|
delay = Math.min(delay * 2, MAX_RETRY_MS);
|
|
@@ -32,6 +32,9 @@ import {
|
|
|
32
32
|
RealtimeRequestRejectedError,
|
|
33
33
|
RealtimeRequestTimeoutError
|
|
34
34
|
} from "../index-dce5wxcq.js";
|
|
35
|
+
import {
|
|
36
|
+
withDeadline
|
|
37
|
+
} from "../index-b9aefrqd.js";
|
|
35
38
|
import"../index-48ffdxgk.js";
|
|
36
39
|
import {
|
|
37
40
|
createContractFactory,
|
|
@@ -398,8 +401,8 @@ function createRetainedTopics() {
|
|
|
398
401
|
}
|
|
399
402
|
// src/browser/session.ts
|
|
400
403
|
class SessionExpiredError extends Error {
|
|
401
|
-
constructor() {
|
|
402
|
-
super("The originating session is no longer active");
|
|
404
|
+
constructor(options) {
|
|
405
|
+
super("The originating session is no longer active", options);
|
|
403
406
|
this.name = "SessionExpiredError";
|
|
404
407
|
}
|
|
405
408
|
}
|
|
@@ -446,7 +449,8 @@ function createSessionScope() {
|
|
|
446
449
|
assertCurrent();
|
|
447
450
|
return result;
|
|
448
451
|
} catch (error) {
|
|
449
|
-
|
|
452
|
+
if (!current())
|
|
453
|
+
throw new SessionExpiredError({ cause: error });
|
|
450
454
|
throw error;
|
|
451
455
|
}
|
|
452
456
|
},
|
|
@@ -500,7 +504,30 @@ function createSessionCredentials(config) {
|
|
|
500
504
|
if (!Number.isInteger(timeout) || timeout < 1 || timeout > 3600000) {
|
|
501
505
|
throw new RangeError("refreshTimeoutMs must be between 1 and 3600000");
|
|
502
506
|
}
|
|
507
|
+
const storageTimeout = config.storageTimeoutMs ?? 30000;
|
|
508
|
+
if (!Number.isInteger(storageTimeout) || storageTimeout < 1 || storageTimeout > 3600000) {
|
|
509
|
+
throw new RangeError("storageTimeoutMs must be between 1 and 3600000");
|
|
510
|
+
}
|
|
503
511
|
let state;
|
|
512
|
+
const bounded = async (work, wrote) => {
|
|
513
|
+
const outcome = await withDeadline(work, storageTimeout);
|
|
514
|
+
if (outcome.settled)
|
|
515
|
+
return;
|
|
516
|
+
work.then(() => {
|
|
517
|
+
if (state?.credentials !== wrote)
|
|
518
|
+
return restore();
|
|
519
|
+
}).catch(() => {
|
|
520
|
+
return;
|
|
521
|
+
});
|
|
522
|
+
throw new Error("Session storage did not answer within storageTimeoutMs");
|
|
523
|
+
};
|
|
524
|
+
const restore = () => serialize(async () => {
|
|
525
|
+
const current = state;
|
|
526
|
+
if (current)
|
|
527
|
+
await bounded(config.storage.write(current.operation.context, current.credentials), current.credentials);
|
|
528
|
+
else
|
|
529
|
+
await bounded(config.storage.clear(), undefined);
|
|
530
|
+
});
|
|
504
531
|
let flight;
|
|
505
532
|
const read = (operation) => {
|
|
506
533
|
if (!config.scope.owns(operation) || state?.operation !== operation)
|
|
@@ -509,7 +536,7 @@ function createSessionCredentials(config) {
|
|
|
509
536
|
};
|
|
510
537
|
const write = (operation, credentials) => serialize(async () => {
|
|
511
538
|
read(operation);
|
|
512
|
-
await config.storage.write(operation.context, credentials);
|
|
539
|
+
await bounded(config.storage.write(operation.context, credentials), credentials);
|
|
513
540
|
operation.assertCurrent();
|
|
514
541
|
});
|
|
515
542
|
return {
|
|
@@ -526,7 +553,7 @@ function createSessionCredentials(config) {
|
|
|
526
553
|
const generation = config.scope.generation;
|
|
527
554
|
return serialize(async () => {
|
|
528
555
|
if (config.scope.generation === generation)
|
|
529
|
-
await config.storage.clear();
|
|
556
|
+
await bounded(config.storage.clear(), undefined);
|
|
530
557
|
});
|
|
531
558
|
},
|
|
532
559
|
read,
|
|
@@ -547,9 +574,9 @@ function createSessionCredentials(config) {
|
|
|
547
574
|
const result = operation.run(async () => {
|
|
548
575
|
const next = await Promise.race([config.refresh(credentials, signal), abortPromise]);
|
|
549
576
|
read(operation);
|
|
577
|
+
state = { operation, credentials: next };
|
|
550
578
|
await write(operation, next);
|
|
551
579
|
read(operation);
|
|
552
|
-
state = { operation, credentials: next };
|
|
553
580
|
return next;
|
|
554
581
|
}).finally(() => {
|
|
555
582
|
clearTimeout(timer);
|
package/dist/entrypoints/live.js
CHANGED
|
@@ -299,33 +299,37 @@ function createWatchClient(contract, config) {
|
|
|
299
299
|
}
|
|
300
300
|
function handleFor(action, args) {
|
|
301
301
|
const mine = new Set;
|
|
302
|
-
|
|
302
|
+
const key = {
|
|
303
303
|
service,
|
|
304
304
|
action,
|
|
305
305
|
digest: argumentsDigest(args),
|
|
306
306
|
...config.session && { instance: config.session.id }
|
|
307
|
-
}
|
|
307
|
+
};
|
|
308
|
+
let entry;
|
|
308
309
|
return {
|
|
309
310
|
subscribe(listeners) {
|
|
310
311
|
config.session?.assertCurrent();
|
|
311
|
-
if (entry.released)
|
|
312
|
-
entry = entryFor(
|
|
313
|
-
|
|
312
|
+
if (!entry || entry.released)
|
|
313
|
+
entry = entryFor(key, args);
|
|
314
|
+
const current = entry;
|
|
315
|
+
if (current.listeners.size >= maxListeners)
|
|
314
316
|
throw new Error("Watch listener capacity exceeded");
|
|
315
317
|
const registered = listeners;
|
|
316
318
|
mine.add(registered);
|
|
317
|
-
|
|
318
|
-
if (
|
|
319
|
-
registered.value(
|
|
320
|
-
registered.state?.(
|
|
321
|
-
openWatch(config, openTimeoutMs,
|
|
319
|
+
current.listeners.add(registered);
|
|
320
|
+
if (current.hasValue)
|
|
321
|
+
registered.value(current.value);
|
|
322
|
+
registered.state?.(current.state);
|
|
323
|
+
openWatch(config, openTimeoutMs, current);
|
|
322
324
|
return () => {
|
|
323
325
|
mine.delete(registered);
|
|
324
|
-
|
|
325
|
-
releaseWatchClientEntry(
|
|
326
|
+
current.listeners.delete(registered);
|
|
327
|
+
releaseWatchClientEntry(current);
|
|
326
328
|
};
|
|
327
329
|
},
|
|
328
330
|
close() {
|
|
331
|
+
if (!entry)
|
|
332
|
+
return;
|
|
329
333
|
for (const listener of mine)
|
|
330
334
|
entry.listeners.delete(listener);
|
|
331
335
|
mine.clear();
|
package/dist/entrypoints/node.js
CHANGED
|
@@ -19,14 +19,11 @@ import {
|
|
|
19
19
|
sseRoute,
|
|
20
20
|
streamingRoute,
|
|
21
21
|
webSocketLane
|
|
22
|
-
} from "../index-
|
|
22
|
+
} from "../index-hadmp193.js";
|
|
23
23
|
import"../index-y91ry9h2.js";
|
|
24
24
|
import {
|
|
25
25
|
createFileStateStore
|
|
26
26
|
} from "../index-tm88avnj.js";
|
|
27
|
-
import {
|
|
28
|
-
withDeadline
|
|
29
|
-
} from "../index-b9aefrqd.js";
|
|
30
27
|
import {
|
|
31
28
|
ShutdownOptionsSchema,
|
|
32
29
|
ShutdownResultSchema,
|
|
@@ -36,6 +33,9 @@ import {
|
|
|
36
33
|
} from "../index-3z73fh2c.js";
|
|
37
34
|
import"../index-ndddxtyx.js";
|
|
38
35
|
import"../index-dce5wxcq.js";
|
|
36
|
+
import {
|
|
37
|
+
withDeadline
|
|
38
|
+
} from "../index-b9aefrqd.js";
|
|
39
39
|
import {
|
|
40
40
|
inputIsQuery
|
|
41
41
|
} from "../index-48ffdxgk.js";
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
createAgentRuntimeStore,
|
|
8
8
|
isActiveRunState,
|
|
9
9
|
replaceMessage
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-vqr7f7v8.js";
|
|
11
11
|
import {
|
|
12
12
|
AgentRecoverablePageSchema,
|
|
13
13
|
AgentStoreEventEnvelopeSchema,
|
|
@@ -202,6 +202,9 @@ var memoryEvents = {
|
|
|
202
202
|
items,
|
|
203
203
|
...selected.length > items.length && last ? { nextSeq: last.seq + 1 } : {}
|
|
204
204
|
});
|
|
205
|
+
},
|
|
206
|
+
async find(transaction, input) {
|
|
207
|
+
return (transaction.events.get(input.conversationId) ?? []).find((event) => event.eventId === input.eventId);
|
|
205
208
|
}
|
|
206
209
|
};
|
|
207
210
|
function scanMemoryRecoverable(runs, input) {
|
|
@@ -108,7 +108,11 @@ async function readProcessInstance(pid, procRoot = "/proc") {
|
|
|
108
108
|
readFile2(`${procRoot}/sys/kernel/random/boot_id`, "utf8"),
|
|
109
109
|
readlink(`${procRoot}/self/ns/pid`),
|
|
110
110
|
readFile2(`${procRoot}/self/status`, "utf8"),
|
|
111
|
-
readlink(`${procRoot}/self/ns/time`)
|
|
111
|
+
readlink(`${procRoot}/self/ns/time`).catch((error) => {
|
|
112
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
113
|
+
return "";
|
|
114
|
+
throw error;
|
|
115
|
+
}),
|
|
112
116
|
readFile2(`${procRoot}/self/stat`, "utf8"),
|
|
113
117
|
readFile2(`${procRoot}/${pid}/stat`, "utf8")
|
|
114
118
|
]);
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
AgentStoredRunSchema,
|
|
11
11
|
appendEventIn,
|
|
12
12
|
createAgentRuntimeStore
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-vqr7f7v8.js";
|
|
14
14
|
import {
|
|
15
15
|
AgentRecoverableDescriptorSchema,
|
|
16
16
|
AgentRecoverablePageSchema,
|
|
@@ -528,6 +528,15 @@ var sqliteEvents = {
|
|
|
528
528
|
items,
|
|
529
529
|
...hasMore && lastEvent ? { nextSeq: lastEvent.seq + 1 } : {}
|
|
530
530
|
});
|
|
531
|
+
},
|
|
532
|
+
async find(transaction, input) {
|
|
533
|
+
const row = transaction.prepare(`
|
|
534
|
+
SELECT event_id, conversation_id, seq, schema_version, kind, occurred_at,
|
|
535
|
+
ignorable, payload
|
|
536
|
+
FROM stitchkit_agent_runtime_events
|
|
537
|
+
WHERE event_id = ? AND conversation_id = ?
|
|
538
|
+
`).get(input.eventId, input.conversationId);
|
|
539
|
+
return row === null || row === undefined ? undefined : parseEventRow(row);
|
|
531
540
|
}
|
|
532
541
|
};
|
|
533
542
|
var sqliteArchive = {
|
|
@@ -714,7 +714,7 @@ var InvocationPayloadSchema = Base.extend({
|
|
|
714
714
|
|
|
715
715
|
// src/agent-runtime/invocation-attempt.ts
|
|
716
716
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
717
|
-
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
717
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
|
|
718
718
|
import { wrapLanguageModel } from "ai";
|
|
719
719
|
|
|
720
720
|
// src/agent-runtime/invocation-failure.ts
|
|
@@ -742,13 +742,18 @@ import {
|
|
|
742
742
|
} from "node:crypto";
|
|
743
743
|
import { z as z2 } from "zod";
|
|
744
744
|
function invocationJson(value) {
|
|
745
|
+
const errors = new WeakSet;
|
|
745
746
|
return z2.json().parse(JSON.parse(JSON.stringify(value, (_key, item) => {
|
|
746
747
|
if (item instanceof Uint8Array)
|
|
747
748
|
return { base64: Buffer.from(item).toString("base64") };
|
|
748
749
|
if (item instanceof ArrayBuffer)
|
|
749
750
|
return { base64: Buffer.from(item).toString("base64") };
|
|
750
|
-
if (item instanceof Error)
|
|
751
|
+
if (item instanceof Error) {
|
|
752
|
+
if (errors.has(item))
|
|
753
|
+
return { name: item.name, message: item.message };
|
|
754
|
+
errors.add(item);
|
|
751
755
|
return { name: item.name, message: item.message, cause: item.cause };
|
|
756
|
+
}
|
|
752
757
|
return item;
|
|
753
758
|
})));
|
|
754
759
|
}
|
|
@@ -760,6 +765,7 @@ function createInvocationPayloads(store, key, maxBytes) {
|
|
|
760
765
|
throw new TypeError("Invocation payload key must contain 32 bytes");
|
|
761
766
|
const encryptionKey = Uint8Array.from(key);
|
|
762
767
|
return {
|
|
768
|
+
maxBytes,
|
|
763
769
|
async write(identity, value) {
|
|
764
770
|
const data = Buffer.from(serializeCanonicalJson(invocationJson(value)));
|
|
765
771
|
if (data.byteLength > maxBytes)
|
|
@@ -771,7 +777,9 @@ function createInvocationPayloads(store, key, maxBytes) {
|
|
|
771
777
|
cipher.setAAD(Buffer.from(JSON.stringify([identity.conversationId, identity.invocationId, artifactId])));
|
|
772
778
|
const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]);
|
|
773
779
|
const { conversationId } = identity;
|
|
774
|
-
|
|
780
|
+
if (!store.appendEventOnce)
|
|
781
|
+
throw new TypeError("Invocation payloads require atomic store.appendEventOnce");
|
|
782
|
+
await store.appendEventOnce({
|
|
775
783
|
conversationId,
|
|
776
784
|
kind: "provider/payload",
|
|
777
785
|
payload: {
|
|
@@ -783,7 +791,7 @@ function createInvocationPayloads(store, key, maxBytes) {
|
|
|
783
791
|
tag: cipher.getAuthTag().toString("base64"),
|
|
784
792
|
ciphertext: ciphertext.toString("base64")
|
|
785
793
|
}
|
|
786
|
-
});
|
|
794
|
+
}, artifactId);
|
|
787
795
|
return { artifactId, sha256 };
|
|
788
796
|
},
|
|
789
797
|
decrypt(conversationId, value) {
|
|
@@ -1067,8 +1075,11 @@ function wrapInvocationModel(writer, selected, step, model = selected.model) {
|
|
|
1067
1075
|
}
|
|
1068
1076
|
return context;
|
|
1069
1077
|
});
|
|
1070
|
-
const finish =
|
|
1071
|
-
|
|
1078
|
+
const finish = (...args) => recordResponse(...args).catch((error) => {
|
|
1079
|
+
writer.receiptErrors.push(error);
|
|
1080
|
+
});
|
|
1081
|
+
const recordResponse = async (context, status, payload, usage, response, providerMetadata) => invocationAudit(async () => {
|
|
1082
|
+
const artifact = await writer.payloads.write(writer.identity, boundedEvidence(payload, Math.floor(writer.payloads.maxBytes / 2)));
|
|
1072
1083
|
await writer.append({
|
|
1073
1084
|
...writer.identity,
|
|
1074
1085
|
type: "provider/response",
|
|
@@ -1126,13 +1137,12 @@ function wrapInvocationModel(writer, selected, step, model = selected.model) {
|
|
|
1126
1137
|
let ended = false;
|
|
1127
1138
|
let failed = false;
|
|
1128
1139
|
let response;
|
|
1129
|
-
const
|
|
1130
|
-
let outputBytes = 0;
|
|
1140
|
+
const evidence = streamEvidence(Math.floor(writer.payloads.maxBytes / 2));
|
|
1131
1141
|
const end = async (status, value, usage, metadata) => {
|
|
1132
1142
|
if (ended)
|
|
1133
1143
|
return;
|
|
1134
1144
|
ended = true;
|
|
1135
|
-
await finish(context, status,
|
|
1145
|
+
await finish(context, status, evidence.payload(value), usage, response, metadata);
|
|
1136
1146
|
};
|
|
1137
1147
|
return {
|
|
1138
1148
|
...result,
|
|
@@ -1159,14 +1169,7 @@ function wrapInvocationModel(writer, selected, step, model = selected.model) {
|
|
|
1159
1169
|
} else {
|
|
1160
1170
|
if (part.type === "error")
|
|
1161
1171
|
failed = true;
|
|
1162
|
-
|
|
1163
|
-
outputBytes += Buffer.byteLength(JSON.stringify(json));
|
|
1164
|
-
if (outputBytes > 1048576) {
|
|
1165
|
-
await reader.cancel();
|
|
1166
|
-
await end("failed", { reason: "receipt-output-limit" });
|
|
1167
|
-
throw new RangeError("Invocation stream evidence exceeds 1 MiB");
|
|
1168
|
-
}
|
|
1169
|
-
output.push(json);
|
|
1172
|
+
evidence.record(part);
|
|
1170
1173
|
}
|
|
1171
1174
|
controller.enqueue(part);
|
|
1172
1175
|
},
|
|
@@ -1183,6 +1186,45 @@ function wrapInvocationModel(writer, selected, step, model = selected.model) {
|
|
|
1183
1186
|
}
|
|
1184
1187
|
});
|
|
1185
1188
|
}
|
|
1189
|
+
function boundedEvidence(payload, limit) {
|
|
1190
|
+
const serialized = JSON.stringify(invocationJson(payload));
|
|
1191
|
+
const bytes = Buffer.byteLength(serialized);
|
|
1192
|
+
if (bytes <= limit)
|
|
1193
|
+
return payload;
|
|
1194
|
+
return {
|
|
1195
|
+
truncated: { bytes, sha256: createHash2("sha256").update(serialized).digest("hex") }
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
function streamEvidence(limit) {
|
|
1199
|
+
const output = [];
|
|
1200
|
+
const whole = createHash2("sha256");
|
|
1201
|
+
let bytes = 0;
|
|
1202
|
+
let parts = 0;
|
|
1203
|
+
let truncated = false;
|
|
1204
|
+
return {
|
|
1205
|
+
record(part) {
|
|
1206
|
+
const json = invocationJson(part);
|
|
1207
|
+
const serialized = JSON.stringify(json);
|
|
1208
|
+
whole.update(serialized).update(`
|
|
1209
|
+
`);
|
|
1210
|
+
parts += 1;
|
|
1211
|
+
const size = Buffer.byteLength(serialized);
|
|
1212
|
+
if (!truncated && bytes + size > limit)
|
|
1213
|
+
truncated = true;
|
|
1214
|
+
if (truncated)
|
|
1215
|
+
return;
|
|
1216
|
+
bytes += size;
|
|
1217
|
+
output.push(json);
|
|
1218
|
+
},
|
|
1219
|
+
payload(terminal) {
|
|
1220
|
+
return {
|
|
1221
|
+
output,
|
|
1222
|
+
terminal,
|
|
1223
|
+
...truncated && { truncated: { parts, sha256: whole.digest("hex") } }
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1186
1228
|
function reportedUsage(selected, value, providerMetadata) {
|
|
1187
1229
|
const usage = {
|
|
1188
1230
|
inputTokens: value?.inputTokens.total,
|
|
@@ -1240,6 +1282,13 @@ async function readInvocationRecords(store, input) {
|
|
|
1240
1282
|
};
|
|
1241
1283
|
}
|
|
1242
1284
|
async function readInvocationPayload(store, payloads, input) {
|
|
1285
|
+
const keyed = await store.findEventOnce?.({
|
|
1286
|
+
conversationId: input.conversationId,
|
|
1287
|
+
kind: "provider/payload",
|
|
1288
|
+
key: input.artifactId
|
|
1289
|
+
});
|
|
1290
|
+
if (keyed)
|
|
1291
|
+
return payloads.decrypt(input.conversationId, keyed.payload);
|
|
1243
1292
|
let fromSeq;
|
|
1244
1293
|
do {
|
|
1245
1294
|
const page = await store.readEvents({
|
|
@@ -1268,6 +1317,30 @@ function beginAgentInvocation(ledger, store, input, context) {
|
|
|
1268
1317
|
throw new TypeError("Agent and invocations must share the same store");
|
|
1269
1318
|
return binding.start(input, context);
|
|
1270
1319
|
}
|
|
1320
|
+
function withReceipt(result, writer) {
|
|
1321
|
+
const [receiptError] = writer.receiptErrors;
|
|
1322
|
+
return receiptError === undefined ? result : { ...result, receiptError };
|
|
1323
|
+
}
|
|
1324
|
+
async function generateWithFallback(models, writer, input, abortSignal) {
|
|
1325
|
+
for (const [step, key] of input.models.entries()) {
|
|
1326
|
+
abortSignal.throwIfAborted();
|
|
1327
|
+
const selected = models.resolve(key);
|
|
1328
|
+
try {
|
|
1329
|
+
const result = await generateText({
|
|
1330
|
+
model: wrapInvocationModel(writer, selected, step),
|
|
1331
|
+
prompt: input.prompt,
|
|
1332
|
+
...input.settings,
|
|
1333
|
+
maxRetries: 0,
|
|
1334
|
+
abortSignal
|
|
1335
|
+
});
|
|
1336
|
+
return result.text;
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
if (!hasProviderOrigin(error) || abortSignal.aborted || step === input.models.length - 1)
|
|
1339
|
+
throw error;
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
throw new Error("No completion model attempt available");
|
|
1343
|
+
}
|
|
1271
1344
|
function createModelInvocationLedger(config) {
|
|
1272
1345
|
const { store } = config;
|
|
1273
1346
|
const appendOnce = store.appendEventOnce?.bind(store);
|
|
@@ -1315,6 +1388,7 @@ function createModelInvocationLedger(config) {
|
|
|
1315
1388
|
process: executingProcess,
|
|
1316
1389
|
payloads,
|
|
1317
1390
|
onAttempt: config.onAttempt,
|
|
1391
|
+
receiptErrors: [],
|
|
1318
1392
|
async append(record) {
|
|
1319
1393
|
const parsed = ModelInvocationRecordSchema.parse(record);
|
|
1320
1394
|
await store.appendEvent({
|
|
@@ -1349,35 +1423,31 @@ function createModelInvocationLedger(config) {
|
|
|
1349
1423
|
return { invocationId, outcome: "duplicate" };
|
|
1350
1424
|
const deadline = AbortSignal.timeout(input.timeoutMs);
|
|
1351
1425
|
const abortSignal = signal ? AbortSignal.any([signal, deadline]) : deadline;
|
|
1426
|
+
let text;
|
|
1352
1427
|
try {
|
|
1353
|
-
|
|
1354
|
-
abortSignal.throwIfAborted();
|
|
1355
|
-
const selected = config.models.resolve(key);
|
|
1356
|
-
try {
|
|
1357
|
-
const result = await generateText({
|
|
1358
|
-
model: wrapInvocationModel(writer, selected, step),
|
|
1359
|
-
prompt: input.prompt,
|
|
1360
|
-
...input.settings,
|
|
1361
|
-
maxRetries: 0,
|
|
1362
|
-
abortSignal
|
|
1363
|
-
});
|
|
1364
|
-
await finish(writer, "succeeded");
|
|
1365
|
-
return { invocationId, outcome: "succeeded", text: result.text };
|
|
1366
|
-
} catch (error) {
|
|
1367
|
-
if (!hasProviderOrigin(error) || abortSignal.aborted || step === input.models.length - 1)
|
|
1368
|
-
throw error;
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
throw new Error("No completion model attempt available");
|
|
1428
|
+
text = await generateWithFallback(config.models, writer, input, abortSignal);
|
|
1372
1429
|
} catch (error) {
|
|
1373
1430
|
const status = abortSignal.aborted ? "cancelled" : "failed";
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1431
|
+
try {
|
|
1432
|
+
const artifact = await payloads.write(writer.identity, { error });
|
|
1433
|
+
await finish(writer, status, {
|
|
1434
|
+
artifactId: artifact.artifactId,
|
|
1435
|
+
payloadSha256: artifact.sha256
|
|
1436
|
+
});
|
|
1437
|
+
} catch (receiptError) {
|
|
1438
|
+
writer.receiptErrors.push(receiptError);
|
|
1439
|
+
await finish(writer, status).catch((error2) => {
|
|
1440
|
+
writer.receiptErrors.push(error2);
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
return withReceipt({ invocationId, outcome: status }, writer);
|
|
1444
|
+
}
|
|
1445
|
+
try {
|
|
1446
|
+
await finish(writer, "succeeded");
|
|
1447
|
+
} catch (receiptError) {
|
|
1448
|
+
writer.receiptErrors.push(receiptError);
|
|
1380
1449
|
}
|
|
1450
|
+
return withReceipt({ invocationId, outcome: "succeeded", text }, writer);
|
|
1381
1451
|
},
|
|
1382
1452
|
async read(rawInput, context) {
|
|
1383
1453
|
const input = InvocationReadInputSchema.parse(rawInput);
|
|
@@ -2751,14 +2821,14 @@ async function* ownedProviderStream(input) {
|
|
|
2751
2821
|
}
|
|
2752
2822
|
|
|
2753
2823
|
// src/agent-runtime/run-provider-ledger.ts
|
|
2754
|
-
import { createHash as
|
|
2824
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
2755
2825
|
function withoutBinaryParts(message) {
|
|
2756
2826
|
const reference = (value) => {
|
|
2757
2827
|
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
2758
2828
|
const bytes = value instanceof ArrayBuffer ? new Uint8Array(value) : value;
|
|
2759
2829
|
return {
|
|
2760
2830
|
binary: true,
|
|
2761
|
-
sha256:
|
|
2831
|
+
sha256: createHash3("sha256").update(bytes).digest("hex"),
|
|
2762
2832
|
bytes: bytes.byteLength
|
|
2763
2833
|
};
|
|
2764
2834
|
}
|
|
@@ -2813,7 +2883,7 @@ async function createRunProviderLedger(input) {
|
|
|
2813
2883
|
const messageShas = [];
|
|
2814
2884
|
for (const message of request.messages) {
|
|
2815
2885
|
const body = canonicalAgentJson(JSON.parse(JSON.stringify(withoutBinaryParts(message))));
|
|
2816
|
-
const sha256 =
|
|
2886
|
+
const sha256 = createHash3("sha256").update(body).digest("hex");
|
|
2817
2887
|
messageShas.push(sha256);
|
|
2818
2888
|
if (knownMessageShas.has(sha256))
|
|
2819
2889
|
continue;
|
|
@@ -2824,8 +2894,8 @@ async function createRunProviderLedger(input) {
|
|
|
2824
2894
|
});
|
|
2825
2895
|
knownMessageShas.add(sha256);
|
|
2826
2896
|
}
|
|
2827
|
-
const instructionsSha256 =
|
|
2828
|
-
const bodySha256 =
|
|
2897
|
+
const instructionsSha256 = createHash3("sha256").update(typeof request.instructions === "string" ? request.instructions : canonicalAgentJson(JSON.parse(JSON.stringify(withoutBinaryParts(request.instructions))))).digest("hex");
|
|
2898
|
+
const bodySha256 = createHash3("sha256").update(instructionsSha256).update(messageShas.join(",")).digest("hex");
|
|
2829
2899
|
await store.appendEvent({
|
|
2830
2900
|
conversationId: state.run.conversationId,
|
|
2831
2901
|
kind: "provider/request",
|
|
@@ -3596,7 +3666,7 @@ async function commitRunTerminal(execution) {
|
|
|
3596
3666
|
}
|
|
3597
3667
|
|
|
3598
3668
|
// src/agent-runtime/run-prompt-history.ts
|
|
3599
|
-
import { createHash as
|
|
3669
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
3600
3670
|
function snapshotForRunPrompt(snapshot, runId) {
|
|
3601
3671
|
const runIndex = snapshot.runs.findIndex((candidate) => candidate.id === runId);
|
|
3602
3672
|
if (runIndex < 0)
|
|
@@ -3615,7 +3685,7 @@ var USER_INSTRUCTION_SEED_KEY = "prompt.user-instructions";
|
|
|
3615
3685
|
function seededInstructionMessage(input) {
|
|
3616
3686
|
return AgentMessageSchema.parse({
|
|
3617
3687
|
schemaVersion: 1,
|
|
3618
|
-
id:
|
|
3688
|
+
id: createHash4("sha256").update(`${input.conversationId}\x00${input.seedKey}\x00${input.index}`).digest("hex"),
|
|
3619
3689
|
conversationId: input.conversationId,
|
|
3620
3690
|
role: "user",
|
|
3621
3691
|
status: "committed",
|
|
@@ -2365,11 +2365,20 @@ function bindRealtimeServer(contract, handle, options = {}) {
|
|
|
2365
2365
|
onRejected: options.onRejected,
|
|
2366
2366
|
logger: options.logger
|
|
2367
2367
|
});
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2368
|
+
const failed = (error) => {
|
|
2369
|
+
try {
|
|
2370
|
+
raw.disconnect(true);
|
|
2371
|
+
} catch {}
|
|
2372
|
+
if (options.logger)
|
|
2373
|
+
options.logger.error("Realtime connection handler failed", { error });
|
|
2374
|
+
else
|
|
2375
|
+
console.error("[stitchkit] realtime connection handler failed", error);
|
|
2376
|
+
};
|
|
2377
|
+
try {
|
|
2378
|
+
Promise.resolve(handler({ raw, events, to: (room) => outbound(raw.to(room)) })).catch(failed);
|
|
2379
|
+
} catch (error) {
|
|
2380
|
+
failed(error);
|
|
2381
|
+
}
|
|
2373
2382
|
};
|
|
2374
2383
|
handle.io.on("connection", listener);
|
|
2375
2384
|
return () => {
|
|
@@ -429,27 +429,38 @@ async function importConversation(driver, bytes) {
|
|
|
429
429
|
|
|
430
430
|
// src/agent-runtime/store-event-once.ts
|
|
431
431
|
import { createHash } from "node:crypto";
|
|
432
|
-
function
|
|
433
|
-
const parsed = AppendAgentStoreEventSchema.parse(input);
|
|
432
|
+
function eventOnceId(conversationId, kind, key) {
|
|
434
433
|
if (!key)
|
|
435
434
|
throw new TypeError("Event admission key must not be empty");
|
|
436
|
-
|
|
435
|
+
return createHash("sha256").update(JSON.stringify([conversationId, kind, key])).digest("hex");
|
|
436
|
+
}
|
|
437
|
+
async function findEvent(driver, transaction, conversationId, eventId) {
|
|
438
|
+
if (driver.events.find)
|
|
439
|
+
return driver.events.find(transaction, { conversationId, eventId });
|
|
440
|
+
let fromSeq;
|
|
441
|
+
do {
|
|
442
|
+
const page = await driver.events.list(transaction, {
|
|
443
|
+
conversationId,
|
|
444
|
+
fromSeq,
|
|
445
|
+
limit: 1e4
|
|
446
|
+
});
|
|
447
|
+
const found = page.items.find((event) => event.eventId === eventId);
|
|
448
|
+
if (found)
|
|
449
|
+
return found;
|
|
450
|
+
fromSeq = page.nextSeq;
|
|
451
|
+
} while (fromSeq !== undefined);
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
function appendEventOnce(driver, input, key) {
|
|
455
|
+
const parsed = AppendAgentStoreEventSchema.parse(input);
|
|
456
|
+
const eventId = eventOnceId(parsed.conversationId, parsed.kind, key);
|
|
437
457
|
return driver.transaction(async (transaction) => {
|
|
438
458
|
if (await driver.conversations?.isPurged(transaction, parsed.conversationId)) {
|
|
439
459
|
throw new AgentConversationPurgedError;
|
|
440
460
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
conversationId: parsed.conversationId,
|
|
445
|
-
fromSeq,
|
|
446
|
-
limit: 1e4
|
|
447
|
-
});
|
|
448
|
-
const previous = page.items.find((event2) => event2.eventId === eventId);
|
|
449
|
-
if (previous)
|
|
450
|
-
return { outcome: "duplicate", event: previous };
|
|
451
|
-
fromSeq = page.nextSeq;
|
|
452
|
-
} while (fromSeq !== undefined);
|
|
461
|
+
const previous = await findEvent(driver, transaction, parsed.conversationId, eventId);
|
|
462
|
+
if (previous)
|
|
463
|
+
return { outcome: "duplicate", event: previous };
|
|
453
464
|
const event = await driver.events.append(transaction, {
|
|
454
465
|
...agentStoreEventDraft(parsed),
|
|
455
466
|
eventId
|
|
@@ -457,6 +468,10 @@ function appendEventOnce(driver, input, key) {
|
|
|
457
468
|
return { outcome: "applied", event };
|
|
458
469
|
});
|
|
459
470
|
}
|
|
471
|
+
function findEventOnce(driver, input) {
|
|
472
|
+
const eventId = eventOnceId(input.conversationId, input.kind, input.key);
|
|
473
|
+
return driver.transaction((transaction) => findEvent(driver, transaction, input.conversationId, eventId), { access: "read" });
|
|
474
|
+
}
|
|
460
475
|
|
|
461
476
|
// src/agent-runtime/store-mutate.ts
|
|
462
477
|
import { z as z4 } from "zod";
|
|
@@ -1011,6 +1026,7 @@ function createAgentRuntimeStore(driver) {
|
|
|
1011
1026
|
loadRun: (input) => loadRun(driver, input),
|
|
1012
1027
|
listActiveRuns: (conversationId) => listActiveRuns(driver, conversationId),
|
|
1013
1028
|
appendEventOnce: (input, key) => appendEventOnce(driver, input, key),
|
|
1029
|
+
findEventOnce: (input) => findEventOnce(driver, input),
|
|
1014
1030
|
appendEvent: (input) => appendEvent(driver, input),
|
|
1015
1031
|
readEvents: (input) => readEvents(driver, input),
|
|
1016
1032
|
exportConversation: (conversationId) => exportConversation(driver, conversationId),
|