stitchkit 0.57.0 → 0.59.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/dist/agent-runtime/compaction.d.ts +3 -0
- package/dist/agent-runtime/compaction.d.ts.map +1 -1
- package/dist/agent-runtime/events.d.ts +40 -0
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/history.d.ts +13 -0
- package/dist/agent-runtime/history.d.ts.map +1 -1
- package/dist/agent-runtime/managed-tools.d.ts +1 -0
- package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
- package/dist/agent-runtime/models.d.ts +39 -3
- package/dist/agent-runtime/models.d.ts.map +1 -1
- package/dist/agent-runtime/observability.d.ts +3 -0
- package/dist/agent-runtime/observability.d.ts.map +1 -1
- package/dist/agent-runtime/prompt.d.ts +21 -0
- package/dist/agent-runtime/prompt.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +9 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +2 -0
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +202 -24
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +229 -0
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime/testing.d.ts +10 -1
- package/dist/agent-runtime/testing.d.ts.map +1 -1
- package/dist/agent-runtime.d.ts +5 -5
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +629 -200
- package/dist/{index-1f4fcj0b.js → index-vtjgx3vv.js} +1 -0
- package/dist/testing/agent-store-conformance.d.ts.map +1 -1
- package/dist/testing.d.ts +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +198 -4
- package/llms-full.txt +156 -36
- package/package.json +1 -1
package/dist/agent-runtime.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
AgentToolResultPartSchema,
|
|
30
30
|
AgentUsageSchema,
|
|
31
31
|
AgentUsageValueSchema
|
|
32
|
-
} from "./index-
|
|
32
|
+
} from "./index-vtjgx3vv.js";
|
|
33
33
|
import"./index-6djpbnda.js";
|
|
34
34
|
import"./index-cby4ar3v.js";
|
|
35
35
|
import {
|
|
@@ -86,54 +86,66 @@ function eligibleForCompaction(messages, keepRecentTurns) {
|
|
|
86
86
|
const eligibleCount = Math.max(0, completeTurns.length - keepRecentTurns);
|
|
87
87
|
return completeTurns.slice(0, eligibleCount).flatMap((turn) => turn.messages);
|
|
88
88
|
}
|
|
89
|
-
function mutationSnapshot(result, fallback) {
|
|
89
|
+
function mutationSnapshot(result, fallback, attempts) {
|
|
90
90
|
if (result.outcome === "applied" || result.outcome === "duplicate") {
|
|
91
|
-
return { outcome: "applied", snapshot: result.snapshot };
|
|
91
|
+
return { outcome: "applied", snapshot: result.snapshot, attempts };
|
|
92
92
|
}
|
|
93
|
-
return { outcome: result.outcome, snapshot: fallback };
|
|
93
|
+
return { outcome: result.outcome, snapshot: fallback, attempts };
|
|
94
94
|
}
|
|
95
95
|
function structuredCompaction(config) {
|
|
96
96
|
if (!Number.isSafeInteger(config.keepRecentTurns) || config.keepRecentTurns < 1) {
|
|
97
97
|
throw new TypeError("keepRecentTurns must be a positive safe integer");
|
|
98
98
|
}
|
|
99
|
+
const maxAttempts = config.maxAttempts ?? 1;
|
|
100
|
+
if (!Number.isSafeInteger(maxAttempts) || maxAttempts < 1) {
|
|
101
|
+
throw new TypeError("maxAttempts must be a positive safe integer");
|
|
102
|
+
}
|
|
99
103
|
return async (input) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
summary
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
104
|
+
let lastSnapshot = await input.store.loadSnapshot(input.conversationId);
|
|
105
|
+
for (let attempt = 1;attempt <= maxAttempts; attempt += 1) {
|
|
106
|
+
const snapshot = attempt === 1 ? lastSnapshot : await input.store.loadSnapshot(input.conversationId);
|
|
107
|
+
lastSnapshot = snapshot;
|
|
108
|
+
if (!await config.threshold(snapshot)) {
|
|
109
|
+
return { outcome: "not_needed", snapshot, attempts: attempt };
|
|
110
|
+
}
|
|
111
|
+
const eligibleMessages = eligibleForCompaction(snapshot.messages, config.keepRecentTurns);
|
|
112
|
+
if (eligibleMessages.length === 0) {
|
|
113
|
+
return { outcome: "nothing_eligible", snapshot, attempts: attempt };
|
|
114
|
+
}
|
|
115
|
+
const leadingSummary = snapshot.messages[0]?.role === "summary" ? snapshot.messages[0] : undefined;
|
|
116
|
+
const previousSummary = leadingSummary && config.readPreviousSummary ? config.schema.parse(config.readPreviousSummary(leadingSummary)) : attempt === 1 ? input.previousSummary : undefined;
|
|
117
|
+
const rawSummary = await config.summarize({
|
|
118
|
+
conversationId: input.conversationId,
|
|
119
|
+
snapshot,
|
|
120
|
+
eligibleMessages,
|
|
121
|
+
...previousSummary !== undefined && { previousSummary },
|
|
122
|
+
signal: input.signal
|
|
123
|
+
});
|
|
124
|
+
const summary = config.schema.parse(rawSummary);
|
|
125
|
+
if (input.signal.aborted)
|
|
126
|
+
throw input.signal.reason;
|
|
127
|
+
const summaryMessage = config.createSummaryMessage({
|
|
128
|
+
conversationId: input.conversationId,
|
|
129
|
+
summary,
|
|
130
|
+
compactedMessages: eligibleMessages
|
|
131
|
+
});
|
|
132
|
+
if (summaryMessage.role !== "summary" || summaryMessage.status !== "committed") {
|
|
133
|
+
throw new TypeError("Compaction summary must be one committed summary message");
|
|
134
|
+
}
|
|
135
|
+
const applied = await input.store.replaceCompactedRange({
|
|
136
|
+
conversationId: input.conversationId,
|
|
137
|
+
expectedVersion: snapshot.version,
|
|
138
|
+
replacedMessageIds: [
|
|
139
|
+
...leadingSummary ? [leadingSummary.id] : [],
|
|
140
|
+
...eligibleMessages.map((message) => message.id)
|
|
141
|
+
],
|
|
142
|
+
summary: summaryMessage
|
|
143
|
+
});
|
|
144
|
+
if (applied.outcome !== "conflict" || attempt === maxAttempts) {
|
|
145
|
+
return mutationSnapshot(applied, snapshot, attempt);
|
|
146
|
+
}
|
|
125
147
|
}
|
|
126
|
-
|
|
127
|
-
const applied = await input.store.replaceCompactedRange({
|
|
128
|
-
conversationId: input.conversationId,
|
|
129
|
-
expectedVersion: snapshot.version,
|
|
130
|
-
replacedMessageIds: [
|
|
131
|
-
...previousSummaryMessage ? [previousSummaryMessage.id] : [],
|
|
132
|
-
...eligibleMessages.map((message) => message.id)
|
|
133
|
-
],
|
|
134
|
-
summary: summaryMessage
|
|
135
|
-
});
|
|
136
|
-
return mutationSnapshot(applied, snapshot);
|
|
148
|
+
return { outcome: "conflict", snapshot: lastSnapshot, attempts: maxAttempts };
|
|
137
149
|
};
|
|
138
150
|
}
|
|
139
151
|
// src/agent-runtime/coordinator.ts
|
|
@@ -335,6 +347,63 @@ var AgentRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
|
335
347
|
AgentToolStatusEventSchema,
|
|
336
348
|
AgentTerminalEventSchema
|
|
337
349
|
]);
|
|
350
|
+
var AgentRuntimeEventCursorSchema = z.object({
|
|
351
|
+
snapshotVersion: AgentRecordVersionSchema.optional(),
|
|
352
|
+
durableEventIds: z.array(AgentRecordIdSchema).optional(),
|
|
353
|
+
runtimeEpoch: z.string().min(1).optional(),
|
|
354
|
+
sequence: z.int().nonnegative().optional()
|
|
355
|
+
});
|
|
356
|
+
function isDurableEvent(event) {
|
|
357
|
+
return event.type === "admission" || event.type === "assistant-checkpoint" || event.type === "run-state" || event.type === "terminal";
|
|
358
|
+
}
|
|
359
|
+
function advanceAgentRuntimeEventCursor(rawCursor, event) {
|
|
360
|
+
const cursor = AgentRuntimeEventCursorSchema.parse(rawCursor);
|
|
361
|
+
if (isDurableEvent(event)) {
|
|
362
|
+
const previous = cursor.snapshotVersion;
|
|
363
|
+
const durableEventIds = previous === event.snapshotVersion ? cursor.durableEventIds ?? [] : [];
|
|
364
|
+
if (previous !== undefined && event.snapshotVersion < previous || durableEventIds.includes(event.eventId)) {
|
|
365
|
+
return { status: "duplicate", cursor };
|
|
366
|
+
}
|
|
367
|
+
return {
|
|
368
|
+
status: previous !== undefined && event.snapshotVersion > previous + 1 ? "gap" : "accepted",
|
|
369
|
+
cursor: {
|
|
370
|
+
...cursor,
|
|
371
|
+
snapshotVersion: event.snapshotVersion,
|
|
372
|
+
durableEventIds: [...durableEventIds, event.eventId]
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
const previousSequence = cursor.runtimeEpoch === event.runtimeEpoch ? cursor.sequence : undefined;
|
|
377
|
+
if (previousSequence !== undefined && event.sequence <= previousSequence) {
|
|
378
|
+
return { status: "duplicate", cursor };
|
|
379
|
+
}
|
|
380
|
+
return {
|
|
381
|
+
status: previousSequence !== undefined && event.sequence > previousSequence + 1 ? "gap" : "accepted",
|
|
382
|
+
cursor: { ...cursor, runtimeEpoch: event.runtimeEpoch, sequence: event.sequence }
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function createAgentRuntimeEventSink(config) {
|
|
386
|
+
const manager = createBoundedSinkManager({
|
|
387
|
+
write: config.write,
|
|
388
|
+
...config.maxPending !== undefined && { maxPending: config.maxPending },
|
|
389
|
+
...config.onSinkError && { onSinkError: config.onSinkError },
|
|
390
|
+
...config.onDrop && { onDrop: config.onDrop }
|
|
391
|
+
});
|
|
392
|
+
return {
|
|
393
|
+
publish(rawEvent) {
|
|
394
|
+
const event = AgentRuntimeEventSchema.parse(rawEvent);
|
|
395
|
+
const projected = config.project?.(event) ?? (config.project ? undefined : event);
|
|
396
|
+
if (projected)
|
|
397
|
+
manager.submit(() => AgentRuntimeEventSchema.parse(projected));
|
|
398
|
+
},
|
|
399
|
+
flush: () => manager.flush(),
|
|
400
|
+
getStatus: () => manager.getStatus(),
|
|
401
|
+
close: () => manager.close()
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
function agentDurableEventId(type, runId, snapshotVersion) {
|
|
405
|
+
return `${runId}:${type}:${snapshotVersion}`;
|
|
406
|
+
}
|
|
338
407
|
// src/agent-runtime/history.ts
|
|
339
408
|
import { modelMessageSchema } from "ai";
|
|
340
409
|
function providerOptions(envelope) {
|
|
@@ -416,26 +485,75 @@ function assistantMessages(message) {
|
|
|
416
485
|
}
|
|
417
486
|
return messages;
|
|
418
487
|
}
|
|
419
|
-
|
|
488
|
+
function completeToolChronology(message) {
|
|
489
|
+
const calls = new Set(message.parts.filter((part) => part.type === "tool-call").map((part) => part.callId));
|
|
490
|
+
const results = new Set(message.parts.filter((part) => part.type === "tool-result").map((part) => part.callId));
|
|
491
|
+
return [...calls].every((callId) => results.has(callId)) && [...results].every((callId) => calls.has(callId));
|
|
492
|
+
}
|
|
493
|
+
async function projectAgentHistoryDetailed(messages, options = {}) {
|
|
420
494
|
const projected = [];
|
|
495
|
+
const decisions = [];
|
|
496
|
+
let observedUser = false;
|
|
421
497
|
for (const message of messages) {
|
|
422
|
-
if (message.status === "streaming" || message.status === "failed")
|
|
498
|
+
if (message.status === "streaming" || message.status === "failed") {
|
|
499
|
+
decisions.push({ messageId: message.id, action: "omitted", reason: "draft-or-failed" });
|
|
423
500
|
continue;
|
|
501
|
+
}
|
|
424
502
|
if (message.role === "user") {
|
|
425
503
|
const user = await userMessage(message, options);
|
|
426
|
-
|
|
504
|
+
observedUser = true;
|
|
505
|
+
if (user) {
|
|
427
506
|
projected.push(user);
|
|
507
|
+
decisions.push({ messageId: message.id, action: "projected", reason: "projected" });
|
|
508
|
+
} else {
|
|
509
|
+
decisions.push({ messageId: message.id, action: "omitted", reason: "empty" });
|
|
510
|
+
}
|
|
428
511
|
continue;
|
|
429
512
|
}
|
|
430
513
|
if (message.role === "system" || message.role === "summary") {
|
|
431
514
|
const content = textContent(message.parts);
|
|
432
|
-
if (content)
|
|
515
|
+
if (content) {
|
|
433
516
|
projected.push(modelMessageSchema.parse({ role: "system", content }));
|
|
517
|
+
decisions.push({ messageId: message.id, action: "projected", reason: "projected" });
|
|
518
|
+
} else {
|
|
519
|
+
decisions.push({ messageId: message.id, action: "omitted", reason: "empty" });
|
|
520
|
+
}
|
|
434
521
|
continue;
|
|
435
522
|
}
|
|
436
|
-
|
|
523
|
+
if (!observedUser && options.leadingAssistant !== "allow") {
|
|
524
|
+
if (options.leadingAssistant === "error") {
|
|
525
|
+
throw new Error(`Assistant message ${message.id} precedes the first user message`);
|
|
526
|
+
}
|
|
527
|
+
decisions.push({
|
|
528
|
+
messageId: message.id,
|
|
529
|
+
action: "omitted",
|
|
530
|
+
reason: "leading-assistant"
|
|
531
|
+
});
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (!completeToolChronology(message)) {
|
|
535
|
+
if (options.incompleteToolTurn === "error") {
|
|
536
|
+
throw new Error(`Assistant message ${message.id} has incomplete tool chronology`);
|
|
537
|
+
}
|
|
538
|
+
decisions.push({
|
|
539
|
+
messageId: message.id,
|
|
540
|
+
action: "omitted",
|
|
541
|
+
reason: "incomplete-tool-turn"
|
|
542
|
+
});
|
|
543
|
+
continue;
|
|
544
|
+
}
|
|
545
|
+
const assistant = assistantMessages(message);
|
|
546
|
+
projected.push(...assistant);
|
|
547
|
+
decisions.push({
|
|
548
|
+
messageId: message.id,
|
|
549
|
+
action: assistant.length > 0 ? "projected" : "omitted",
|
|
550
|
+
reason: assistant.length > 0 ? "projected" : "empty"
|
|
551
|
+
});
|
|
437
552
|
}
|
|
438
|
-
return projected;
|
|
553
|
+
return { messages: projected, decisions };
|
|
554
|
+
}
|
|
555
|
+
async function projectAgentHistory(messages, options = {}) {
|
|
556
|
+
return [...(await projectAgentHistoryDetailed(messages, options)).messages];
|
|
439
557
|
}
|
|
440
558
|
// src/agent-runtime/managed-tools.ts
|
|
441
559
|
async function assertFence(config, input) {
|
|
@@ -469,7 +587,14 @@ var AgentModelDescriptorSchema = z2.object({
|
|
|
469
587
|
contextWindow: z2.int().positive(),
|
|
470
588
|
capabilities: z2.array(AgentModelCapabilitySchema),
|
|
471
589
|
observedAt: z2.iso.datetime({ offset: true }).optional(),
|
|
472
|
-
source: z2.string().min(1).optional()
|
|
590
|
+
source: z2.string().min(1).optional(),
|
|
591
|
+
availability: z2.enum(["available", "unavailable"]).optional()
|
|
592
|
+
});
|
|
593
|
+
var AgentModelRegistrySnapshotSchema = z2.object({
|
|
594
|
+
schemaVersion: z2.literal(1),
|
|
595
|
+
source: z2.string().min(1),
|
|
596
|
+
observedAt: z2.iso.datetime({ offset: true }),
|
|
597
|
+
models: z2.record(z2.string().min(1), AgentModelDescriptorSchema)
|
|
473
598
|
});
|
|
474
599
|
function defineModelRegistry(config) {
|
|
475
600
|
const descriptors = new Map;
|
|
@@ -488,15 +613,26 @@ function defineModelRegistry(config) {
|
|
|
488
613
|
const available = new Set(descriptor(key).capabilities);
|
|
489
614
|
return capabilities.every((capability) => available.has(capability));
|
|
490
615
|
};
|
|
616
|
+
const preflight = (key, required = []) => {
|
|
617
|
+
const selected = descriptor(key);
|
|
618
|
+
if (selected.availability === "unavailable") {
|
|
619
|
+
throw new Error(`Agent model ${key} is unavailable`);
|
|
620
|
+
}
|
|
621
|
+
if (!supports(key, required)) {
|
|
622
|
+
throw new Error(`Agent model ${key} does not satisfy required capabilities`);
|
|
623
|
+
}
|
|
624
|
+
if (!config.providers[selected.provider]) {
|
|
625
|
+
throw new Error(`Unknown agent model provider: ${selected.provider}`);
|
|
626
|
+
}
|
|
627
|
+
return selected;
|
|
628
|
+
};
|
|
491
629
|
return {
|
|
492
630
|
keys: () => [...descriptors.keys()],
|
|
493
631
|
descriptor,
|
|
494
632
|
supports,
|
|
633
|
+
preflight,
|
|
495
634
|
resolve(key, required = []) {
|
|
496
|
-
const selected =
|
|
497
|
-
if (!supports(key, required)) {
|
|
498
|
-
throw new Error(`Agent model ${key} does not satisfy required capabilities`);
|
|
499
|
-
}
|
|
635
|
+
const selected = preflight(key, required);
|
|
500
636
|
const provider = config.providers[selected.provider];
|
|
501
637
|
if (!provider)
|
|
502
638
|
throw new Error(`Unknown agent model provider: ${selected.provider}`);
|
|
@@ -505,9 +641,29 @@ function defineModelRegistry(config) {
|
|
|
505
641
|
model: provider.create(selected.modelId),
|
|
506
642
|
...provider.normalizeUsage && { normalizeUsage: provider.normalizeUsage }
|
|
507
643
|
};
|
|
644
|
+
},
|
|
645
|
+
snapshot(input) {
|
|
646
|
+
return AgentModelRegistrySnapshotSchema.parse({
|
|
647
|
+
schemaVersion: 1,
|
|
648
|
+
source: input.source,
|
|
649
|
+
observedAt: input.observedAt,
|
|
650
|
+
models: Object.fromEntries(descriptors.entries())
|
|
651
|
+
});
|
|
508
652
|
}
|
|
509
653
|
};
|
|
510
654
|
}
|
|
655
|
+
function validateAgentModelSnapshot(input, policy) {
|
|
656
|
+
if (!Number.isSafeInteger(policy.maxAgeMs) || policy.maxAgeMs < 0) {
|
|
657
|
+
throw new TypeError("maxAgeMs must be a non-negative safe integer");
|
|
658
|
+
}
|
|
659
|
+
const snapshot = AgentModelRegistrySnapshotSchema.parse(input);
|
|
660
|
+
const now = policy.now?.() ?? new Date;
|
|
661
|
+
const age = now.getTime() - new Date(snapshot.observedAt).getTime();
|
|
662
|
+
if (age < 0 || age > policy.maxAgeMs) {
|
|
663
|
+
throw new Error(`Agent model snapshot from ${snapshot.source} is stale`);
|
|
664
|
+
}
|
|
665
|
+
return snapshot;
|
|
666
|
+
}
|
|
511
667
|
// src/agent-runtime/observability.ts
|
|
512
668
|
import { z as z3 } from "zod";
|
|
513
669
|
var AgentRunEventSchema = z3.object({
|
|
@@ -538,13 +694,18 @@ function createAgentObservability(config) {
|
|
|
538
694
|
...config.onSinkError && { onSinkError: config.onSinkError },
|
|
539
695
|
...config.onDrop && { onDrop: config.onDrop }
|
|
540
696
|
});
|
|
697
|
+
const emitted = new Set;
|
|
541
698
|
return {
|
|
542
699
|
rootTrace(parent) {
|
|
543
700
|
const trace = parent ? childSpan(parent) : createTraceContext();
|
|
544
701
|
return trace;
|
|
545
702
|
},
|
|
546
703
|
emit(rawEvent) {
|
|
547
|
-
|
|
704
|
+
const parsed = AgentRunEventSchema.parse(rawEvent);
|
|
705
|
+
if ((config.deduplicate ?? true) && emitted.has(parsed.eventId))
|
|
706
|
+
return;
|
|
707
|
+
emitted.add(parsed.eventId);
|
|
708
|
+
manager.submit(() => config.includeInternalCause ? parsed : AgentRunEventSchema.omit({ internalCause: true }).parse(parsed));
|
|
548
709
|
},
|
|
549
710
|
flush: () => manager.flush(),
|
|
550
711
|
getStatus: () => manager.getStatus(),
|
|
@@ -557,6 +718,110 @@ var AgentTokenCountSchema = z4.object({
|
|
|
557
718
|
value: z4.int().nonnegative().optional(),
|
|
558
719
|
provenance: z4.enum(["measured", "estimated", "unavailable"])
|
|
559
720
|
});
|
|
721
|
+
function completeTurn(messages) {
|
|
722
|
+
if (messages[0]?.role !== "user")
|
|
723
|
+
return false;
|
|
724
|
+
const assistant = messages.find((message) => message.role === "assistant");
|
|
725
|
+
if (assistant?.status !== "completed")
|
|
726
|
+
return false;
|
|
727
|
+
const calls = new Set(assistant.parts.filter((part) => part.type === "tool-call").map((part) => part.callId));
|
|
728
|
+
const results = new Set(assistant.parts.filter((part) => part.type === "tool-result").map((part) => part.callId));
|
|
729
|
+
return [...calls].every((callId) => results.has(callId)) && [...results].every((callId) => calls.has(callId));
|
|
730
|
+
}
|
|
731
|
+
function budgetTurns(messages) {
|
|
732
|
+
const turns = [];
|
|
733
|
+
let current = [];
|
|
734
|
+
const flush = () => {
|
|
735
|
+
if (current.length === 0)
|
|
736
|
+
return;
|
|
737
|
+
turns.push({ messages: current, complete: completeTurn(current), protectedSystem: false });
|
|
738
|
+
current = [];
|
|
739
|
+
};
|
|
740
|
+
for (const message of messages) {
|
|
741
|
+
if (message.role === "system" || message.role === "summary") {
|
|
742
|
+
flush();
|
|
743
|
+
turns.push({ messages: [message], complete: true, protectedSystem: true });
|
|
744
|
+
continue;
|
|
745
|
+
}
|
|
746
|
+
if (message.role === "user")
|
|
747
|
+
flush();
|
|
748
|
+
current.push(message);
|
|
749
|
+
}
|
|
750
|
+
flush();
|
|
751
|
+
return turns;
|
|
752
|
+
}
|
|
753
|
+
async function selectAgentHistory(options) {
|
|
754
|
+
if (!Number.isSafeInteger(options.availableTokens) || options.availableTokens < 0) {
|
|
755
|
+
throw new TypeError("availableTokens must be a non-negative safe integer");
|
|
756
|
+
}
|
|
757
|
+
const keepRecentTurns = options.keepRecentTurns ?? 1;
|
|
758
|
+
if (!Number.isSafeInteger(keepRecentTurns) || keepRecentTurns < 0) {
|
|
759
|
+
throw new TypeError("keepRecentTurns must be a non-negative safe integer");
|
|
760
|
+
}
|
|
761
|
+
const counts = new Map;
|
|
762
|
+
let total = 0;
|
|
763
|
+
let estimated = false;
|
|
764
|
+
for (const message of options.messages) {
|
|
765
|
+
const count = AgentTokenCountSchema.parse(await options.estimateMessage(message));
|
|
766
|
+
counts.set(message.id, count);
|
|
767
|
+
const value = knownValue(count);
|
|
768
|
+
if (value === undefined) {
|
|
769
|
+
return {
|
|
770
|
+
messages: [...options.messages],
|
|
771
|
+
decisions: options.messages.map((candidate) => ({
|
|
772
|
+
messageId: candidate.id,
|
|
773
|
+
action: "kept",
|
|
774
|
+
reason: "token-count-unavailable",
|
|
775
|
+
tokens: counts.get(candidate.id) ?? { provenance: "unavailable" }
|
|
776
|
+
})),
|
|
777
|
+
totalTokens: { provenance: "unavailable" },
|
|
778
|
+
outcome: "unavailable"
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
total += value;
|
|
782
|
+
if (count.provenance === "estimated")
|
|
783
|
+
estimated = true;
|
|
784
|
+
}
|
|
785
|
+
const turns = budgetTurns(options.messages);
|
|
786
|
+
const completeIndexes = turns.map((turn, index) => ({ turn, index })).filter(({ turn }) => turn.complete && !turn.protectedSystem).map(({ index }) => index);
|
|
787
|
+
const protectedRecent = new Set(completeIndexes.slice(-keepRecentTurns));
|
|
788
|
+
const removed = new Set;
|
|
789
|
+
for (let index = 0;index < turns.length && total > options.availableTokens; index += 1) {
|
|
790
|
+
const turn = turns[index];
|
|
791
|
+
if (!turn || turn.protectedSystem || !turn.complete || protectedRecent.has(index))
|
|
792
|
+
continue;
|
|
793
|
+
for (const message of turn.messages) {
|
|
794
|
+
removed.add(message.id);
|
|
795
|
+
total -= knownValue(counts.get(message.id) ?? { provenance: "unavailable" }) ?? 0;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
const messages = options.messages.filter((message) => !removed.has(message.id));
|
|
799
|
+
const decisions = options.messages.map((message) => {
|
|
800
|
+
const turnIndex = turns.findIndex((turn2) => turn2.messages.some((item) => item.id === message.id));
|
|
801
|
+
const turn = turns[turnIndex];
|
|
802
|
+
let reason = "within-budget";
|
|
803
|
+
if (removed.has(message.id))
|
|
804
|
+
reason = "oldest-eligible-turn";
|
|
805
|
+
else if (turn?.protectedSystem)
|
|
806
|
+
reason = "protected-system";
|
|
807
|
+
else if (turn && !turn.complete)
|
|
808
|
+
reason = "protected-incomplete-turn";
|
|
809
|
+
else if (protectedRecent.has(turnIndex))
|
|
810
|
+
reason = "protected-recent-turn";
|
|
811
|
+
return {
|
|
812
|
+
messageId: message.id,
|
|
813
|
+
action: removed.has(message.id) ? "removed" : "kept",
|
|
814
|
+
reason,
|
|
815
|
+
tokens: counts.get(message.id) ?? { provenance: "unavailable" }
|
|
816
|
+
};
|
|
817
|
+
});
|
|
818
|
+
return {
|
|
819
|
+
messages,
|
|
820
|
+
decisions,
|
|
821
|
+
totalTokens: { value: total, provenance: estimated ? "estimated" : "measured" },
|
|
822
|
+
outcome: total > options.availableTokens ? "oversized" : removed.size > 0 ? "truncated" : "fits"
|
|
823
|
+
};
|
|
824
|
+
}
|
|
560
825
|
function knownValue(value) {
|
|
561
826
|
return value.provenance === "unavailable" ? undefined : value.value;
|
|
562
827
|
}
|
|
@@ -795,7 +1060,11 @@ function createAgentRuntime(config) {
|
|
|
795
1060
|
const publish = async (event) => {
|
|
796
1061
|
try {
|
|
797
1062
|
await config.publish?.(event);
|
|
798
|
-
} catch {
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
try {
|
|
1065
|
+
await config.onPublishError?.({ event, error });
|
|
1066
|
+
} catch {}
|
|
1067
|
+
}
|
|
799
1068
|
};
|
|
800
1069
|
const executeRun = async (input) => {
|
|
801
1070
|
const queuedSnapshot = await config.store.loadSnapshot(input.acceptedRun.conversationId);
|
|
@@ -809,7 +1078,7 @@ function createAgentRuntime(config) {
|
|
|
809
1078
|
let run = findRun(acquired.runs, input.acceptedRun.id);
|
|
810
1079
|
await publish({
|
|
811
1080
|
type: "run-state",
|
|
812
|
-
eventId:
|
|
1081
|
+
eventId: agentDurableEventId("run-state", run.id, acquired.version),
|
|
813
1082
|
conversationId: run.conversationId,
|
|
814
1083
|
runId: run.id,
|
|
815
1084
|
snapshotVersion: acquired.version,
|
|
@@ -847,6 +1116,7 @@ function createAgentRuntime(config) {
|
|
|
847
1116
|
runId: run.id,
|
|
848
1117
|
expectedRevision: run.revision,
|
|
849
1118
|
ownerId: runtimeEpoch,
|
|
1119
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
850
1120
|
assistant
|
|
851
1121
|
}), "assistant draft");
|
|
852
1122
|
run = findRun(snapshot.runs, run.id);
|
|
@@ -895,6 +1165,7 @@ function createAgentRuntime(config) {
|
|
|
895
1165
|
runId: run.id,
|
|
896
1166
|
expectedRevision: run.revision,
|
|
897
1167
|
ownerId: runtimeEpoch,
|
|
1168
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
898
1169
|
assistant
|
|
899
1170
|
}), "assistant checkpoint");
|
|
900
1171
|
run = findRun(snapshot.runs, run.id);
|
|
@@ -906,7 +1177,7 @@ function createAgentRuntime(config) {
|
|
|
906
1177
|
};
|
|
907
1178
|
await publish({
|
|
908
1179
|
type: "assistant-checkpoint",
|
|
909
|
-
eventId:
|
|
1180
|
+
eventId: agentDurableEventId("assistant-checkpoint", run.id, snapshot.version),
|
|
910
1181
|
conversationId: run.conversationId,
|
|
911
1182
|
runId: run.id,
|
|
912
1183
|
snapshotVersion: snapshot.version,
|
|
@@ -932,6 +1203,8 @@ function createAgentRuntime(config) {
|
|
|
932
1203
|
const currentRun = current.runs.find((candidate) => candidate.id === run.id);
|
|
933
1204
|
if (!currentRun || currentRun.ownerId !== runtimeEpoch)
|
|
934
1205
|
return "stale_run";
|
|
1206
|
+
if (currentRun.fencingToken !== run.fencingToken)
|
|
1207
|
+
return "stale_run";
|
|
935
1208
|
if (currentRun.state === "interrupt_requested")
|
|
936
1209
|
return "run_interrupted";
|
|
937
1210
|
if (currentRun.state !== "running")
|
|
@@ -940,7 +1213,10 @@ function createAgentRuntime(config) {
|
|
|
940
1213
|
};
|
|
941
1214
|
const toolFenceLifecycle = createAgentToolFenceLifecycle({
|
|
942
1215
|
runId: run.id,
|
|
943
|
-
assertCurrent
|
|
1216
|
+
assertCurrent,
|
|
1217
|
+
context: () => ({
|
|
1218
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken }
|
|
1219
|
+
})
|
|
944
1220
|
});
|
|
945
1221
|
const runtimeContext = {
|
|
946
1222
|
context: input.context,
|
|
@@ -1276,6 +1552,7 @@ function createAgentRuntime(config) {
|
|
|
1276
1552
|
runId: run.id,
|
|
1277
1553
|
expectedRevision: run.revision,
|
|
1278
1554
|
ownerId: runtimeEpoch,
|
|
1555
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
1279
1556
|
assistant,
|
|
1280
1557
|
reason: terminalReason,
|
|
1281
1558
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
@@ -1289,7 +1566,7 @@ function createAgentRuntime(config) {
|
|
|
1289
1566
|
};
|
|
1290
1567
|
config.observe?.emit({
|
|
1291
1568
|
schemaVersion: 1,
|
|
1292
|
-
eventId:
|
|
1569
|
+
eventId: agentDurableEventId("terminal", run.id, snapshot.version),
|
|
1293
1570
|
type: "run-terminal",
|
|
1294
1571
|
conversationId: run.conversationId,
|
|
1295
1572
|
runId: run.id,
|
|
@@ -1307,7 +1584,7 @@ function createAgentRuntime(config) {
|
|
|
1307
1584
|
});
|
|
1308
1585
|
await publish({
|
|
1309
1586
|
type: "terminal",
|
|
1310
|
-
eventId:
|
|
1587
|
+
eventId: agentDurableEventId("terminal", run.id, snapshot.version),
|
|
1311
1588
|
conversationId: run.conversationId,
|
|
1312
1589
|
runId: run.id,
|
|
1313
1590
|
snapshotVersion: snapshot.version,
|
|
@@ -1433,6 +1710,10 @@ function createAgentRuntime(config) {
|
|
|
1433
1710
|
await previousAcceptance.catch(() => {
|
|
1434
1711
|
return;
|
|
1435
1712
|
});
|
|
1713
|
+
await config.models.preflight?.({
|
|
1714
|
+
context,
|
|
1715
|
+
conversationId: input.conversationId
|
|
1716
|
+
});
|
|
1436
1717
|
const acceptance = await config.store.acceptInputAndAssignRun({
|
|
1437
1718
|
idempotencyKey: input.idempotencyKey,
|
|
1438
1719
|
input: userMessage2,
|
|
@@ -1443,7 +1724,7 @@ function createAgentRuntime(config) {
|
|
|
1443
1724
|
});
|
|
1444
1725
|
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
1445
1726
|
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
1446
|
-
const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1727
|
+
const acceptedRun = acceptance.outcome === "duplicate" ? acceptance.run : findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1447
1728
|
const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
|
|
1448
1729
|
const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
|
|
1449
1730
|
if (!acceptedInput) {
|
|
@@ -1458,7 +1739,7 @@ function createAgentRuntime(config) {
|
|
|
1458
1739
|
createdAt: acceptedRun.createdAt,
|
|
1459
1740
|
updatedAt: acceptedRun.updatedAt
|
|
1460
1741
|
});
|
|
1461
|
-
const acceptedAssistant = acceptance.outcome === "duplicate" ?
|
|
1742
|
+
const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptance.assistant ?? assistantPlaceholder : assistantPlaceholder;
|
|
1462
1743
|
const admission = {
|
|
1463
1744
|
inputMessageId: acceptedInput.id,
|
|
1464
1745
|
runId: acceptedRun.id,
|
|
@@ -1471,7 +1752,7 @@ function createAgentRuntime(config) {
|
|
|
1471
1752
|
outerAdmission.resolve(admission);
|
|
1472
1753
|
await publish({
|
|
1473
1754
|
type: "admission",
|
|
1474
|
-
eventId:
|
|
1755
|
+
eventId: agentDurableEventId("admission", acceptedRun.id, acceptedSnapshot.version),
|
|
1475
1756
|
conversationId: acceptedRun.conversationId,
|
|
1476
1757
|
runId: acceptedRun.id,
|
|
1477
1758
|
snapshotVersion: acceptedSnapshot.version,
|
|
@@ -1482,7 +1763,7 @@ function createAgentRuntime(config) {
|
|
|
1482
1763
|
});
|
|
1483
1764
|
await publish({
|
|
1484
1765
|
type: "run-state",
|
|
1485
|
-
eventId:
|
|
1766
|
+
eventId: agentDurableEventId("run-state", acceptedRun.id, acceptedSnapshot.version),
|
|
1486
1767
|
conversationId: acceptedRun.conversationId,
|
|
1487
1768
|
runId: acceptedRun.id,
|
|
1488
1769
|
snapshotVersion: acceptedSnapshot.version,
|
|
@@ -1500,9 +1781,9 @@ function createAgentRuntime(config) {
|
|
|
1500
1781
|
}
|
|
1501
1782
|
return;
|
|
1502
1783
|
}
|
|
1503
|
-
const message =
|
|
1784
|
+
const message = acceptance.assistant;
|
|
1504
1785
|
if (!message) {
|
|
1505
|
-
const error = new Error("Duplicate terminal
|
|
1786
|
+
const error = new Error("Duplicate terminal admission has no retained canonical assistant");
|
|
1506
1787
|
outerResult.reject(error);
|
|
1507
1788
|
if (reservation?.shouldSchedule) {
|
|
1508
1789
|
reservation.admission.completion.reject(error);
|
|
@@ -1580,7 +1861,7 @@ function createAgentRuntime(config) {
|
|
|
1580
1861
|
const interruptedRun = findRun(requested.snapshot.runs, input.runId);
|
|
1581
1862
|
await publish({
|
|
1582
1863
|
type: "run-state",
|
|
1583
|
-
eventId:
|
|
1864
|
+
eventId: agentDurableEventId("run-state", interruptedRun.id, requested.snapshot.version),
|
|
1584
1865
|
conversationId: interruptedRun.conversationId,
|
|
1585
1866
|
runId: interruptedRun.id,
|
|
1586
1867
|
snapshotVersion: requested.snapshot.version,
|
|
@@ -1715,6 +1996,8 @@ var AgentStoreDuplicateSchema = z6.object({
|
|
|
1715
1996
|
inputMessageId: AgentRecordIdSchema,
|
|
1716
1997
|
runId: AgentRecordIdSchema,
|
|
1717
1998
|
assistantMessageId: AgentRecordIdSchema,
|
|
1999
|
+
run: AgentRunSchema,
|
|
2000
|
+
assistant: AgentMessageSchema.optional(),
|
|
1718
2001
|
snapshot: AgentSnapshotSchema
|
|
1719
2002
|
});
|
|
1720
2003
|
var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
@@ -1741,6 +2024,7 @@ var CheckpointRunAssistantSchema = z6.object({
|
|
|
1741
2024
|
runId: AgentRecordIdSchema,
|
|
1742
2025
|
expectedRevision: AgentRecordVersionSchema,
|
|
1743
2026
|
ownerId: z6.string().min(1),
|
|
2027
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1744
2028
|
assistant: AgentMessageSchema
|
|
1745
2029
|
});
|
|
1746
2030
|
var CommitRunTerminalSchema = z6.object({
|
|
@@ -1748,6 +2032,7 @@ var CommitRunTerminalSchema = z6.object({
|
|
|
1748
2032
|
runId: AgentRecordIdSchema,
|
|
1749
2033
|
expectedRevision: AgentRecordVersionSchema,
|
|
1750
2034
|
ownerId: z6.string().min(1),
|
|
2035
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1751
2036
|
assistant: AgentMessageSchema,
|
|
1752
2037
|
reason: AgentTerminalReasonSchema,
|
|
1753
2038
|
policyName: z6.string().min(1).optional()
|
|
@@ -1772,18 +2057,23 @@ var ReplaceCompactedRangeSchema = z6.object({
|
|
|
1772
2057
|
});
|
|
1773
2058
|
// src/agent-runtime/store-driver.ts
|
|
1774
2059
|
import { z as z7 } from "zod";
|
|
1775
|
-
var
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
2060
|
+
var AgentRuntimeHeadSchema = z7.object({
|
|
2061
|
+
schemaVersion: z7.literal(1),
|
|
2062
|
+
conversationId: AgentRecordIdSchema,
|
|
2063
|
+
version: AgentRecordVersionSchema
|
|
2064
|
+
});
|
|
2065
|
+
var AgentStoredRunSchema = z7.object({
|
|
2066
|
+
schemaVersion: z7.literal(1),
|
|
2067
|
+
run: AgentRunSchema,
|
|
2068
|
+
terminalAssistant: AgentMessageSchema.optional()
|
|
1780
2069
|
});
|
|
1781
|
-
var
|
|
2070
|
+
var AgentAdmissionReceiptSchema = z7.object({
|
|
1782
2071
|
schemaVersion: z7.literal(1),
|
|
1783
2072
|
conversationId: AgentRecordIdSchema,
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
2073
|
+
idempotencyKey: z7.string().min(1),
|
|
2074
|
+
input: AgentMessageSchema,
|
|
2075
|
+
runId: AgentRecordIdSchema,
|
|
2076
|
+
assistantMessageId: AgentRecordIdSchema
|
|
1787
2077
|
});
|
|
1788
2078
|
var AgentHistoryMutationSchema = z7.discriminatedUnion("type", [
|
|
1789
2079
|
z7.object({ type: z7.literal("admit"), input: AgentMessageSchema }),
|
|
@@ -1809,40 +2099,40 @@ var AgentRecoverableScanInputSchema = z7.object({
|
|
|
1809
2099
|
cursor: z7.string().min(1).optional(),
|
|
1810
2100
|
limit: z7.number().int().min(1).max(1000)
|
|
1811
2101
|
});
|
|
1812
|
-
function
|
|
1813
|
-
return
|
|
2102
|
+
function emptyHead(conversationId) {
|
|
2103
|
+
return AgentRuntimeHeadSchema.parse({
|
|
1814
2104
|
schemaVersion: 1,
|
|
1815
2105
|
conversationId,
|
|
1816
|
-
version: 0
|
|
1817
|
-
runs: [],
|
|
1818
|
-
admissions: []
|
|
2106
|
+
version: 0
|
|
1819
2107
|
});
|
|
1820
2108
|
}
|
|
1821
|
-
function snapshotOf(
|
|
1822
|
-
|
|
2109
|
+
function snapshotOf(head, messages, records) {
|
|
2110
|
+
validateSnapshot(head, messages, records);
|
|
1823
2111
|
return AgentSnapshotSchema.parse({
|
|
1824
2112
|
schemaVersion: 1,
|
|
1825
|
-
conversationId:
|
|
1826
|
-
version:
|
|
2113
|
+
conversationId: head.conversationId,
|
|
2114
|
+
version: head.version,
|
|
1827
2115
|
messages,
|
|
1828
|
-
runs:
|
|
2116
|
+
runs: records.map((record) => record.run).sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
|
|
1829
2117
|
});
|
|
1830
2118
|
}
|
|
1831
|
-
function
|
|
2119
|
+
function validateSnapshot(head, messages, records) {
|
|
1832
2120
|
const runIds = new Set;
|
|
1833
2121
|
const assistantIds = new Set;
|
|
1834
2122
|
const messageIds = new Set;
|
|
1835
|
-
const
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2123
|
+
for (const record of records) {
|
|
2124
|
+
const run = record.run;
|
|
2125
|
+
if (run.conversationId !== head.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
|
|
2126
|
+
throw new TypeError("Stored agent runs contain inconsistent identities");
|
|
2127
|
+
}
|
|
2128
|
+
if (record.terminalAssistant && (record.terminalAssistant.id !== run.assistantMessageId || record.terminalAssistant.conversationId !== run.conversationId || record.terminalAssistant.runId !== run.id || record.terminalAssistant.role !== "assistant" || run.terminalReason === undefined)) {
|
|
2129
|
+
throw new TypeError("Retained terminal assistant does not match its run");
|
|
1840
2130
|
}
|
|
1841
2131
|
runIds.add(run.id);
|
|
1842
2132
|
assistantIds.add(run.assistantMessageId);
|
|
1843
2133
|
}
|
|
1844
2134
|
for (const message of messages) {
|
|
1845
|
-
if (message.conversationId !==
|
|
2135
|
+
if (message.conversationId !== head.conversationId || messageIds.has(message.id)) {
|
|
1846
2136
|
throw new TypeError("Stored agent history contains inconsistent message identities");
|
|
1847
2137
|
}
|
|
1848
2138
|
messageIds.add(message.id);
|
|
@@ -1850,23 +2140,12 @@ function validateAggregate(state, messages) {
|
|
|
1850
2140
|
throw new TypeError("Stored history occupies a reserved assistant identity");
|
|
1851
2141
|
}
|
|
1852
2142
|
if (message.runId !== undefined) {
|
|
1853
|
-
const run =
|
|
2143
|
+
const run = records.find((candidate) => candidate.run.id === message.runId)?.run;
|
|
1854
2144
|
if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
|
|
1855
2145
|
throw new TypeError("Stored assistant history does not match its reserved run identity");
|
|
1856
2146
|
}
|
|
1857
2147
|
}
|
|
1858
2148
|
}
|
|
1859
|
-
for (const admission of state.admissions) {
|
|
1860
|
-
const run = state.runs.find((candidate) => candidate.id === admission.runId);
|
|
1861
|
-
if (idempotencyKeys.has(admission.idempotencyKey) || admittedInputIds.has(admission.inputMessageId) || !run || run.assistantMessageId !== admission.assistantMessageId || !run.inputMessageIds.includes(admission.inputMessageId)) {
|
|
1862
|
-
throw new TypeError("Stored admission identity is inconsistent with its assigned run");
|
|
1863
|
-
}
|
|
1864
|
-
idempotencyKeys.add(admission.idempotencyKey);
|
|
1865
|
-
admittedInputIds.add(admission.inputMessageId);
|
|
1866
|
-
}
|
|
1867
|
-
}
|
|
1868
|
-
function recoverableDescriptors(state) {
|
|
1869
|
-
return state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }));
|
|
1870
2149
|
}
|
|
1871
2150
|
var RecoverableCursorSchema = z7.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
|
|
1872
2151
|
function recoverableCursor(input) {
|
|
@@ -1904,7 +2183,7 @@ function terminalMessageStatus(reason) {
|
|
|
1904
2183
|
}
|
|
1905
2184
|
return "failed";
|
|
1906
2185
|
}
|
|
1907
|
-
function applied(current,
|
|
2186
|
+
function applied(current, input, effects) {
|
|
1908
2187
|
return {
|
|
1909
2188
|
outcome: "applied",
|
|
1910
2189
|
snapshot: AgentSnapshotSchema.parse({
|
|
@@ -1913,27 +2192,14 @@ function applied(current, admissions, input, historyMutation) {
|
|
|
1913
2192
|
runs: input.runs ?? current.runs,
|
|
1914
2193
|
messages: input.messages ?? current.messages
|
|
1915
2194
|
}),
|
|
1916
|
-
|
|
1917
|
-
...
|
|
2195
|
+
...effects?.runRecord && { runRecord: effects.runRecord },
|
|
2196
|
+
...effects?.admissionReceipt && { admissionReceipt: effects.admissionReceipt },
|
|
2197
|
+
...effects?.historyMutation && { historyMutation: effects.historyMutation }
|
|
1918
2198
|
};
|
|
1919
2199
|
}
|
|
1920
|
-
function reduceStore(current,
|
|
2200
|
+
function reduceStore(current, operation) {
|
|
1921
2201
|
if (operation.type === "accept") {
|
|
1922
2202
|
const input = operation.input;
|
|
1923
|
-
const duplicate = currentAdmissions.find((candidate) => candidate.idempotencyKey === input.idempotencyKey);
|
|
1924
|
-
if (duplicate) {
|
|
1925
|
-
if (!duplicateInput) {
|
|
1926
|
-
throw new Error("Duplicate admission input is unavailable from canonical history");
|
|
1927
|
-
}
|
|
1928
|
-
return {
|
|
1929
|
-
outcome: "duplicate",
|
|
1930
|
-
input: duplicateInput,
|
|
1931
|
-
inputMessageId: duplicate.inputMessageId,
|
|
1932
|
-
runId: duplicate.runId,
|
|
1933
|
-
assistantMessageId: duplicate.assistantMessageId,
|
|
1934
|
-
snapshot: current
|
|
1935
|
-
};
|
|
1936
|
-
}
|
|
1937
2203
|
if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
|
|
1938
2204
|
return conflict(current.version);
|
|
1939
2205
|
}
|
|
@@ -1950,16 +2216,25 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
1950
2216
|
revision: coalescedRun.revision + 1,
|
|
1951
2217
|
updatedAt: new Date().toISOString()
|
|
1952
2218
|
}) : input.run;
|
|
1953
|
-
const
|
|
2219
|
+
const admissionReceipt = AgentAdmissionReceiptSchema.parse({
|
|
2220
|
+
schemaVersion: 1,
|
|
2221
|
+
conversationId: input.input.conversationId,
|
|
1954
2222
|
idempotencyKey: input.idempotencyKey,
|
|
1955
|
-
|
|
2223
|
+
input: input.input,
|
|
1956
2224
|
runId: assignedRun.id,
|
|
1957
2225
|
assistantMessageId: assignedRun.assistantMessageId
|
|
1958
2226
|
});
|
|
1959
|
-
return applied(current,
|
|
2227
|
+
return applied(current, {
|
|
1960
2228
|
messages: [...current.messages, input.input],
|
|
1961
2229
|
runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
|
|
1962
|
-
}, {
|
|
2230
|
+
}, {
|
|
2231
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2232
|
+
schemaVersion: 1,
|
|
2233
|
+
run: assignedRun
|
|
2234
|
+
}),
|
|
2235
|
+
admissionReceipt,
|
|
2236
|
+
historyMutation: { type: "admit", input: input.input }
|
|
2237
|
+
});
|
|
1963
2238
|
}
|
|
1964
2239
|
const conversationId = operation.input.conversationId;
|
|
1965
2240
|
const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
|
|
@@ -1975,16 +2250,17 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
1975
2250
|
...run,
|
|
1976
2251
|
state: "running",
|
|
1977
2252
|
ownerId: operation.input.ownerId,
|
|
2253
|
+
fencingToken: (run.fencingToken ?? 0) + 1,
|
|
1978
2254
|
revision: run.revision + 1,
|
|
1979
2255
|
updatedAt: new Date().toISOString()
|
|
1980
2256
|
});
|
|
1981
|
-
return applied(current,
|
|
1982
|
-
|
|
2257
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2258
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
1983
2259
|
});
|
|
1984
2260
|
}
|
|
1985
2261
|
if (operation.type === "checkpoint" && run) {
|
|
1986
2262
|
const input = operation.input;
|
|
1987
|
-
if (run.revision !== input.expectedRevision || run.state !== "running" || run.ownerId !== input.ownerId || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== "streaming") {
|
|
2263
|
+
if (run.revision !== input.expectedRevision || run.state !== "running" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== "streaming") {
|
|
1988
2264
|
return conflict(run.revision);
|
|
1989
2265
|
}
|
|
1990
2266
|
const next = AgentRunSchema.parse({
|
|
@@ -1992,10 +2268,13 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
1992
2268
|
revision: run.revision + 1,
|
|
1993
2269
|
updatedAt: new Date().toISOString()
|
|
1994
2270
|
});
|
|
1995
|
-
return applied(current,
|
|
2271
|
+
return applied(current, {
|
|
1996
2272
|
runs: replaceRun(current.runs, next),
|
|
1997
2273
|
messages: replaceMessage(current.messages, input.assistant)
|
|
1998
|
-
}, {
|
|
2274
|
+
}, {
|
|
2275
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next }),
|
|
2276
|
+
historyMutation: { type: "upsert-assistant", message: input.assistant }
|
|
2277
|
+
});
|
|
1999
2278
|
}
|
|
2000
2279
|
if (operation.type === "interrupt" && run) {
|
|
2001
2280
|
if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
|
|
@@ -2007,8 +2286,8 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2007
2286
|
revision: run.revision + 1,
|
|
2008
2287
|
updatedAt: new Date().toISOString()
|
|
2009
2288
|
});
|
|
2010
|
-
return applied(current,
|
|
2011
|
-
|
|
2289
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2290
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
2012
2291
|
});
|
|
2013
2292
|
}
|
|
2014
2293
|
if (operation.type === "recover" && run) {
|
|
@@ -2046,18 +2325,25 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2046
2325
|
status: "failed",
|
|
2047
2326
|
updatedAt: new Date().toISOString()
|
|
2048
2327
|
});
|
|
2049
|
-
return applied(current,
|
|
2328
|
+
return applied(current, {
|
|
2050
2329
|
runs: replaceRun(current.runs, next),
|
|
2051
2330
|
messages: replaceMessage(current.messages, assistant)
|
|
2052
|
-
}, {
|
|
2331
|
+
}, {
|
|
2332
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2333
|
+
schemaVersion: 1,
|
|
2334
|
+
run: next,
|
|
2335
|
+
terminalAssistant: assistant
|
|
2336
|
+
}),
|
|
2337
|
+
historyMutation: { type: "upsert-assistant", message: assistant }
|
|
2338
|
+
});
|
|
2053
2339
|
}
|
|
2054
|
-
return applied(current,
|
|
2055
|
-
|
|
2340
|
+
return applied(current, { runs: replaceRun(current.runs, next) }, {
|
|
2341
|
+
runRecord: AgentStoredRunSchema.parse({ schemaVersion: 1, run: next })
|
|
2056
2342
|
});
|
|
2057
2343
|
}
|
|
2058
2344
|
if (operation.type === "terminal" && run) {
|
|
2059
2345
|
const input = operation.input;
|
|
2060
|
-
if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== terminalMessageStatus(input.reason)) {
|
|
2346
|
+
if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.fencingToken !== undefined && run.fencingToken !== input.fencingToken || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== terminalMessageStatus(input.reason)) {
|
|
2061
2347
|
return conflict(run.revision);
|
|
2062
2348
|
}
|
|
2063
2349
|
const next = AgentRunSchema.parse({
|
|
@@ -2068,10 +2354,17 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2068
2354
|
revision: run.revision + 1,
|
|
2069
2355
|
updatedAt: new Date().toISOString()
|
|
2070
2356
|
});
|
|
2071
|
-
return applied(current,
|
|
2357
|
+
return applied(current, {
|
|
2072
2358
|
runs: replaceRun(current.runs, next),
|
|
2073
2359
|
messages: replaceMessage(current.messages, input.assistant)
|
|
2074
|
-
}, {
|
|
2360
|
+
}, {
|
|
2361
|
+
runRecord: AgentStoredRunSchema.parse({
|
|
2362
|
+
schemaVersion: 1,
|
|
2363
|
+
run: next,
|
|
2364
|
+
terminalAssistant: input.assistant
|
|
2365
|
+
}),
|
|
2366
|
+
historyMutation: { type: "upsert-assistant", message: input.assistant }
|
|
2367
|
+
});
|
|
2075
2368
|
}
|
|
2076
2369
|
if (operation.type === "compact") {
|
|
2077
2370
|
const input = operation.input;
|
|
@@ -2093,10 +2386,12 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2093
2386
|
input.summary,
|
|
2094
2387
|
...current.messages.slice(first + positions.length)
|
|
2095
2388
|
];
|
|
2096
|
-
return applied(current,
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2389
|
+
return applied(current, { messages }, {
|
|
2390
|
+
historyMutation: {
|
|
2391
|
+
type: "replace-compacted-range",
|
|
2392
|
+
replacedMessageIds: input.replacedMessageIds,
|
|
2393
|
+
summary: input.summary
|
|
2394
|
+
}
|
|
2100
2395
|
});
|
|
2101
2396
|
}
|
|
2102
2397
|
return { outcome: "not_found" };
|
|
@@ -2104,48 +2399,130 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2104
2399
|
function operationConversationId(operation) {
|
|
2105
2400
|
return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
|
|
2106
2401
|
}
|
|
2402
|
+
function mergeRunRecords(...groups) {
|
|
2403
|
+
const records = new Map;
|
|
2404
|
+
for (const group of groups) {
|
|
2405
|
+
for (const rawRecord of group) {
|
|
2406
|
+
const record = AgentStoredRunSchema.parse(rawRecord);
|
|
2407
|
+
const previous = records.get(record.run.id);
|
|
2408
|
+
if (previous && previous.run.assistantMessageId !== record.run.assistantMessageId) {
|
|
2409
|
+
throw new TypeError("Stored agent run identity changed across normalized records");
|
|
2410
|
+
}
|
|
2411
|
+
records.set(record.run.id, record);
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
return [...records.values()];
|
|
2415
|
+
}
|
|
2416
|
+
function referencedRunIds(messages) {
|
|
2417
|
+
return [...new Set(messages.flatMap((message) => message.runId ? [message.runId] : []))];
|
|
2418
|
+
}
|
|
2419
|
+
function validateAdmissionReceipt(receipt, record, conversationId) {
|
|
2420
|
+
const input = receipt.input;
|
|
2421
|
+
const run = record.run;
|
|
2422
|
+
if (receipt.conversationId !== conversationId || input.conversationId !== conversationId || input.role !== "user" || input.status !== "committed" || input.runId !== undefined || receipt.runId !== run.id || receipt.assistantMessageId !== run.assistantMessageId || !run.inputMessageIds.includes(input.id)) {
|
|
2423
|
+
throw new TypeError("Admission receipt does not match its canonical run assignment");
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2107
2426
|
function createAgentRuntimeStore(driver) {
|
|
2108
2427
|
const loadSnapshot = (conversationId) => driver.transaction(async (transaction) => {
|
|
2109
|
-
const [stored, messages] = await Promise.all([
|
|
2110
|
-
driver.
|
|
2111
|
-
driver.history.load(transaction, conversationId)
|
|
2428
|
+
const [stored, messages, activeRecords] = await Promise.all([
|
|
2429
|
+
driver.head.load(transaction, conversationId),
|
|
2430
|
+
driver.history.load(transaction, conversationId),
|
|
2431
|
+
driver.runs.listActive(transaction, conversationId)
|
|
2112
2432
|
]);
|
|
2113
|
-
|
|
2433
|
+
const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
|
|
2434
|
+
const referencedRecords = await driver.runs.loadMany(transaction, {
|
|
2435
|
+
conversationId,
|
|
2436
|
+
runIds: referencedRunIds(messages)
|
|
2437
|
+
});
|
|
2438
|
+
return snapshotOf(head, messages, mergeRunRecords(activeRecords, referencedRecords));
|
|
2114
2439
|
});
|
|
2115
2440
|
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
2116
2441
|
const conversationId = operationConversationId(operation);
|
|
2117
|
-
const
|
|
2118
|
-
|
|
2119
|
-
driver.
|
|
2442
|
+
const operationRunId = operation.type === "accept" ? operation.input.coalesceIntoRunId : operation.type === "compact" ? undefined : operation.input.runId;
|
|
2443
|
+
const [stored, messages, activeRecords, operationRecord, duplicateReceipt] = await Promise.all([
|
|
2444
|
+
driver.head.load(transaction, conversationId),
|
|
2445
|
+
driver.history.load(transaction, conversationId),
|
|
2446
|
+
driver.runs.listActive(transaction, conversationId),
|
|
2447
|
+
operationRunId ? driver.runs.load(transaction, { conversationId, runId: operationRunId }) : undefined,
|
|
2448
|
+
operation.type === "accept" ? driver.admissions.load(transaction, {
|
|
2449
|
+
conversationId,
|
|
2450
|
+
idempotencyKey: operation.input.idempotencyKey
|
|
2451
|
+
}) : undefined
|
|
2120
2452
|
]);
|
|
2121
|
-
const
|
|
2122
|
-
const
|
|
2123
|
-
const duplicateIdentity = operation.type === "accept" ? state.admissions.find((candidate) => candidate.idempotencyKey === operation.input.idempotencyKey) : undefined;
|
|
2124
|
-
const duplicateInput = duplicateIdentity ? await driver.history.loadById(transaction, {
|
|
2453
|
+
const head = AgentRuntimeHeadSchema.parse(stored ?? emptyHead(conversationId));
|
|
2454
|
+
const referencedRecords = await driver.runs.loadMany(transaction, {
|
|
2125
2455
|
conversationId,
|
|
2126
|
-
|
|
2127
|
-
})
|
|
2128
|
-
|
|
2129
|
-
|
|
2456
|
+
runIds: referencedRunIds(messages)
|
|
2457
|
+
});
|
|
2458
|
+
const records = mergeRunRecords(activeRecords, referencedRecords, operationRecord ? [operationRecord] : []);
|
|
2459
|
+
const current = snapshotOf(head, messages, records);
|
|
2460
|
+
if (duplicateReceipt) {
|
|
2461
|
+
const duplicateRecord = await driver.runs.load(transaction, {
|
|
2462
|
+
conversationId,
|
|
2463
|
+
runId: duplicateReceipt.runId
|
|
2464
|
+
});
|
|
2465
|
+
if (!duplicateRecord) {
|
|
2466
|
+
throw new TypeError("Admission receipt points to a missing canonical run");
|
|
2467
|
+
}
|
|
2468
|
+
validateAdmissionReceipt(duplicateReceipt, duplicateRecord, conversationId);
|
|
2469
|
+
return {
|
|
2470
|
+
outcome: "duplicate",
|
|
2471
|
+
input: duplicateReceipt.input,
|
|
2472
|
+
inputMessageId: duplicateReceipt.input.id,
|
|
2473
|
+
runId: duplicateReceipt.runId,
|
|
2474
|
+
assistantMessageId: duplicateReceipt.assistantMessageId,
|
|
2475
|
+
run: duplicateRecord.run,
|
|
2476
|
+
...duplicateRecord.terminalAssistant && {
|
|
2477
|
+
assistant: duplicateRecord.terminalAssistant
|
|
2478
|
+
},
|
|
2479
|
+
snapshot: snapshotOf(head, messages, mergeRunRecords(records, [duplicateRecord]))
|
|
2480
|
+
};
|
|
2130
2481
|
}
|
|
2131
|
-
|
|
2482
|
+
if (operation.type === "accept") {
|
|
2483
|
+
const inputCollision = await driver.admissions.loadByInputMessageId(transaction, {
|
|
2484
|
+
conversationId,
|
|
2485
|
+
inputMessageId: operation.input.input.id
|
|
2486
|
+
});
|
|
2487
|
+
if (inputCollision) {
|
|
2488
|
+
throw new TypeError("Input message identity is already assigned to an admission");
|
|
2489
|
+
}
|
|
2490
|
+
if (!operation.input.coalesceIntoRunId) {
|
|
2491
|
+
const [runCollision, assistantCollision] = await Promise.all([
|
|
2492
|
+
driver.runs.load(transaction, {
|
|
2493
|
+
conversationId,
|
|
2494
|
+
runId: operation.input.run.id
|
|
2495
|
+
}),
|
|
2496
|
+
driver.runs.loadByAssistantMessageId(transaction, {
|
|
2497
|
+
conversationId,
|
|
2498
|
+
assistantMessageId: operation.input.run.assistantMessageId
|
|
2499
|
+
})
|
|
2500
|
+
]);
|
|
2501
|
+
if (runCollision || assistantCollision) {
|
|
2502
|
+
throw new TypeError("Queued run identities are already reserved");
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
const reduced = reduceStore(current, operation);
|
|
2132
2507
|
if (reduced.outcome !== "applied")
|
|
2133
2508
|
return reduced;
|
|
2134
|
-
const
|
|
2509
|
+
const nextHead = AgentRuntimeHeadSchema.parse({
|
|
2135
2510
|
schemaVersion: 1,
|
|
2136
2511
|
conversationId,
|
|
2137
|
-
version: reduced.snapshot.version
|
|
2138
|
-
runs: reduced.snapshot.runs,
|
|
2139
|
-
admissions: reduced.admissions
|
|
2512
|
+
version: reduced.snapshot.version
|
|
2140
2513
|
});
|
|
2141
|
-
const outcome = await driver.
|
|
2514
|
+
const outcome = await driver.head.compareAndSwap(transaction, {
|
|
2142
2515
|
conversationId,
|
|
2143
2516
|
expectedVersion: current.version,
|
|
2144
|
-
next:
|
|
2145
|
-
recoverable: recoverableDescriptors(nextState)
|
|
2517
|
+
next: nextHead
|
|
2146
2518
|
});
|
|
2147
2519
|
if (outcome.outcome === "conflict")
|
|
2148
2520
|
return conflict(outcome.actualVersion);
|
|
2521
|
+
if (reduced.runRecord)
|
|
2522
|
+
await driver.runs.save(transaction, reduced.runRecord);
|
|
2523
|
+
if (reduced.admissionReceipt) {
|
|
2524
|
+
await driver.admissions.create(transaction, reduced.admissionReceipt);
|
|
2525
|
+
}
|
|
2149
2526
|
if (reduced.historyMutation) {
|
|
2150
2527
|
await driver.history.apply(transaction, reduced.historyMutation);
|
|
2151
2528
|
}
|
|
@@ -2197,10 +2574,16 @@ function createAgentRuntimeStore(driver) {
|
|
|
2197
2574
|
}
|
|
2198
2575
|
};
|
|
2199
2576
|
}
|
|
2200
|
-
function
|
|
2577
|
+
function cloneHeadMap(source) {
|
|
2201
2578
|
return new Map([...source].map(([key, value]) => [
|
|
2202
2579
|
key,
|
|
2203
|
-
|
|
2580
|
+
AgentRuntimeHeadSchema.parse(structuredClone(value))
|
|
2581
|
+
]));
|
|
2582
|
+
}
|
|
2583
|
+
function cloneNestedMap(source, clone) {
|
|
2584
|
+
return new Map([...source].map(([outerKey, values]) => [
|
|
2585
|
+
outerKey,
|
|
2586
|
+
new Map([...values].map(([innerKey, value]) => [innerKey, clone(value)]))
|
|
2204
2587
|
]));
|
|
2205
2588
|
}
|
|
2206
2589
|
function cloneHistoryMap(source) {
|
|
@@ -2210,9 +2593,10 @@ function cloneHistoryMap(source) {
|
|
|
2210
2593
|
]));
|
|
2211
2594
|
}
|
|
2212
2595
|
function createMemoryAgentRuntimeStore() {
|
|
2213
|
-
let
|
|
2596
|
+
let heads = new Map;
|
|
2597
|
+
let runs = new Map;
|
|
2598
|
+
let admissions = new Map;
|
|
2214
2599
|
let histories = new Map;
|
|
2215
|
-
let archivedMessages = new Map;
|
|
2216
2600
|
let transactionTail = Promise.resolve();
|
|
2217
2601
|
const driver = {
|
|
2218
2602
|
async transaction(work) {
|
|
@@ -2225,50 +2609,91 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2225
2609
|
return;
|
|
2226
2610
|
});
|
|
2227
2611
|
const transaction = {
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
new Map([...messages].map(([messageId, message]) => [
|
|
2233
|
-
messageId,
|
|
2234
|
-
AgentMessageSchema.parse(structuredClone(message))
|
|
2235
|
-
]))
|
|
2236
|
-
]))
|
|
2612
|
+
heads: cloneHeadMap(heads),
|
|
2613
|
+
runs: cloneNestedMap(runs, (record) => AgentStoredRunSchema.parse(structuredClone(record))),
|
|
2614
|
+
admissions: cloneNestedMap(admissions, (receipt) => AgentAdmissionReceiptSchema.parse(structuredClone(receipt))),
|
|
2615
|
+
histories: cloneHistoryMap(histories)
|
|
2237
2616
|
};
|
|
2238
2617
|
try {
|
|
2239
2618
|
const result = await work(transaction);
|
|
2240
|
-
|
|
2619
|
+
heads = transaction.heads;
|
|
2620
|
+
runs = transaction.runs;
|
|
2621
|
+
admissions = transaction.admissions;
|
|
2241
2622
|
histories = transaction.histories;
|
|
2242
|
-
archivedMessages = transaction.archivedMessages;
|
|
2243
2623
|
return result;
|
|
2244
2624
|
} finally {
|
|
2245
2625
|
release.resolve();
|
|
2246
2626
|
}
|
|
2247
2627
|
},
|
|
2248
|
-
|
|
2628
|
+
head: {
|
|
2249
2629
|
async load(transaction, conversationId) {
|
|
2250
|
-
const
|
|
2251
|
-
return
|
|
2630
|
+
const head = transaction.heads.get(conversationId);
|
|
2631
|
+
return head ? AgentRuntimeHeadSchema.parse(structuredClone(head)) : undefined;
|
|
2252
2632
|
},
|
|
2253
2633
|
async compareAndSwap(transaction, input) {
|
|
2254
|
-
const current = transaction.
|
|
2634
|
+
const current = transaction.heads.get(input.conversationId);
|
|
2255
2635
|
const actualVersion = current?.version ?? 0;
|
|
2256
2636
|
if (actualVersion !== input.expectedVersion) {
|
|
2257
2637
|
return { outcome: "conflict", actualVersion };
|
|
2258
2638
|
}
|
|
2259
|
-
transaction.
|
|
2639
|
+
transaction.heads.set(input.conversationId, AgentRuntimeHeadSchema.parse(structuredClone(input.next)));
|
|
2260
2640
|
return { outcome: "applied" };
|
|
2261
2641
|
}
|
|
2262
2642
|
},
|
|
2643
|
+
runs: {
|
|
2644
|
+
async load(transaction, input) {
|
|
2645
|
+
const record = transaction.runs.get(input.conversationId)?.get(input.runId);
|
|
2646
|
+
return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
|
|
2647
|
+
},
|
|
2648
|
+
async loadByAssistantMessageId(transaction, input) {
|
|
2649
|
+
const record = [...transaction.runs.get(input.conversationId)?.values() ?? []].find((candidate) => candidate.run.assistantMessageId === input.assistantMessageId);
|
|
2650
|
+
return record ? AgentStoredRunSchema.parse(structuredClone(record)) : undefined;
|
|
2651
|
+
},
|
|
2652
|
+
async loadMany(transaction, input) {
|
|
2653
|
+
const records = transaction.runs.get(input.conversationId);
|
|
2654
|
+
return input.runIds.flatMap((runId) => {
|
|
2655
|
+
const record = records?.get(runId);
|
|
2656
|
+
return record ? [AgentStoredRunSchema.parse(structuredClone(record))] : [];
|
|
2657
|
+
});
|
|
2658
|
+
},
|
|
2659
|
+
async listActive(transaction, conversationId) {
|
|
2660
|
+
return [...transaction.runs.get(conversationId)?.values() ?? []].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => AgentStoredRunSchema.parse(structuredClone(record)));
|
|
2661
|
+
},
|
|
2662
|
+
async save(transaction, rawRecord) {
|
|
2663
|
+
const record = AgentStoredRunSchema.parse(structuredClone(rawRecord));
|
|
2664
|
+
const conversationRuns = transaction.runs.get(record.run.conversationId) ?? new Map;
|
|
2665
|
+
const collision = [...conversationRuns.values()].find((candidate) => candidate.run.id !== record.run.id && candidate.run.assistantMessageId === record.run.assistantMessageId);
|
|
2666
|
+
if (collision)
|
|
2667
|
+
throw new TypeError("Assistant message identity is already reserved");
|
|
2668
|
+
conversationRuns.set(record.run.id, record);
|
|
2669
|
+
transaction.runs.set(record.run.conversationId, conversationRuns);
|
|
2670
|
+
}
|
|
2671
|
+
},
|
|
2672
|
+
admissions: {
|
|
2673
|
+
async load(transaction, input) {
|
|
2674
|
+
const receipt = transaction.admissions.get(input.conversationId)?.get(input.idempotencyKey);
|
|
2675
|
+
return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
|
|
2676
|
+
},
|
|
2677
|
+
async loadByInputMessageId(transaction, input) {
|
|
2678
|
+
const receipt = [
|
|
2679
|
+
...transaction.admissions.get(input.conversationId)?.values() ?? []
|
|
2680
|
+
].find((candidate) => candidate.input.id === input.inputMessageId);
|
|
2681
|
+
return receipt ? AgentAdmissionReceiptSchema.parse(structuredClone(receipt)) : undefined;
|
|
2682
|
+
},
|
|
2683
|
+
async create(transaction, rawReceipt) {
|
|
2684
|
+
const receipt = AgentAdmissionReceiptSchema.parse(structuredClone(rawReceipt));
|
|
2685
|
+
const conversationAdmissions = transaction.admissions.get(receipt.conversationId) ?? new Map;
|
|
2686
|
+
if (conversationAdmissions.has(receipt.idempotencyKey) || [...conversationAdmissions.values()].some((candidate) => candidate.input.id === receipt.input.id)) {
|
|
2687
|
+
throw new TypeError("Admission identity is already reserved");
|
|
2688
|
+
}
|
|
2689
|
+
conversationAdmissions.set(receipt.idempotencyKey, receipt);
|
|
2690
|
+
transaction.admissions.set(receipt.conversationId, conversationAdmissions);
|
|
2691
|
+
}
|
|
2692
|
+
},
|
|
2263
2693
|
history: {
|
|
2264
2694
|
async load(transaction, conversationId) {
|
|
2265
2695
|
return (transaction.histories.get(conversationId) ?? []).map((message) => AgentMessageSchema.parse(structuredClone(message)));
|
|
2266
2696
|
},
|
|
2267
|
-
async loadById(transaction, input) {
|
|
2268
|
-
const active = (transaction.histories.get(input.conversationId) ?? []).find((message2) => message2.id === input.messageId);
|
|
2269
|
-
const message = active ?? transaction.archivedMessages.get(input.conversationId)?.get(input.messageId);
|
|
2270
|
-
return message ? AgentMessageSchema.parse(structuredClone(message)) : undefined;
|
|
2271
|
-
},
|
|
2272
2697
|
async apply(transaction, rawMutation) {
|
|
2273
2698
|
const mutation = AgentHistoryMutationSchema.parse(rawMutation);
|
|
2274
2699
|
const conversationId = mutation.type === "admit" ? mutation.input.conversationId : mutation.type === "upsert-assistant" ? mutation.message.conversationId : mutation.summary.conversationId;
|
|
@@ -2286,11 +2711,6 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2286
2711
|
const first = positions[0];
|
|
2287
2712
|
if (first === undefined)
|
|
2288
2713
|
throw new Error("Compaction history range disappeared");
|
|
2289
|
-
const archive = transaction.archivedMessages.get(conversationId) ?? new Map;
|
|
2290
|
-
for (const message of current.filter((candidate) => replaced.has(candidate.id))) {
|
|
2291
|
-
archive.set(message.id, AgentMessageSchema.parse(structuredClone(message)));
|
|
2292
|
-
}
|
|
2293
|
-
transaction.archivedMessages.set(conversationId, archive);
|
|
2294
2714
|
transaction.histories.set(conversationId, [
|
|
2295
2715
|
...current.slice(0, first),
|
|
2296
2716
|
mutation.summary,
|
|
@@ -2299,7 +2719,7 @@ function createMemoryAgentRuntimeStore() {
|
|
|
2299
2719
|
}
|
|
2300
2720
|
},
|
|
2301
2721
|
async scanRecoverable(input) {
|
|
2302
|
-
const descriptors = [...
|
|
2722
|
+
const descriptors = [...runs].flatMap(([conversationId, conversationRuns]) => [...conversationRuns.values()].filter((record) => ["queued", "running", "interrupt_requested"].includes(record.run.state)).map((record) => ({ conversationId, run: record.run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
|
|
2303
2723
|
const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
|
|
2304
2724
|
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id === cursorTuple[1]) + 1 : 0;
|
|
2305
2725
|
const items = descriptors.slice(start, start + input.limit);
|
|
@@ -2317,7 +2737,7 @@ export {
|
|
|
2317
2737
|
AcceptInputAndAssignRunSchema,
|
|
2318
2738
|
AcquireAgentRunSchema,
|
|
2319
2739
|
AgentAdmissionEventSchema,
|
|
2320
|
-
|
|
2740
|
+
AgentAdmissionReceiptSchema,
|
|
2321
2741
|
AgentAssistantPlaceholderSchema,
|
|
2322
2742
|
AgentCheckpointEventSchema,
|
|
2323
2743
|
AgentControlPartSchema,
|
|
@@ -2331,6 +2751,7 @@ export {
|
|
|
2331
2751
|
AgentMessageStatusSchema,
|
|
2332
2752
|
AgentModelCapabilitySchema,
|
|
2333
2753
|
AgentModelDescriptorSchema,
|
|
2754
|
+
AgentModelRegistrySnapshotSchema,
|
|
2334
2755
|
AgentOpaquePartSchema,
|
|
2335
2756
|
AgentProviderEnvelopeSchema,
|
|
2336
2757
|
AgentReasoningDeltaEventSchema,
|
|
@@ -2346,7 +2767,9 @@ export {
|
|
|
2346
2767
|
AgentRunSchema,
|
|
2347
2768
|
AgentRunStateEventSchema,
|
|
2348
2769
|
AgentRunStateSchema,
|
|
2770
|
+
AgentRuntimeEventCursorSchema,
|
|
2349
2771
|
AgentRuntimeEventSchema,
|
|
2772
|
+
AgentRuntimeHeadSchema,
|
|
2350
2773
|
AgentSnapshotSchema,
|
|
2351
2774
|
AgentSourcePartSchema,
|
|
2352
2775
|
AgentStoreAppliedSchema,
|
|
@@ -2354,7 +2777,7 @@ export {
|
|
|
2354
2777
|
AgentStoreDuplicateSchema,
|
|
2355
2778
|
AgentStoreMutationResultSchema,
|
|
2356
2779
|
AgentStoreNotFoundSchema,
|
|
2357
|
-
|
|
2780
|
+
AgentStoredRunSchema,
|
|
2358
2781
|
AgentTerminalEventSchema,
|
|
2359
2782
|
AgentTerminalReasonSchema,
|
|
2360
2783
|
AgentTextPartSchema,
|
|
@@ -2371,9 +2794,12 @@ export {
|
|
|
2371
2794
|
RecoverAgentRunSchema,
|
|
2372
2795
|
ReplaceCompactedRangeSchema,
|
|
2373
2796
|
RequestRunInterruptSchema,
|
|
2797
|
+
advanceAgentRuntimeEventCursor,
|
|
2798
|
+
agentDurableEventId,
|
|
2374
2799
|
composeAgentPrompt,
|
|
2375
2800
|
createAgentObservability,
|
|
2376
2801
|
createAgentRuntime,
|
|
2802
|
+
createAgentRuntimeEventSink,
|
|
2377
2803
|
createAgentRuntimeStore,
|
|
2378
2804
|
createAgentSessionCoordinator,
|
|
2379
2805
|
createAgentToolFenceLifecycle,
|
|
@@ -2381,5 +2807,8 @@ export {
|
|
|
2381
2807
|
defineAgentProtocol,
|
|
2382
2808
|
defineModelRegistry,
|
|
2383
2809
|
projectAgentHistory,
|
|
2384
|
-
|
|
2810
|
+
projectAgentHistoryDetailed,
|
|
2811
|
+
selectAgentHistory,
|
|
2812
|
+
structuredCompaction,
|
|
2813
|
+
validateAgentModelSnapshot
|
|
2385
2814
|
};
|