stitchkit 0.57.0 → 0.58.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 +3 -0
- package/dist/agent-runtime/store-driver.d.ts.map +1 -1
- package/dist/agent-runtime/store.d.ts +7 -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 +4 -4
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +356 -64
- 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 +169 -4
- package/llms-full.txt +102 -5
- 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
|
+
}
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
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
|
+
});
|
|
434
543
|
continue;
|
|
435
544
|
}
|
|
436
|
-
|
|
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,
|
|
@@ -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,
|
|
@@ -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,
|
|
@@ -1741,6 +2022,7 @@ var CheckpointRunAssistantSchema = z6.object({
|
|
|
1741
2022
|
runId: AgentRecordIdSchema,
|
|
1742
2023
|
expectedRevision: AgentRecordVersionSchema,
|
|
1743
2024
|
ownerId: z6.string().min(1),
|
|
2025
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1744
2026
|
assistant: AgentMessageSchema
|
|
1745
2027
|
});
|
|
1746
2028
|
var CommitRunTerminalSchema = z6.object({
|
|
@@ -1748,6 +2030,7 @@ var CommitRunTerminalSchema = z6.object({
|
|
|
1748
2030
|
runId: AgentRecordIdSchema,
|
|
1749
2031
|
expectedRevision: AgentRecordVersionSchema,
|
|
1750
2032
|
ownerId: z6.string().min(1),
|
|
2033
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1751
2034
|
assistant: AgentMessageSchema,
|
|
1752
2035
|
reason: AgentTerminalReasonSchema,
|
|
1753
2036
|
policyName: z6.string().min(1).optional()
|
|
@@ -1975,6 +2258,7 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
1975
2258
|
...run,
|
|
1976
2259
|
state: "running",
|
|
1977
2260
|
ownerId: operation.input.ownerId,
|
|
2261
|
+
fencingToken: (run.fencingToken ?? 0) + 1,
|
|
1978
2262
|
revision: run.revision + 1,
|
|
1979
2263
|
updatedAt: new Date().toISOString()
|
|
1980
2264
|
});
|
|
@@ -1984,7 +2268,7 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
1984
2268
|
}
|
|
1985
2269
|
if (operation.type === "checkpoint" && run) {
|
|
1986
2270
|
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") {
|
|
2271
|
+
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
2272
|
return conflict(run.revision);
|
|
1989
2273
|
}
|
|
1990
2274
|
const next = AgentRunSchema.parse({
|
|
@@ -2057,7 +2341,7 @@ function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
|
2057
2341
|
}
|
|
2058
2342
|
if (operation.type === "terminal" && run) {
|
|
2059
2343
|
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)) {
|
|
2344
|
+
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
2345
|
return conflict(run.revision);
|
|
2062
2346
|
}
|
|
2063
2347
|
const next = AgentRunSchema.parse({
|
|
@@ -2331,6 +2615,7 @@ export {
|
|
|
2331
2615
|
AgentMessageStatusSchema,
|
|
2332
2616
|
AgentModelCapabilitySchema,
|
|
2333
2617
|
AgentModelDescriptorSchema,
|
|
2618
|
+
AgentModelRegistrySnapshotSchema,
|
|
2334
2619
|
AgentOpaquePartSchema,
|
|
2335
2620
|
AgentProviderEnvelopeSchema,
|
|
2336
2621
|
AgentReasoningDeltaEventSchema,
|
|
@@ -2346,6 +2631,7 @@ export {
|
|
|
2346
2631
|
AgentRunSchema,
|
|
2347
2632
|
AgentRunStateEventSchema,
|
|
2348
2633
|
AgentRunStateSchema,
|
|
2634
|
+
AgentRuntimeEventCursorSchema,
|
|
2349
2635
|
AgentRuntimeEventSchema,
|
|
2350
2636
|
AgentSnapshotSchema,
|
|
2351
2637
|
AgentSourcePartSchema,
|
|
@@ -2371,9 +2657,12 @@ export {
|
|
|
2371
2657
|
RecoverAgentRunSchema,
|
|
2372
2658
|
ReplaceCompactedRangeSchema,
|
|
2373
2659
|
RequestRunInterruptSchema,
|
|
2660
|
+
advanceAgentRuntimeEventCursor,
|
|
2661
|
+
agentDurableEventId,
|
|
2374
2662
|
composeAgentPrompt,
|
|
2375
2663
|
createAgentObservability,
|
|
2376
2664
|
createAgentRuntime,
|
|
2665
|
+
createAgentRuntimeEventSink,
|
|
2377
2666
|
createAgentRuntimeStore,
|
|
2378
2667
|
createAgentSessionCoordinator,
|
|
2379
2668
|
createAgentToolFenceLifecycle,
|
|
@@ -2381,5 +2670,8 @@ export {
|
|
|
2381
2670
|
defineAgentProtocol,
|
|
2382
2671
|
defineModelRegistry,
|
|
2383
2672
|
projectAgentHistory,
|
|
2384
|
-
|
|
2673
|
+
projectAgentHistoryDetailed,
|
|
2674
|
+
selectAgentHistory,
|
|
2675
|
+
structuredCompaction,
|
|
2676
|
+
validateAgentModelSnapshot
|
|
2385
2677
|
};
|
|
@@ -121,6 +121,7 @@ var AgentRunSchema = z.object({
|
|
|
121
121
|
state: AgentRunStateSchema,
|
|
122
122
|
revision: AgentRecordVersionSchema,
|
|
123
123
|
ownerId: z.string().min(1).optional(),
|
|
124
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
124
125
|
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
125
126
|
terminalPolicyName: z.string().min(1).optional(),
|
|
126
127
|
createdAt: AgentTimestampSchema,
|