stitchkit 0.56.5 → 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 +693 -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 +43 -4
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +75 -0
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +409 -0
- package/dist/agent-runtime/store-driver.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +169 -2
- 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 +7 -6
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +1157 -511
- package/dist/index-vtjgx3vv.js +161 -0
- package/dist/server/error-hook.d.ts +8 -1
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/index.js +4 -2
- package/dist/testing/agent-store-conformance.d.ts +7 -0
- package/dist/testing/agent-store-conformance.d.ts.map +1 -0
- package/dist/testing.d.ts +2 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +331 -0
- package/llms-full.txt +201 -28
- package/package.json +1 -1
package/dist/agent-runtime.js
CHANGED
|
@@ -2,6 +2,34 @@ import {
|
|
|
2
2
|
ToolExecutionControlError,
|
|
3
3
|
isToolExecutionControlError
|
|
4
4
|
} from "./index-sa2mbwa7.js";
|
|
5
|
+
import {
|
|
6
|
+
AgentAssistantPlaceholderSchema,
|
|
7
|
+
AgentControlPartSchema,
|
|
8
|
+
AgentCostValueSchema,
|
|
9
|
+
AgentFilePartSchema,
|
|
10
|
+
AgentJsonObjectSchema,
|
|
11
|
+
AgentMessagePartSchema,
|
|
12
|
+
AgentMessageRoleSchema,
|
|
13
|
+
AgentMessageSchema,
|
|
14
|
+
AgentMessageStatusSchema,
|
|
15
|
+
AgentOpaquePartSchema,
|
|
16
|
+
AgentProviderEnvelopeSchema,
|
|
17
|
+
AgentReasoningPartSchema,
|
|
18
|
+
AgentRecordIdSchema,
|
|
19
|
+
AgentRecordVersionSchema,
|
|
20
|
+
AgentRunMetricsSchema,
|
|
21
|
+
AgentRunSchema,
|
|
22
|
+
AgentRunStateSchema,
|
|
23
|
+
AgentSnapshotSchema,
|
|
24
|
+
AgentSourcePartSchema,
|
|
25
|
+
AgentTerminalReasonSchema,
|
|
26
|
+
AgentTextPartSchema,
|
|
27
|
+
AgentTimestampSchema,
|
|
28
|
+
AgentToolCallPartSchema,
|
|
29
|
+
AgentToolResultPartSchema,
|
|
30
|
+
AgentUsageSchema,
|
|
31
|
+
AgentUsageValueSchema
|
|
32
|
+
} from "./index-vtjgx3vv.js";
|
|
5
33
|
import"./index-6djpbnda.js";
|
|
6
34
|
import"./index-cby4ar3v.js";
|
|
7
35
|
import {
|
|
@@ -58,54 +86,66 @@ function eligibleForCompaction(messages, keepRecentTurns) {
|
|
|
58
86
|
const eligibleCount = Math.max(0, completeTurns.length - keepRecentTurns);
|
|
59
87
|
return completeTurns.slice(0, eligibleCount).flatMap((turn) => turn.messages);
|
|
60
88
|
}
|
|
61
|
-
function mutationSnapshot(result, fallback) {
|
|
89
|
+
function mutationSnapshot(result, fallback, attempts) {
|
|
62
90
|
if (result.outcome === "applied" || result.outcome === "duplicate") {
|
|
63
|
-
return { outcome: "applied", snapshot: result.snapshot };
|
|
91
|
+
return { outcome: "applied", snapshot: result.snapshot, attempts };
|
|
64
92
|
}
|
|
65
|
-
return { outcome: result.outcome, snapshot: fallback };
|
|
93
|
+
return { outcome: result.outcome, snapshot: fallback, attempts };
|
|
66
94
|
}
|
|
67
95
|
function structuredCompaction(config) {
|
|
68
96
|
if (!Number.isSafeInteger(config.keepRecentTurns) || config.keepRecentTurns < 1) {
|
|
69
97
|
throw new TypeError("keepRecentTurns must be a positive safe integer");
|
|
70
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
|
+
}
|
|
71
103
|
return async (input) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
summary
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
+
}
|
|
97
147
|
}
|
|
98
|
-
|
|
99
|
-
const applied = await input.store.replaceCompactedRange({
|
|
100
|
-
conversationId: input.conversationId,
|
|
101
|
-
expectedVersion: snapshot.version,
|
|
102
|
-
replacedMessageIds: [
|
|
103
|
-
...previousSummaryMessage ? [previousSummaryMessage.id] : [],
|
|
104
|
-
...eligibleMessages.map((message) => message.id)
|
|
105
|
-
],
|
|
106
|
-
summary: summaryMessage
|
|
107
|
-
});
|
|
108
|
-
return mutationSnapshot(applied, snapshot);
|
|
148
|
+
return { outcome: "conflict", snapshot: lastSnapshot, attempts: maxAttempts };
|
|
109
149
|
};
|
|
110
150
|
}
|
|
111
151
|
// src/agent-runtime/coordinator.ts
|
|
@@ -226,210 +266,78 @@ function createAgentSessionCoordinator() {
|
|
|
226
266
|
};
|
|
227
267
|
}
|
|
228
268
|
// src/agent-runtime/events.ts
|
|
229
|
-
import { z as z2 } from "zod";
|
|
230
|
-
|
|
231
|
-
// src/agent-runtime/schemas.ts
|
|
232
269
|
import { z } from "zod";
|
|
233
|
-
var
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
var AgentJsonObjectSchema = z.record(z.string(), z.json());
|
|
237
|
-
var AgentProviderEnvelopeSchema = z.object({
|
|
238
|
-
schemaVersion: z.int().positive(),
|
|
239
|
-
provider: z.string().min(1),
|
|
240
|
-
data: AgentJsonObjectSchema
|
|
241
|
-
});
|
|
242
|
-
var AgentTextPartSchema = z.object({
|
|
243
|
-
type: z.literal("text"),
|
|
244
|
-
text: z.string()
|
|
245
|
-
});
|
|
246
|
-
var AgentReasoningPartSchema = z.object({
|
|
247
|
-
type: z.literal("reasoning"),
|
|
248
|
-
text: z.string(),
|
|
249
|
-
provider: AgentProviderEnvelopeSchema.optional()
|
|
250
|
-
});
|
|
251
|
-
var AgentFilePartSchema = z.object({
|
|
252
|
-
type: z.literal("file"),
|
|
253
|
-
mediaType: z.string().min(1),
|
|
254
|
-
reference: z.string().min(1),
|
|
255
|
-
filename: z.string().min(1).optional()
|
|
256
|
-
});
|
|
257
|
-
var AgentSourcePartSchema = z.object({
|
|
258
|
-
type: z.literal("source"),
|
|
259
|
-
sourceId: z.string().min(1),
|
|
260
|
-
url: z.url().optional(),
|
|
261
|
-
title: z.string().optional()
|
|
262
|
-
});
|
|
263
|
-
var AgentToolCallPartSchema = z.object({
|
|
264
|
-
type: z.literal("tool-call"),
|
|
265
|
-
callId: z.string().min(1),
|
|
266
|
-
toolName: z.string().min(1),
|
|
267
|
-
input: z.json(),
|
|
268
|
-
provider: AgentProviderEnvelopeSchema.optional()
|
|
269
|
-
});
|
|
270
|
-
var AgentToolResultPartSchema = z.object({
|
|
271
|
-
type: z.literal("tool-result"),
|
|
272
|
-
callId: z.string().min(1),
|
|
273
|
-
toolName: z.string().min(1),
|
|
274
|
-
outcome: z.enum(["success", "error", "interrupted"]),
|
|
275
|
-
output: z.json().optional()
|
|
276
|
-
});
|
|
277
|
-
var AgentOpaquePartSchema = z.object({
|
|
278
|
-
type: z.literal("provider"),
|
|
279
|
-
envelope: AgentProviderEnvelopeSchema
|
|
280
|
-
});
|
|
281
|
-
var AgentControlPartSchema = z.object({
|
|
282
|
-
type: z.literal("control"),
|
|
283
|
-
reason: z.enum(["run-interrupted", "stale-run"])
|
|
284
|
-
});
|
|
285
|
-
var AgentMessagePartSchema = z.discriminatedUnion("type", [
|
|
286
|
-
AgentTextPartSchema,
|
|
287
|
-
AgentReasoningPartSchema,
|
|
288
|
-
AgentFilePartSchema,
|
|
289
|
-
AgentSourcePartSchema,
|
|
290
|
-
AgentToolCallPartSchema,
|
|
291
|
-
AgentToolResultPartSchema,
|
|
292
|
-
AgentOpaquePartSchema,
|
|
293
|
-
AgentControlPartSchema
|
|
294
|
-
]);
|
|
295
|
-
var AgentMessageRoleSchema = z.enum(["user", "assistant", "system", "summary"]);
|
|
296
|
-
var AgentMessageStatusSchema = z.enum([
|
|
297
|
-
"committed",
|
|
298
|
-
"streaming",
|
|
299
|
-
"completed",
|
|
300
|
-
"interrupted",
|
|
301
|
-
"failed"
|
|
302
|
-
]);
|
|
303
|
-
var AgentMessageSchema = z.object({
|
|
304
|
-
schemaVersion: z.literal(1),
|
|
305
|
-
id: AgentRecordIdSchema,
|
|
306
|
-
conversationId: AgentRecordIdSchema,
|
|
307
|
-
runId: AgentRecordIdSchema.optional(),
|
|
308
|
-
role: AgentMessageRoleSchema,
|
|
309
|
-
status: AgentMessageStatusSchema,
|
|
310
|
-
parts: z.array(AgentMessagePartSchema),
|
|
311
|
-
metadata: AgentJsonObjectSchema.optional(),
|
|
312
|
-
createdAt: AgentTimestampSchema,
|
|
313
|
-
updatedAt: AgentTimestampSchema
|
|
314
|
-
});
|
|
315
|
-
var AgentRunStateSchema = z.enum([
|
|
316
|
-
"queued",
|
|
317
|
-
"running",
|
|
318
|
-
"interrupt_requested",
|
|
319
|
-
"completed",
|
|
320
|
-
"interrupted",
|
|
321
|
-
"failed",
|
|
322
|
-
"cancelled",
|
|
323
|
-
"abandoned"
|
|
324
|
-
]);
|
|
325
|
-
var AgentTerminalReasonSchema = z.enum([
|
|
326
|
-
"success",
|
|
327
|
-
"policy_stop",
|
|
328
|
-
"interrupted",
|
|
329
|
-
"cancelled",
|
|
330
|
-
"timeout",
|
|
331
|
-
"shutdown",
|
|
332
|
-
"provider_failure",
|
|
333
|
-
"tool_failure",
|
|
334
|
-
"abandoned"
|
|
335
|
-
]);
|
|
336
|
-
var AgentRunSchema = z.object({
|
|
337
|
-
schemaVersion: z.literal(1),
|
|
338
|
-
id: AgentRecordIdSchema,
|
|
339
|
-
conversationId: AgentRecordIdSchema,
|
|
340
|
-
inputMessageIds: z.array(AgentRecordIdSchema).min(1),
|
|
341
|
-
assistantMessageId: AgentRecordIdSchema,
|
|
342
|
-
state: AgentRunStateSchema,
|
|
343
|
-
revision: AgentRecordVersionSchema,
|
|
344
|
-
ownerId: z.string().min(1).optional(),
|
|
345
|
-
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
346
|
-
terminalPolicyName: z.string().min(1).optional(),
|
|
347
|
-
createdAt: AgentTimestampSchema,
|
|
348
|
-
updatedAt: AgentTimestampSchema
|
|
349
|
-
});
|
|
350
|
-
var AgentSnapshotSchema = z.object({
|
|
351
|
-
schemaVersion: z.literal(1),
|
|
270
|
+
var AgentAdmissionEventSchema = z.object({
|
|
271
|
+
type: z.literal("admission"),
|
|
272
|
+
eventId: AgentRecordIdSchema,
|
|
352
273
|
conversationId: AgentRecordIdSchema,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
provenance: z.enum(["provider-reported", "computed", "estimated", "unavailable"])
|
|
360
|
-
});
|
|
361
|
-
var AgentCostValueSchema = z.object({
|
|
362
|
-
value: z.number().nonnegative().optional(),
|
|
363
|
-
currency: z.string().length(3).optional(),
|
|
364
|
-
provenance: z.enum(["provider-reported", "computed", "estimated", "unavailable"])
|
|
365
|
-
});
|
|
366
|
-
var AgentUsageSchema = z.object({
|
|
367
|
-
inputTokens: AgentUsageValueSchema,
|
|
368
|
-
outputTokens: AgentUsageValueSchema,
|
|
369
|
-
reasoningTokens: AgentUsageValueSchema.optional(),
|
|
370
|
-
cacheReadTokens: AgentUsageValueSchema.optional(),
|
|
371
|
-
cacheWriteTokens: AgentUsageValueSchema.optional(),
|
|
372
|
-
cost: AgentCostValueSchema.optional()
|
|
274
|
+
runId: AgentRecordIdSchema,
|
|
275
|
+
snapshotVersion: AgentRecordVersionSchema,
|
|
276
|
+
input: AgentMessageSchema,
|
|
277
|
+
run: AgentRunSchema,
|
|
278
|
+
assistant: z.union([AgentAssistantPlaceholderSchema, AgentMessageSchema]),
|
|
279
|
+
emittedAt: AgentTimestampSchema
|
|
373
280
|
});
|
|
374
|
-
|
|
375
|
-
// src/agent-runtime/events.ts
|
|
376
|
-
var EventIdentitySchema = z2.object({
|
|
281
|
+
var EventIdentitySchema = z.object({
|
|
377
282
|
conversationId: AgentRecordIdSchema,
|
|
378
283
|
runId: AgentRecordIdSchema,
|
|
379
284
|
emittedAt: AgentTimestampSchema
|
|
380
285
|
});
|
|
381
286
|
var AgentTransientDeltaEventSchema = EventIdentitySchema.extend({
|
|
382
|
-
type:
|
|
383
|
-
runtimeEpoch:
|
|
384
|
-
sequence:
|
|
385
|
-
textDelta:
|
|
287
|
+
type: z.literal("assistant-delta"),
|
|
288
|
+
runtimeEpoch: z.string().min(1),
|
|
289
|
+
sequence: z.int().nonnegative(),
|
|
290
|
+
textDelta: z.string()
|
|
386
291
|
});
|
|
387
292
|
var AgentTransientReasoningIdentitySchema = EventIdentitySchema.extend({
|
|
388
|
-
runtimeEpoch:
|
|
389
|
-
sequence:
|
|
293
|
+
runtimeEpoch: z.string().min(1),
|
|
294
|
+
sequence: z.int().nonnegative(),
|
|
390
295
|
provider: AgentProviderEnvelopeSchema.optional()
|
|
391
296
|
});
|
|
392
297
|
var AgentReasoningStartEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
393
|
-
type:
|
|
298
|
+
type: z.literal("reasoning-start")
|
|
394
299
|
});
|
|
395
300
|
var AgentReasoningDeltaEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
396
|
-
type:
|
|
397
|
-
textDelta:
|
|
301
|
+
type: z.literal("reasoning-delta"),
|
|
302
|
+
textDelta: z.string()
|
|
398
303
|
});
|
|
399
304
|
var AgentReasoningEndEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
400
|
-
type:
|
|
305
|
+
type: z.literal("reasoning-end")
|
|
401
306
|
});
|
|
402
307
|
var AgentCheckpointEventSchema = EventIdentitySchema.extend({
|
|
403
|
-
type:
|
|
308
|
+
type: z.literal("assistant-checkpoint"),
|
|
404
309
|
eventId: AgentRecordIdSchema,
|
|
405
310
|
snapshotVersion: AgentRecordVersionSchema,
|
|
406
|
-
message: AgentMessageSchema
|
|
311
|
+
message: AgentMessageSchema,
|
|
312
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
407
313
|
});
|
|
408
314
|
var AgentRunStateEventSchema = EventIdentitySchema.extend({
|
|
409
|
-
type:
|
|
315
|
+
type: z.literal("run-state"),
|
|
410
316
|
eventId: AgentRecordIdSchema,
|
|
411
317
|
snapshotVersion: AgentRecordVersionSchema,
|
|
412
318
|
state: AgentRunStateSchema
|
|
413
319
|
});
|
|
414
320
|
var AgentToolStatusEventSchema = EventIdentitySchema.extend({
|
|
415
|
-
type:
|
|
416
|
-
runtimeEpoch:
|
|
417
|
-
sequence:
|
|
321
|
+
type: z.literal("tool-status"),
|
|
322
|
+
runtimeEpoch: z.string().min(1),
|
|
323
|
+
sequence: z.int().nonnegative(),
|
|
418
324
|
callId: AgentRecordIdSchema,
|
|
419
|
-
toolName:
|
|
420
|
-
status:
|
|
421
|
-
input:
|
|
422
|
-
output:
|
|
325
|
+
toolName: z.string().min(1),
|
|
326
|
+
status: z.enum(["started", "completed", "failed", "interrupted"]),
|
|
327
|
+
input: z.json().optional(),
|
|
328
|
+
output: z.json().optional()
|
|
423
329
|
});
|
|
424
330
|
var AgentTerminalEventSchema = EventIdentitySchema.extend({
|
|
425
|
-
type:
|
|
331
|
+
type: z.literal("terminal"),
|
|
426
332
|
eventId: AgentRecordIdSchema,
|
|
427
333
|
snapshotVersion: AgentRecordVersionSchema,
|
|
428
334
|
reason: AgentTerminalReasonSchema,
|
|
429
|
-
policyName:
|
|
430
|
-
message: AgentMessageSchema
|
|
335
|
+
policyName: z.string().min(1).optional(),
|
|
336
|
+
message: AgentMessageSchema,
|
|
337
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
431
338
|
});
|
|
432
|
-
var AgentRuntimeEventSchema =
|
|
339
|
+
var AgentRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
340
|
+
AgentAdmissionEventSchema,
|
|
433
341
|
AgentTransientDeltaEventSchema,
|
|
434
342
|
AgentReasoningStartEventSchema,
|
|
435
343
|
AgentReasoningDeltaEventSchema,
|
|
@@ -439,6 +347,63 @@ var AgentRuntimeEventSchema = z2.discriminatedUnion("type", [
|
|
|
439
347
|
AgentToolStatusEventSchema,
|
|
440
348
|
AgentTerminalEventSchema
|
|
441
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
|
+
}
|
|
442
407
|
// src/agent-runtime/history.ts
|
|
443
408
|
import { modelMessageSchema } from "ai";
|
|
444
409
|
function providerOptions(envelope) {
|
|
@@ -520,26 +485,75 @@ function assistantMessages(message) {
|
|
|
520
485
|
}
|
|
521
486
|
return messages;
|
|
522
487
|
}
|
|
523
|
-
|
|
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 = {}) {
|
|
524
494
|
const projected = [];
|
|
495
|
+
const decisions = [];
|
|
496
|
+
let observedUser = false;
|
|
525
497
|
for (const message of messages) {
|
|
526
|
-
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" });
|
|
527
500
|
continue;
|
|
501
|
+
}
|
|
528
502
|
if (message.role === "user") {
|
|
529
503
|
const user = await userMessage(message, options);
|
|
530
|
-
|
|
504
|
+
observedUser = true;
|
|
505
|
+
if (user) {
|
|
531
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
|
+
}
|
|
532
511
|
continue;
|
|
533
512
|
}
|
|
534
513
|
if (message.role === "system" || message.role === "summary") {
|
|
535
514
|
const content = textContent(message.parts);
|
|
536
|
-
if (content)
|
|
515
|
+
if (content) {
|
|
537
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
|
+
}
|
|
538
521
|
continue;
|
|
539
522
|
}
|
|
540
|
-
|
|
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
|
+
});
|
|
541
552
|
}
|
|
542
|
-
return projected;
|
|
553
|
+
return { messages: projected, decisions };
|
|
554
|
+
}
|
|
555
|
+
async function projectAgentHistory(messages, options = {}) {
|
|
556
|
+
return [...(await projectAgentHistoryDetailed(messages, options)).messages];
|
|
543
557
|
}
|
|
544
558
|
// src/agent-runtime/managed-tools.ts
|
|
545
559
|
async function assertFence(config, input) {
|
|
@@ -565,15 +579,22 @@ function createAgentToolFenceLifecycle(config) {
|
|
|
565
579
|
};
|
|
566
580
|
}
|
|
567
581
|
// src/agent-runtime/models.ts
|
|
568
|
-
import { z as
|
|
569
|
-
var AgentModelCapabilitySchema =
|
|
570
|
-
var AgentModelDescriptorSchema =
|
|
571
|
-
provider:
|
|
572
|
-
modelId:
|
|
573
|
-
contextWindow:
|
|
574
|
-
capabilities:
|
|
575
|
-
observedAt:
|
|
576
|
-
source:
|
|
582
|
+
import { z as z2 } from "zod";
|
|
583
|
+
var AgentModelCapabilitySchema = z2.enum(["tools", "vision", "reasoning", "files"]);
|
|
584
|
+
var AgentModelDescriptorSchema = z2.object({
|
|
585
|
+
provider: z2.string().min(1),
|
|
586
|
+
modelId: z2.string().min(1),
|
|
587
|
+
contextWindow: z2.int().positive(),
|
|
588
|
+
capabilities: z2.array(AgentModelCapabilitySchema),
|
|
589
|
+
observedAt: z2.iso.datetime({ offset: true }).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)
|
|
577
598
|
});
|
|
578
599
|
function defineModelRegistry(config) {
|
|
579
600
|
const descriptors = new Map;
|
|
@@ -592,15 +613,26 @@ function defineModelRegistry(config) {
|
|
|
592
613
|
const available = new Set(descriptor(key).capabilities);
|
|
593
614
|
return capabilities.every((capability) => available.has(capability));
|
|
594
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
|
+
};
|
|
595
629
|
return {
|
|
596
630
|
keys: () => [...descriptors.keys()],
|
|
597
631
|
descriptor,
|
|
598
632
|
supports,
|
|
633
|
+
preflight,
|
|
599
634
|
resolve(key, required = []) {
|
|
600
|
-
const selected =
|
|
601
|
-
if (!supports(key, required)) {
|
|
602
|
-
throw new Error(`Agent model ${key} does not satisfy required capabilities`);
|
|
603
|
-
}
|
|
635
|
+
const selected = preflight(key, required);
|
|
604
636
|
const provider = config.providers[selected.provider];
|
|
605
637
|
if (!provider)
|
|
606
638
|
throw new Error(`Unknown agent model provider: ${selected.provider}`);
|
|
@@ -609,29 +641,49 @@ function defineModelRegistry(config) {
|
|
|
609
641
|
model: provider.create(selected.modelId),
|
|
610
642
|
...provider.normalizeUsage && { normalizeUsage: provider.normalizeUsage }
|
|
611
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
|
+
});
|
|
612
652
|
}
|
|
613
653
|
};
|
|
614
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
|
+
}
|
|
615
667
|
// src/agent-runtime/observability.ts
|
|
616
|
-
import { z as
|
|
617
|
-
var AgentRunEventSchema =
|
|
618
|
-
schemaVersion:
|
|
668
|
+
import { z as z3 } from "zod";
|
|
669
|
+
var AgentRunEventSchema = z3.object({
|
|
670
|
+
schemaVersion: z3.literal(1),
|
|
619
671
|
eventId: AgentRecordIdSchema,
|
|
620
|
-
type:
|
|
672
|
+
type: z3.enum(["run-started", "step-finished", "run-terminal"]),
|
|
621
673
|
conversationId: AgentRecordIdSchema,
|
|
622
674
|
runId: AgentRecordIdSchema,
|
|
623
|
-
traceId:
|
|
624
|
-
spanId:
|
|
625
|
-
parentSpanId:
|
|
675
|
+
traceId: z3.string().min(1),
|
|
676
|
+
spanId: z3.string().min(1),
|
|
677
|
+
parentSpanId: z3.string().min(1).optional(),
|
|
626
678
|
state: AgentRunStateSchema,
|
|
627
679
|
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
628
|
-
modelId:
|
|
629
|
-
step:
|
|
630
|
-
queueWaitMs:
|
|
631
|
-
durationMs:
|
|
632
|
-
ttftMs:
|
|
680
|
+
modelId: z3.string().min(1).optional(),
|
|
681
|
+
step: z3.int().nonnegative().optional(),
|
|
682
|
+
queueWaitMs: z3.number().nonnegative().optional(),
|
|
683
|
+
durationMs: z3.number().nonnegative().optional(),
|
|
684
|
+
ttftMs: z3.number().nonnegative().optional(),
|
|
633
685
|
usage: AgentUsageSchema.optional(),
|
|
634
|
-
internalCause:
|
|
686
|
+
internalCause: z3.unknown().optional(),
|
|
635
687
|
emittedAt: AgentTimestampSchema
|
|
636
688
|
});
|
|
637
689
|
function createAgentObservability(config) {
|
|
@@ -642,13 +694,18 @@ function createAgentObservability(config) {
|
|
|
642
694
|
...config.onSinkError && { onSinkError: config.onSinkError },
|
|
643
695
|
...config.onDrop && { onDrop: config.onDrop }
|
|
644
696
|
});
|
|
697
|
+
const emitted = new Set;
|
|
645
698
|
return {
|
|
646
699
|
rootTrace(parent) {
|
|
647
700
|
const trace = parent ? childSpan(parent) : createTraceContext();
|
|
648
701
|
return trace;
|
|
649
702
|
},
|
|
650
703
|
emit(rawEvent) {
|
|
651
|
-
|
|
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));
|
|
652
709
|
},
|
|
653
710
|
flush: () => manager.flush(),
|
|
654
711
|
getStatus: () => manager.getStatus(),
|
|
@@ -656,11 +713,115 @@ function createAgentObservability(config) {
|
|
|
656
713
|
};
|
|
657
714
|
}
|
|
658
715
|
// src/agent-runtime/prompt.ts
|
|
659
|
-
import { z as
|
|
660
|
-
var AgentTokenCountSchema =
|
|
661
|
-
value:
|
|
662
|
-
provenance:
|
|
716
|
+
import { z as z4 } from "zod";
|
|
717
|
+
var AgentTokenCountSchema = z4.object({
|
|
718
|
+
value: z4.int().nonnegative().optional(),
|
|
719
|
+
provenance: z4.enum(["measured", "estimated", "unavailable"])
|
|
663
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
|
+
}
|
|
664
825
|
function knownValue(value) {
|
|
665
826
|
return value.provenance === "unavailable" ? undefined : value.value;
|
|
666
827
|
}
|
|
@@ -738,7 +899,7 @@ import {
|
|
|
738
899
|
stepCountIs,
|
|
739
900
|
streamText
|
|
740
901
|
} from "ai";
|
|
741
|
-
import { z as
|
|
902
|
+
import { z as z5 } from "zod";
|
|
742
903
|
class AgentRuntimeConflictError extends Error {
|
|
743
904
|
constructor(operation) {
|
|
744
905
|
super(`Agent runtime store conflict during ${operation}`);
|
|
@@ -757,7 +918,7 @@ function findRun(runs, runId) {
|
|
|
757
918
|
return run;
|
|
758
919
|
}
|
|
759
920
|
function jsonValue(value) {
|
|
760
|
-
const parsed =
|
|
921
|
+
const parsed = z5.json().safeParse(value);
|
|
761
922
|
return parsed.success ? parsed.data : { message: "Non-JSON tool output omitted" };
|
|
762
923
|
}
|
|
763
924
|
function providerEnvelope(value) {
|
|
@@ -899,7 +1060,11 @@ function createAgentRuntime(config) {
|
|
|
899
1060
|
const publish = async (event) => {
|
|
900
1061
|
try {
|
|
901
1062
|
await config.publish?.(event);
|
|
902
|
-
} catch {
|
|
1063
|
+
} catch (error) {
|
|
1064
|
+
try {
|
|
1065
|
+
await config.onPublishError?.({ event, error });
|
|
1066
|
+
} catch {}
|
|
1067
|
+
}
|
|
903
1068
|
};
|
|
904
1069
|
const executeRun = async (input) => {
|
|
905
1070
|
const queuedSnapshot = await config.store.loadSnapshot(input.acceptedRun.conversationId);
|
|
@@ -913,7 +1078,7 @@ function createAgentRuntime(config) {
|
|
|
913
1078
|
let run = findRun(acquired.runs, input.acceptedRun.id);
|
|
914
1079
|
await publish({
|
|
915
1080
|
type: "run-state",
|
|
916
|
-
eventId:
|
|
1081
|
+
eventId: agentDurableEventId("run-state", run.id, acquired.version),
|
|
917
1082
|
conversationId: run.conversationId,
|
|
918
1083
|
runId: run.id,
|
|
919
1084
|
snapshotVersion: acquired.version,
|
|
@@ -951,6 +1116,7 @@ function createAgentRuntime(config) {
|
|
|
951
1116
|
runId: run.id,
|
|
952
1117
|
expectedRevision: run.revision,
|
|
953
1118
|
ownerId: runtimeEpoch,
|
|
1119
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
954
1120
|
assistant
|
|
955
1121
|
}), "assistant draft");
|
|
956
1122
|
run = findRun(snapshot.runs, run.id);
|
|
@@ -999,16 +1165,24 @@ function createAgentRuntime(config) {
|
|
|
999
1165
|
runId: run.id,
|
|
1000
1166
|
expectedRevision: run.revision,
|
|
1001
1167
|
ownerId: runtimeEpoch,
|
|
1168
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
1002
1169
|
assistant
|
|
1003
1170
|
}), "assistant checkpoint");
|
|
1004
1171
|
run = findRun(snapshot.runs, run.id);
|
|
1172
|
+
const checkpointMetrics = {
|
|
1173
|
+
partial: true,
|
|
1174
|
+
durationMs: performance.now() - runStartedAt,
|
|
1175
|
+
...usage && { usage },
|
|
1176
|
+
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
1177
|
+
};
|
|
1005
1178
|
await publish({
|
|
1006
1179
|
type: "assistant-checkpoint",
|
|
1007
|
-
eventId:
|
|
1180
|
+
eventId: agentDurableEventId("assistant-checkpoint", run.id, snapshot.version),
|
|
1008
1181
|
conversationId: run.conversationId,
|
|
1009
1182
|
runId: run.id,
|
|
1010
1183
|
snapshotVersion: snapshot.version,
|
|
1011
1184
|
message: assistant,
|
|
1185
|
+
metrics: checkpointMetrics,
|
|
1012
1186
|
emittedAt: now().toISOString()
|
|
1013
1187
|
});
|
|
1014
1188
|
};
|
|
@@ -1029,6 +1203,8 @@ function createAgentRuntime(config) {
|
|
|
1029
1203
|
const currentRun = current.runs.find((candidate) => candidate.id === run.id);
|
|
1030
1204
|
if (!currentRun || currentRun.ownerId !== runtimeEpoch)
|
|
1031
1205
|
return "stale_run";
|
|
1206
|
+
if (currentRun.fencingToken !== run.fencingToken)
|
|
1207
|
+
return "stale_run";
|
|
1032
1208
|
if (currentRun.state === "interrupt_requested")
|
|
1033
1209
|
return "run_interrupted";
|
|
1034
1210
|
if (currentRun.state !== "running")
|
|
@@ -1037,7 +1213,10 @@ function createAgentRuntime(config) {
|
|
|
1037
1213
|
};
|
|
1038
1214
|
const toolFenceLifecycle = createAgentToolFenceLifecycle({
|
|
1039
1215
|
runId: run.id,
|
|
1040
|
-
assertCurrent
|
|
1216
|
+
assertCurrent,
|
|
1217
|
+
context: () => ({
|
|
1218
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken }
|
|
1219
|
+
})
|
|
1041
1220
|
});
|
|
1042
1221
|
const runtimeContext = {
|
|
1043
1222
|
context: input.context,
|
|
@@ -1373,14 +1552,21 @@ function createAgentRuntime(config) {
|
|
|
1373
1552
|
runId: run.id,
|
|
1374
1553
|
expectedRevision: run.revision,
|
|
1375
1554
|
ownerId: runtimeEpoch,
|
|
1555
|
+
...run.fencingToken !== undefined && { fencingToken: run.fencingToken },
|
|
1376
1556
|
assistant,
|
|
1377
1557
|
reason: terminalReason,
|
|
1378
1558
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1379
1559
|
}), "terminal commit");
|
|
1380
1560
|
run = findRun(snapshot.runs, run.id);
|
|
1561
|
+
const terminalMetrics = {
|
|
1562
|
+
partial: false,
|
|
1563
|
+
durationMs: performance.now() - runStartedAt,
|
|
1564
|
+
...usage && { usage },
|
|
1565
|
+
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
1566
|
+
};
|
|
1381
1567
|
config.observe?.emit({
|
|
1382
1568
|
schemaVersion: 1,
|
|
1383
|
-
eventId:
|
|
1569
|
+
eventId: agentDurableEventId("terminal", run.id, snapshot.version),
|
|
1384
1570
|
type: "run-terminal",
|
|
1385
1571
|
conversationId: run.conversationId,
|
|
1386
1572
|
runId: run.id,
|
|
@@ -1390,7 +1576,7 @@ function createAgentRuntime(config) {
|
|
|
1390
1576
|
state: run.state,
|
|
1391
1577
|
terminalReason,
|
|
1392
1578
|
...selectedModel && { modelId: selectedModel.descriptor.modelId },
|
|
1393
|
-
durationMs:
|
|
1579
|
+
durationMs: terminalMetrics.durationMs,
|
|
1394
1580
|
...usage && { usage },
|
|
1395
1581
|
...internalCause !== undefined && { internalCause },
|
|
1396
1582
|
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt },
|
|
@@ -1398,13 +1584,14 @@ function createAgentRuntime(config) {
|
|
|
1398
1584
|
});
|
|
1399
1585
|
await publish({
|
|
1400
1586
|
type: "terminal",
|
|
1401
|
-
eventId:
|
|
1587
|
+
eventId: agentDurableEventId("terminal", run.id, snapshot.version),
|
|
1402
1588
|
conversationId: run.conversationId,
|
|
1403
1589
|
runId: run.id,
|
|
1404
1590
|
snapshotVersion: snapshot.version,
|
|
1405
1591
|
reason: terminalReason,
|
|
1406
1592
|
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
1407
1593
|
message: assistant,
|
|
1594
|
+
metrics: terminalMetrics,
|
|
1408
1595
|
emittedAt: now().toISOString()
|
|
1409
1596
|
});
|
|
1410
1597
|
return {
|
|
@@ -1412,9 +1599,38 @@ function createAgentRuntime(config) {
|
|
|
1412
1599
|
message: assistant,
|
|
1413
1600
|
reason: terminalReason,
|
|
1414
1601
|
snapshotVersion: snapshot.version,
|
|
1602
|
+
metrics: terminalMetrics,
|
|
1415
1603
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1416
1604
|
};
|
|
1417
1605
|
};
|
|
1606
|
+
const resume = (rawInput) => {
|
|
1607
|
+
const context = config.protocol.parseContext(rawInput.context);
|
|
1608
|
+
const accepted = Promise.withResolvers();
|
|
1609
|
+
const result = Promise.withResolvers();
|
|
1610
|
+
(async () => {
|
|
1611
|
+
try {
|
|
1612
|
+
const snapshot = await config.store.loadSnapshot(rawInput.conversationId);
|
|
1613
|
+
const recoveredRun = findRun(snapshot.runs, rawInput.runId);
|
|
1614
|
+
if (recoveredRun.state !== "queued") {
|
|
1615
|
+
throw new Error("Only a queued recovered agent run can be resumed");
|
|
1616
|
+
}
|
|
1617
|
+
accepted.resolve();
|
|
1618
|
+
const ticket = coordinator.submit({
|
|
1619
|
+
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
1620
|
+
policy: "queue",
|
|
1621
|
+
create: (signal) => ({
|
|
1622
|
+
runId: recoveredRun.id,
|
|
1623
|
+
execute: () => executeRun({ acceptedRun: recoveredRun, context, signal })
|
|
1624
|
+
})
|
|
1625
|
+
});
|
|
1626
|
+
ticket.result.then(result.resolve, result.reject);
|
|
1627
|
+
} catch (error) {
|
|
1628
|
+
accepted.reject(error);
|
|
1629
|
+
result.reject(error);
|
|
1630
|
+
}
|
|
1631
|
+
})();
|
|
1632
|
+
return { accepted: accepted.promise, result: result.promise };
|
|
1633
|
+
};
|
|
1418
1634
|
return {
|
|
1419
1635
|
submit(rawInput) {
|
|
1420
1636
|
const metadata = rawInput.metadata === undefined ? undefined : config.protocol.parseInputMetadata(rawInput.metadata);
|
|
@@ -1494,6 +1710,10 @@ function createAgentRuntime(config) {
|
|
|
1494
1710
|
await previousAcceptance.catch(() => {
|
|
1495
1711
|
return;
|
|
1496
1712
|
});
|
|
1713
|
+
await config.models.preflight?.({
|
|
1714
|
+
context,
|
|
1715
|
+
conversationId: input.conversationId
|
|
1716
|
+
});
|
|
1497
1717
|
const acceptance = await config.store.acceptInputAndAssignRun({
|
|
1498
1718
|
idempotencyKey: input.idempotencyKey,
|
|
1499
1719
|
input: userMessage2,
|
|
@@ -1505,14 +1725,45 @@ function createAgentRuntime(config) {
|
|
|
1505
1725
|
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
1506
1726
|
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
1507
1727
|
const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1508
|
-
|
|
1728
|
+
const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
|
|
1729
|
+
const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
|
|
1730
|
+
if (!acceptedInput) {
|
|
1731
|
+
throw new AgentRuntimeConflictError("admission input projection");
|
|
1732
|
+
}
|
|
1733
|
+
const assistantPlaceholder = AgentAssistantPlaceholderSchema.parse({
|
|
1734
|
+
schemaVersion: 1,
|
|
1735
|
+
id: acceptedRun.assistantMessageId,
|
|
1736
|
+
conversationId: acceptedRun.conversationId,
|
|
1509
1737
|
runId: acceptedRun.id,
|
|
1510
|
-
|
|
1738
|
+
status: "pending",
|
|
1739
|
+
createdAt: acceptedRun.createdAt,
|
|
1740
|
+
updatedAt: acceptedRun.updatedAt
|
|
1741
|
+
});
|
|
1742
|
+
const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptedSnapshot.messages.find((candidate) => candidate.id === acceptedRun.assistantMessageId) ?? assistantPlaceholder : assistantPlaceholder;
|
|
1743
|
+
const admission = {
|
|
1744
|
+
inputMessageId: acceptedInput.id,
|
|
1745
|
+
runId: acceptedRun.id,
|
|
1746
|
+
assistantMessageId: assistantPlaceholder.id,
|
|
1747
|
+
input: acceptedInput,
|
|
1748
|
+
run: acceptedRun,
|
|
1749
|
+
assistant: acceptedAssistant,
|
|
1511
1750
|
snapshotVersion: acceptedSnapshot.version
|
|
1751
|
+
};
|
|
1752
|
+
outerAdmission.resolve(admission);
|
|
1753
|
+
await publish({
|
|
1754
|
+
type: "admission",
|
|
1755
|
+
eventId: agentDurableEventId("admission", acceptedRun.id, acceptedSnapshot.version),
|
|
1756
|
+
conversationId: acceptedRun.conversationId,
|
|
1757
|
+
runId: acceptedRun.id,
|
|
1758
|
+
snapshotVersion: acceptedSnapshot.version,
|
|
1759
|
+
input: acceptedInput,
|
|
1760
|
+
run: acceptedRun,
|
|
1761
|
+
assistant: acceptedAssistant,
|
|
1762
|
+
emittedAt: now().toISOString()
|
|
1512
1763
|
});
|
|
1513
1764
|
await publish({
|
|
1514
1765
|
type: "run-state",
|
|
1515
|
-
eventId:
|
|
1766
|
+
eventId: agentDurableEventId("run-state", acceptedRun.id, acceptedSnapshot.version),
|
|
1516
1767
|
conversationId: acceptedRun.conversationId,
|
|
1517
1768
|
runId: acceptedRun.id,
|
|
1518
1769
|
snapshotVersion: acceptedSnapshot.version,
|
|
@@ -1597,34 +1848,7 @@ function createAgentRuntime(config) {
|
|
|
1597
1848
|
})();
|
|
1598
1849
|
return publicTicket;
|
|
1599
1850
|
},
|
|
1600
|
-
resume
|
|
1601
|
-
const context = config.protocol.parseContext(rawInput.context);
|
|
1602
|
-
const accepted = Promise.withResolvers();
|
|
1603
|
-
const result = Promise.withResolvers();
|
|
1604
|
-
(async () => {
|
|
1605
|
-
try {
|
|
1606
|
-
const snapshot = await config.store.loadSnapshot(rawInput.conversationId);
|
|
1607
|
-
const recoveredRun = findRun(snapshot.runs, rawInput.runId);
|
|
1608
|
-
if (recoveredRun.state !== "queued") {
|
|
1609
|
-
throw new Error("Only a queued recovered agent run can be resumed");
|
|
1610
|
-
}
|
|
1611
|
-
accepted.resolve();
|
|
1612
|
-
const ticket = coordinator.submit({
|
|
1613
|
-
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
1614
|
-
policy: "queue",
|
|
1615
|
-
create: (signal) => ({
|
|
1616
|
-
runId: recoveredRun.id,
|
|
1617
|
-
execute: () => executeRun({ acceptedRun: recoveredRun, context, signal })
|
|
1618
|
-
})
|
|
1619
|
-
});
|
|
1620
|
-
ticket.result.then(result.resolve, result.reject);
|
|
1621
|
-
} catch (error) {
|
|
1622
|
-
accepted.reject(error);
|
|
1623
|
-
result.reject(error);
|
|
1624
|
-
}
|
|
1625
|
-
})();
|
|
1626
|
-
return { accepted: accepted.promise, result: result.promise };
|
|
1627
|
-
},
|
|
1851
|
+
resume,
|
|
1628
1852
|
async interrupt(input) {
|
|
1629
1853
|
const snapshot = await config.store.loadSnapshot(input.conversationId);
|
|
1630
1854
|
const run = findRun(snapshot.runs, input.runId);
|
|
@@ -1637,7 +1861,7 @@ function createAgentRuntime(config) {
|
|
|
1637
1861
|
const interruptedRun = findRun(requested.snapshot.runs, input.runId);
|
|
1638
1862
|
await publish({
|
|
1639
1863
|
type: "run-state",
|
|
1640
|
-
eventId:
|
|
1864
|
+
eventId: agentDurableEventId("run-state", interruptedRun.id, requested.snapshot.version),
|
|
1641
1865
|
conversationId: interruptedRun.conversationId,
|
|
1642
1866
|
runId: interruptedRun.id,
|
|
1643
1867
|
snapshotVersion: requested.snapshot.version,
|
|
@@ -1648,101 +1872,300 @@ function createAgentRuntime(config) {
|
|
|
1648
1872
|
}
|
|
1649
1873
|
return requested;
|
|
1650
1874
|
},
|
|
1875
|
+
async recover(options) {
|
|
1876
|
+
if (!config.store.scanRecoverablePage) {
|
|
1877
|
+
throw new Error("The configured agent store does not support bounded recovery scans");
|
|
1878
|
+
}
|
|
1879
|
+
const pageSize = options.pageSize ?? 100;
|
|
1880
|
+
const maxRuns = options.maxRuns ?? 1000;
|
|
1881
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 1000) {
|
|
1882
|
+
throw new TypeError("Recovery pageSize must be an integer between 1 and 1000");
|
|
1883
|
+
}
|
|
1884
|
+
if (!Number.isSafeInteger(maxRuns) || maxRuns < 1) {
|
|
1885
|
+
throw new TypeError("Recovery maxRuns must be a positive safe integer");
|
|
1886
|
+
}
|
|
1887
|
+
const outcomes = [];
|
|
1888
|
+
let cursor;
|
|
1889
|
+
while (outcomes.length < maxRuns && !options.signal?.aborted) {
|
|
1890
|
+
const page = await config.store.scanRecoverablePage({
|
|
1891
|
+
...cursor && { cursor },
|
|
1892
|
+
limit: Math.min(pageSize, maxRuns - outcomes.length)
|
|
1893
|
+
});
|
|
1894
|
+
for (const item of page.items) {
|
|
1895
|
+
if (options.signal?.aborted)
|
|
1896
|
+
break;
|
|
1897
|
+
try {
|
|
1898
|
+
if (item.run.state === "queued") {
|
|
1899
|
+
const snapshot = await config.store.loadSnapshot(item.conversationId);
|
|
1900
|
+
const blockedByAcquiredPredecessor = snapshot.runs.some((run) => run.id !== item.run.id && (run.state === "running" || run.state === "interrupt_requested"));
|
|
1901
|
+
if (blockedByAcquiredPredecessor) {
|
|
1902
|
+
outcomes.push({
|
|
1903
|
+
conversationId: item.conversationId,
|
|
1904
|
+
runId: item.run.id,
|
|
1905
|
+
outcome: "skipped"
|
|
1906
|
+
});
|
|
1907
|
+
continue;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
const decision = await options.decide?.(item) ?? (item.run.state === "queued" ? { action: "resume" } : { action: "skip" });
|
|
1911
|
+
if (decision.action === "skip") {
|
|
1912
|
+
outcomes.push({
|
|
1913
|
+
conversationId: item.conversationId,
|
|
1914
|
+
runId: item.run.id,
|
|
1915
|
+
outcome: "skipped"
|
|
1916
|
+
});
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
if (decision.action === "abandon") {
|
|
1920
|
+
const abandoned = await config.store.recoverRun({
|
|
1921
|
+
conversationId: item.conversationId,
|
|
1922
|
+
runId: item.run.id,
|
|
1923
|
+
expectedRevision: item.run.revision,
|
|
1924
|
+
action: "abandon"
|
|
1925
|
+
});
|
|
1926
|
+
if (abandoned.outcome !== "applied") {
|
|
1927
|
+
throw new AgentRuntimeConflictError("recovery abandon");
|
|
1928
|
+
}
|
|
1929
|
+
outcomes.push({
|
|
1930
|
+
conversationId: item.conversationId,
|
|
1931
|
+
runId: item.run.id,
|
|
1932
|
+
outcome: "abandoned"
|
|
1933
|
+
});
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
if (decision.action === "requeue") {
|
|
1937
|
+
const requeued = await config.store.recoverRun({
|
|
1938
|
+
conversationId: item.conversationId,
|
|
1939
|
+
runId: item.run.id,
|
|
1940
|
+
expectedRevision: item.run.revision,
|
|
1941
|
+
action: "requeue",
|
|
1942
|
+
replaySafe: true
|
|
1943
|
+
});
|
|
1944
|
+
if (requeued.outcome !== "applied") {
|
|
1945
|
+
throw new AgentRuntimeConflictError("recovery requeue");
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
const context = await options.resolveContext(item);
|
|
1949
|
+
const resumed = resume({
|
|
1950
|
+
conversationId: item.conversationId,
|
|
1951
|
+
runId: item.run.id,
|
|
1952
|
+
context
|
|
1953
|
+
});
|
|
1954
|
+
resumed.result.catch(() => {
|
|
1955
|
+
return;
|
|
1956
|
+
});
|
|
1957
|
+
await resumed.accepted;
|
|
1958
|
+
outcomes.push({
|
|
1959
|
+
conversationId: item.conversationId,
|
|
1960
|
+
runId: item.run.id,
|
|
1961
|
+
outcome: decision.action === "requeue" ? "requeued" : "resumed"
|
|
1962
|
+
});
|
|
1963
|
+
} catch (error) {
|
|
1964
|
+
outcomes.push({
|
|
1965
|
+
conversationId: item.conversationId,
|
|
1966
|
+
runId: item.run.id,
|
|
1967
|
+
outcome: "failed",
|
|
1968
|
+
error
|
|
1969
|
+
});
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
cursor = page.nextCursor;
|
|
1973
|
+
if (!cursor || page.items.length === 0)
|
|
1974
|
+
break;
|
|
1975
|
+
}
|
|
1976
|
+
return outcomes;
|
|
1977
|
+
},
|
|
1651
1978
|
stop: (conversationKey, reason) => coordinator.stop(conversationKey, reason),
|
|
1652
1979
|
close: (options) => coordinator.close(options)
|
|
1653
1980
|
};
|
|
1654
1981
|
}
|
|
1655
1982
|
// src/agent-runtime/store.ts
|
|
1656
|
-
import { z as
|
|
1657
|
-
var AgentStoreConflictSchema =
|
|
1658
|
-
outcome:
|
|
1983
|
+
import { z as z6 } from "zod";
|
|
1984
|
+
var AgentStoreConflictSchema = z6.object({
|
|
1985
|
+
outcome: z6.literal("conflict"),
|
|
1659
1986
|
actualVersion: AgentRecordVersionSchema
|
|
1660
1987
|
});
|
|
1661
|
-
var AgentStoreNotFoundSchema =
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
var AgentStoreAppliedSchema = z7.object({
|
|
1665
|
-
outcome: z7.literal("applied"),
|
|
1988
|
+
var AgentStoreNotFoundSchema = z6.object({ outcome: z6.literal("not_found") });
|
|
1989
|
+
var AgentStoreAppliedSchema = z6.object({
|
|
1990
|
+
outcome: z6.literal("applied"),
|
|
1666
1991
|
snapshot: AgentSnapshotSchema
|
|
1667
1992
|
});
|
|
1668
|
-
var AgentStoreDuplicateSchema =
|
|
1669
|
-
outcome:
|
|
1993
|
+
var AgentStoreDuplicateSchema = z6.object({
|
|
1994
|
+
outcome: z6.literal("duplicate"),
|
|
1995
|
+
input: AgentMessageSchema,
|
|
1996
|
+
inputMessageId: AgentRecordIdSchema,
|
|
1670
1997
|
runId: AgentRecordIdSchema,
|
|
1998
|
+
assistantMessageId: AgentRecordIdSchema,
|
|
1671
1999
|
snapshot: AgentSnapshotSchema
|
|
1672
2000
|
});
|
|
1673
|
-
var AgentStoreMutationResultSchema =
|
|
2001
|
+
var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
1674
2002
|
AgentStoreAppliedSchema,
|
|
1675
2003
|
AgentStoreDuplicateSchema,
|
|
1676
2004
|
AgentStoreConflictSchema,
|
|
1677
2005
|
AgentStoreNotFoundSchema
|
|
1678
2006
|
]);
|
|
1679
|
-
var AcceptInputAndAssignRunSchema =
|
|
1680
|
-
idempotencyKey:
|
|
2007
|
+
var AcceptInputAndAssignRunSchema = z6.object({
|
|
2008
|
+
idempotencyKey: z6.string().min(1),
|
|
1681
2009
|
expectedVersion: AgentRecordVersionSchema.optional(),
|
|
1682
2010
|
input: AgentMessageSchema,
|
|
1683
2011
|
run: AgentRunSchema,
|
|
1684
2012
|
coalesceIntoRunId: AgentRecordIdSchema.optional()
|
|
1685
2013
|
});
|
|
1686
|
-
var AcquireAgentRunSchema =
|
|
2014
|
+
var AcquireAgentRunSchema = z6.object({
|
|
1687
2015
|
conversationId: AgentRecordIdSchema,
|
|
1688
2016
|
runId: AgentRecordIdSchema,
|
|
1689
2017
|
expectedRevision: AgentRecordVersionSchema,
|
|
1690
|
-
ownerId:
|
|
2018
|
+
ownerId: z6.string().min(1)
|
|
1691
2019
|
});
|
|
1692
|
-
var CheckpointRunAssistantSchema =
|
|
2020
|
+
var CheckpointRunAssistantSchema = z6.object({
|
|
1693
2021
|
conversationId: AgentRecordIdSchema,
|
|
1694
2022
|
runId: AgentRecordIdSchema,
|
|
1695
2023
|
expectedRevision: AgentRecordVersionSchema,
|
|
1696
|
-
ownerId:
|
|
2024
|
+
ownerId: z6.string().min(1),
|
|
2025
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1697
2026
|
assistant: AgentMessageSchema
|
|
1698
2027
|
});
|
|
1699
|
-
var CommitRunTerminalSchema =
|
|
2028
|
+
var CommitRunTerminalSchema = z6.object({
|
|
1700
2029
|
conversationId: AgentRecordIdSchema,
|
|
1701
2030
|
runId: AgentRecordIdSchema,
|
|
1702
2031
|
expectedRevision: AgentRecordVersionSchema,
|
|
1703
|
-
ownerId:
|
|
2032
|
+
ownerId: z6.string().min(1),
|
|
2033
|
+
fencingToken: AgentRecordVersionSchema.optional(),
|
|
1704
2034
|
assistant: AgentMessageSchema,
|
|
1705
2035
|
reason: AgentTerminalReasonSchema,
|
|
1706
|
-
policyName:
|
|
2036
|
+
policyName: z6.string().min(1).optional()
|
|
1707
2037
|
});
|
|
1708
|
-
var RequestRunInterruptSchema =
|
|
2038
|
+
var RequestRunInterruptSchema = z6.object({
|
|
1709
2039
|
conversationId: AgentRecordIdSchema,
|
|
1710
2040
|
runId: AgentRecordIdSchema,
|
|
1711
2041
|
expectedRevision: AgentRecordVersionSchema
|
|
1712
2042
|
});
|
|
1713
|
-
var RecoverAgentRunSchema =
|
|
2043
|
+
var RecoverAgentRunSchema = z6.object({
|
|
1714
2044
|
conversationId: AgentRecordIdSchema,
|
|
1715
2045
|
runId: AgentRecordIdSchema,
|
|
1716
2046
|
expectedRevision: AgentRecordVersionSchema,
|
|
1717
|
-
action:
|
|
1718
|
-
replaySafe:
|
|
2047
|
+
action: z6.enum(["requeue", "abandon"]),
|
|
2048
|
+
replaySafe: z6.boolean().optional()
|
|
1719
2049
|
});
|
|
1720
|
-
var ReplaceCompactedRangeSchema =
|
|
2050
|
+
var ReplaceCompactedRangeSchema = z6.object({
|
|
1721
2051
|
conversationId: AgentRecordIdSchema,
|
|
1722
2052
|
expectedVersion: AgentRecordVersionSchema,
|
|
1723
|
-
replacedMessageIds:
|
|
2053
|
+
replacedMessageIds: z6.array(AgentRecordIdSchema).min(1),
|
|
1724
2054
|
summary: AgentMessageSchema
|
|
1725
2055
|
});
|
|
1726
|
-
|
|
1727
|
-
|
|
2056
|
+
// src/agent-runtime/store-driver.ts
|
|
2057
|
+
import { z as z7 } from "zod";
|
|
2058
|
+
var AgentAdmissionIdentitySchema = z7.object({
|
|
2059
|
+
idempotencyKey: z7.string().min(1),
|
|
2060
|
+
inputMessageId: AgentRecordIdSchema,
|
|
2061
|
+
runId: AgentRecordIdSchema,
|
|
2062
|
+
assistantMessageId: AgentRecordIdSchema
|
|
2063
|
+
});
|
|
2064
|
+
var AgentStoredStateSchema = z7.object({
|
|
2065
|
+
schemaVersion: z7.literal(1),
|
|
2066
|
+
conversationId: AgentRecordIdSchema,
|
|
2067
|
+
version: AgentRecordVersionSchema,
|
|
2068
|
+
runs: z7.array(AgentRunSchema),
|
|
2069
|
+
admissions: z7.array(AgentAdmissionIdentitySchema)
|
|
2070
|
+
});
|
|
2071
|
+
var AgentHistoryMutationSchema = z7.discriminatedUnion("type", [
|
|
2072
|
+
z7.object({ type: z7.literal("admit"), input: AgentMessageSchema }),
|
|
2073
|
+
z7.object({
|
|
2074
|
+
type: z7.literal("upsert-assistant"),
|
|
2075
|
+
message: AgentMessageSchema
|
|
2076
|
+
}),
|
|
2077
|
+
z7.object({
|
|
2078
|
+
type: z7.literal("replace-compacted-range"),
|
|
2079
|
+
replacedMessageIds: z7.array(AgentRecordIdSchema).min(1),
|
|
2080
|
+
summary: AgentMessageSchema
|
|
2081
|
+
})
|
|
2082
|
+
]);
|
|
2083
|
+
var AgentRecoverableDescriptorSchema = z7.object({
|
|
2084
|
+
conversationId: AgentRecordIdSchema,
|
|
2085
|
+
run: AgentRunSchema
|
|
2086
|
+
});
|
|
2087
|
+
var AgentRecoverablePageSchema = z7.object({
|
|
2088
|
+
items: z7.array(AgentRecoverableDescriptorSchema),
|
|
2089
|
+
nextCursor: z7.string().min(1).optional()
|
|
2090
|
+
});
|
|
2091
|
+
var AgentRecoverableScanInputSchema = z7.object({
|
|
2092
|
+
cursor: z7.string().min(1).optional(),
|
|
2093
|
+
limit: z7.number().int().min(1).max(1000)
|
|
2094
|
+
});
|
|
2095
|
+
function emptyState(conversationId) {
|
|
2096
|
+
return AgentStoredStateSchema.parse({
|
|
1728
2097
|
schemaVersion: 1,
|
|
1729
2098
|
conversationId,
|
|
1730
2099
|
version: 0,
|
|
1731
|
-
|
|
1732
|
-
|
|
2100
|
+
runs: [],
|
|
2101
|
+
admissions: []
|
|
2102
|
+
});
|
|
2103
|
+
}
|
|
2104
|
+
function snapshotOf(state, messages) {
|
|
2105
|
+
validateAggregate(state, messages);
|
|
2106
|
+
return AgentSnapshotSchema.parse({
|
|
2107
|
+
schemaVersion: 1,
|
|
2108
|
+
conversationId: state.conversationId,
|
|
2109
|
+
version: state.version,
|
|
2110
|
+
messages,
|
|
2111
|
+
runs: state.runs
|
|
1733
2112
|
});
|
|
1734
2113
|
}
|
|
1735
|
-
function
|
|
1736
|
-
|
|
2114
|
+
function validateAggregate(state, messages) {
|
|
2115
|
+
const runIds = new Set;
|
|
2116
|
+
const assistantIds = new Set;
|
|
2117
|
+
const messageIds = new Set;
|
|
2118
|
+
const idempotencyKeys = new Set;
|
|
2119
|
+
const admittedInputIds = new Set;
|
|
2120
|
+
for (const run of state.runs) {
|
|
2121
|
+
if (run.conversationId !== state.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
|
|
2122
|
+
throw new TypeError("Stored agent state contains inconsistent run identities");
|
|
2123
|
+
}
|
|
2124
|
+
runIds.add(run.id);
|
|
2125
|
+
assistantIds.add(run.assistantMessageId);
|
|
2126
|
+
}
|
|
2127
|
+
for (const message of messages) {
|
|
2128
|
+
if (message.conversationId !== state.conversationId || messageIds.has(message.id)) {
|
|
2129
|
+
throw new TypeError("Stored agent history contains inconsistent message identities");
|
|
2130
|
+
}
|
|
2131
|
+
messageIds.add(message.id);
|
|
2132
|
+
if (assistantIds.has(message.id) && message.runId === undefined) {
|
|
2133
|
+
throw new TypeError("Stored history occupies a reserved assistant identity");
|
|
2134
|
+
}
|
|
2135
|
+
if (message.runId !== undefined) {
|
|
2136
|
+
const run = state.runs.find((candidate) => candidate.id === message.runId);
|
|
2137
|
+
if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
|
|
2138
|
+
throw new TypeError("Stored assistant history does not match its reserved run identity");
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
for (const admission of state.admissions) {
|
|
2143
|
+
const run = state.runs.find((candidate) => candidate.id === admission.runId);
|
|
2144
|
+
if (idempotencyKeys.has(admission.idempotencyKey) || admittedInputIds.has(admission.inputMessageId) || !run || run.assistantMessageId !== admission.assistantMessageId || !run.inputMessageIds.includes(admission.inputMessageId)) {
|
|
2145
|
+
throw new TypeError("Stored admission identity is inconsistent with its assigned run");
|
|
2146
|
+
}
|
|
2147
|
+
idempotencyKeys.add(admission.idempotencyKey);
|
|
2148
|
+
admittedInputIds.add(admission.inputMessageId);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
function recoverableDescriptors(state) {
|
|
2152
|
+
return state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }));
|
|
2153
|
+
}
|
|
2154
|
+
var RecoverableCursorSchema = z7.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
|
|
2155
|
+
function recoverableCursor(input) {
|
|
2156
|
+
return JSON.stringify([input.conversationId, input.run.id]);
|
|
2157
|
+
}
|
|
2158
|
+
function parseRecoverableCursor(cursor) {
|
|
2159
|
+
return RecoverableCursorSchema.parse(JSON.parse(cursor));
|
|
1737
2160
|
}
|
|
1738
2161
|
function replaceRun(runs, next) {
|
|
1739
2162
|
return runs.map((run) => run.id === next.id ? next : run);
|
|
1740
2163
|
}
|
|
1741
2164
|
function replaceMessage(messages, next) {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
return
|
|
2165
|
+
return messages.some((message) => message.id === next.id) ? messages.map((message) => message.id === next.id ? next : message) : [...messages, next];
|
|
2166
|
+
}
|
|
2167
|
+
function conflict(actualVersion) {
|
|
2168
|
+
return { outcome: "conflict", actualVersion };
|
|
1746
2169
|
}
|
|
1747
2170
|
function terminalState(reason) {
|
|
1748
2171
|
if (reason === "success" || reason === "policy_stop")
|
|
@@ -1764,217 +2187,427 @@ function terminalMessageStatus(reason) {
|
|
|
1764
2187
|
}
|
|
1765
2188
|
return "failed";
|
|
1766
2189
|
}
|
|
1767
|
-
function
|
|
1768
|
-
const conversations = new Map;
|
|
1769
|
-
const get = (conversationId) => {
|
|
1770
|
-
const existing = conversations.get(conversationId);
|
|
1771
|
-
if (existing)
|
|
1772
|
-
return existing;
|
|
1773
|
-
const created = { snapshot: emptySnapshot(conversationId), idempotency: new Map };
|
|
1774
|
-
conversations.set(conversationId, created);
|
|
1775
|
-
return created;
|
|
1776
|
-
};
|
|
1777
|
-
const conflict = (actualVersion) => ({
|
|
1778
|
-
outcome: "conflict",
|
|
1779
|
-
actualVersion
|
|
1780
|
-
});
|
|
1781
|
-
const apply = (entry, snapshot) => {
|
|
1782
|
-
entry.snapshot = AgentSnapshotSchema.parse(snapshot);
|
|
1783
|
-
return { outcome: "applied", snapshot: cloneSnapshot(entry.snapshot) };
|
|
1784
|
-
};
|
|
2190
|
+
function applied(current, admissions, input, historyMutation) {
|
|
1785
2191
|
return {
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
if (input.coalesceIntoRunId !== undefined && (!coalescedRun || coalescedRun.conversationId !== input.input.conversationId || coalescedRun.state !== "queued" || coalescedRun.ownerId !== undefined || coalescedRun.terminalReason !== undefined)) {
|
|
1805
|
-
return coalescedRun ? conflict(coalescedRun.revision) : { outcome: "not_found" };
|
|
1806
|
-
}
|
|
1807
|
-
if (input.run.conversationId !== input.input.conversationId || input.run.inputMessageIds.length !== 1 || input.run.inputMessageIds[0] !== input.input.id || input.run.state !== "queued" || input.run.revision !== 0 || input.run.ownerId !== undefined || input.run.terminalReason !== undefined || input.run.terminalPolicyName !== undefined || input.input.role !== "user" || input.input.status !== "committed" || input.input.runId !== undefined || entry.snapshot.messages.some((message) => message.id === input.input.id) || !coalescedRun && (input.run.assistantMessageId === input.input.id || entry.snapshot.runs.some((candidate) => candidate.id === input.run.id) || entry.snapshot.runs.some((candidate) => candidate.assistantMessageId === input.run.assistantMessageId) || entry.snapshot.messages.some((message) => message.id === input.run.assistantMessageId))) {
|
|
1808
|
-
throw new TypeError("Input and queued run do not form one valid assignment");
|
|
2192
|
+
outcome: "applied",
|
|
2193
|
+
snapshot: AgentSnapshotSchema.parse({
|
|
2194
|
+
...current,
|
|
2195
|
+
version: current.version + 1,
|
|
2196
|
+
runs: input.runs ?? current.runs,
|
|
2197
|
+
messages: input.messages ?? current.messages
|
|
2198
|
+
}),
|
|
2199
|
+
admissions,
|
|
2200
|
+
...historyMutation && { historyMutation }
|
|
2201
|
+
};
|
|
2202
|
+
}
|
|
2203
|
+
function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
2204
|
+
if (operation.type === "accept") {
|
|
2205
|
+
const input = operation.input;
|
|
2206
|
+
const duplicate = currentAdmissions.find((candidate) => candidate.idempotencyKey === input.idempotencyKey);
|
|
2207
|
+
if (duplicate) {
|
|
2208
|
+
if (!duplicateInput) {
|
|
2209
|
+
throw new Error("Duplicate admission input is unavailable from canonical history");
|
|
1809
2210
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
2211
|
+
return {
|
|
2212
|
+
outcome: "duplicate",
|
|
2213
|
+
input: duplicateInput,
|
|
2214
|
+
inputMessageId: duplicate.inputMessageId,
|
|
2215
|
+
runId: duplicate.runId,
|
|
2216
|
+
assistantMessageId: duplicate.assistantMessageId,
|
|
2217
|
+
snapshot: current
|
|
2218
|
+
};
|
|
2219
|
+
}
|
|
2220
|
+
if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
|
|
2221
|
+
return conflict(current.version);
|
|
2222
|
+
}
|
|
2223
|
+
const coalescedRun = input.coalesceIntoRunId ? current.runs.find((candidate) => candidate.id === input.coalesceIntoRunId) : undefined;
|
|
2224
|
+
if (input.coalesceIntoRunId !== undefined && (!coalescedRun || coalescedRun.conversationId !== input.input.conversationId || coalescedRun.state !== "queued" || coalescedRun.ownerId !== undefined || coalescedRun.terminalReason !== undefined)) {
|
|
2225
|
+
return coalescedRun ? conflict(coalescedRun.revision) : { outcome: "not_found" };
|
|
2226
|
+
}
|
|
2227
|
+
if (input.run.conversationId !== input.input.conversationId || input.run.inputMessageIds.length !== 1 || input.run.inputMessageIds[0] !== input.input.id || input.run.state !== "queued" || input.run.revision !== 0 || input.run.ownerId !== undefined || input.run.terminalReason !== undefined || input.run.terminalPolicyName !== undefined || input.input.role !== "user" || input.input.status !== "committed" || input.input.runId !== undefined || current.messages.some((message) => message.id === input.input.id) || coalescedRun !== undefined && input.input.id === coalescedRun.assistantMessageId || !coalescedRun && (input.run.assistantMessageId === input.input.id || current.runs.some((candidate) => candidate.id === input.run.id) || current.runs.some((candidate) => candidate.assistantMessageId === input.run.assistantMessageId) || current.messages.some((message) => message.id === input.run.assistantMessageId))) {
|
|
2228
|
+
throw new TypeError("Input and queued run do not form one valid assignment");
|
|
2229
|
+
}
|
|
2230
|
+
const assignedRun = coalescedRun ? AgentRunSchema.parse({
|
|
2231
|
+
...coalescedRun,
|
|
2232
|
+
inputMessageIds: [...coalescedRun.inputMessageIds, input.input.id],
|
|
2233
|
+
revision: coalescedRun.revision + 1,
|
|
2234
|
+
updatedAt: new Date().toISOString()
|
|
2235
|
+
}) : input.run;
|
|
2236
|
+
const admission = AgentAdmissionIdentitySchema.parse({
|
|
2237
|
+
idempotencyKey: input.idempotencyKey,
|
|
2238
|
+
inputMessageId: input.input.id,
|
|
2239
|
+
runId: assignedRun.id,
|
|
2240
|
+
assistantMessageId: assignedRun.assistantMessageId
|
|
2241
|
+
});
|
|
2242
|
+
return applied(current, [...currentAdmissions, admission], {
|
|
2243
|
+
messages: [...current.messages, input.input],
|
|
2244
|
+
runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
|
|
2245
|
+
}, { type: "admit", input: input.input });
|
|
2246
|
+
}
|
|
2247
|
+
const conversationId = operation.input.conversationId;
|
|
2248
|
+
const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
|
|
2249
|
+
if (operation.type !== "compact" && !run)
|
|
2250
|
+
return { outcome: "not_found" };
|
|
2251
|
+
if (operation.type === "acquire" && run) {
|
|
2252
|
+
const runPosition = current.runs.findIndex((candidate) => candidate.id === run.id);
|
|
2253
|
+
const acquisitionBlocked = current.runs.some((candidate, index) => candidate.id !== run.id && (candidate.state === "running" || candidate.state === "interrupt_requested" || index < runPosition && candidate.state === "queued"));
|
|
2254
|
+
if (run.revision !== operation.input.expectedRevision || run.state !== "queued" || acquisitionBlocked) {
|
|
2255
|
+
return conflict(run.revision);
|
|
2256
|
+
}
|
|
2257
|
+
const next = AgentRunSchema.parse({
|
|
2258
|
+
...run,
|
|
2259
|
+
state: "running",
|
|
2260
|
+
ownerId: operation.input.ownerId,
|
|
2261
|
+
fencingToken: (run.fencingToken ?? 0) + 1,
|
|
2262
|
+
revision: run.revision + 1,
|
|
2263
|
+
updatedAt: new Date().toISOString()
|
|
2264
|
+
});
|
|
2265
|
+
return applied(current, currentAdmissions, {
|
|
2266
|
+
runs: replaceRun(current.runs, next)
|
|
2267
|
+
});
|
|
2268
|
+
}
|
|
2269
|
+
if (operation.type === "checkpoint" && run) {
|
|
2270
|
+
const input = operation.input;
|
|
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") {
|
|
2272
|
+
return conflict(run.revision);
|
|
2273
|
+
}
|
|
2274
|
+
const next = AgentRunSchema.parse({
|
|
2275
|
+
...run,
|
|
2276
|
+
revision: run.revision + 1,
|
|
2277
|
+
updatedAt: new Date().toISOString()
|
|
2278
|
+
});
|
|
2279
|
+
return applied(current, currentAdmissions, {
|
|
2280
|
+
runs: replaceRun(current.runs, next),
|
|
2281
|
+
messages: replaceMessage(current.messages, input.assistant)
|
|
2282
|
+
}, { type: "upsert-assistant", message: input.assistant });
|
|
2283
|
+
}
|
|
2284
|
+
if (operation.type === "interrupt" && run) {
|
|
2285
|
+
if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
|
|
2286
|
+
return conflict(run.revision);
|
|
2287
|
+
}
|
|
2288
|
+
const next = AgentRunSchema.parse({
|
|
2289
|
+
...run,
|
|
2290
|
+
state: "interrupt_requested",
|
|
2291
|
+
revision: run.revision + 1,
|
|
2292
|
+
updatedAt: new Date().toISOString()
|
|
2293
|
+
});
|
|
2294
|
+
return applied(current, currentAdmissions, {
|
|
2295
|
+
runs: replaceRun(current.runs, next)
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
if (operation.type === "recover" && run) {
|
|
2299
|
+
const input = operation.input;
|
|
2300
|
+
if (run.revision !== input.expectedRevision || !["queued", "running", "interrupt_requested"].includes(run.state)) {
|
|
2301
|
+
return conflict(run.revision);
|
|
2302
|
+
}
|
|
2303
|
+
if (input.action === "requeue" && run.state !== "queued" && input.replaySafe !== true) {
|
|
2304
|
+
throw new TypeError("Recovering an acquired run requires explicit replaySafe evidence");
|
|
2305
|
+
}
|
|
2306
|
+
const next = AgentRunSchema.parse({
|
|
2307
|
+
schemaVersion: 1,
|
|
2308
|
+
id: run.id,
|
|
2309
|
+
conversationId: run.conversationId,
|
|
2310
|
+
inputMessageIds: run.inputMessageIds,
|
|
2311
|
+
assistantMessageId: run.assistantMessageId,
|
|
2312
|
+
state: input.action === "requeue" ? "queued" : "abandoned",
|
|
2313
|
+
revision: run.revision + 1,
|
|
2314
|
+
...input.action === "abandon" && { terminalReason: "abandoned" },
|
|
2315
|
+
createdAt: run.createdAt,
|
|
2316
|
+
updatedAt: new Date().toISOString()
|
|
2317
|
+
});
|
|
2318
|
+
if (input.action === "abandon") {
|
|
2319
|
+
const existingAssistant = current.messages.find((message) => message.id === run.assistantMessageId);
|
|
2320
|
+
const assistant = AgentMessageSchema.parse({
|
|
2321
|
+
...existingAssistant ?? {
|
|
2322
|
+
schemaVersion: 1,
|
|
2323
|
+
id: run.assistantMessageId,
|
|
2324
|
+
conversationId: run.conversationId,
|
|
2325
|
+
runId: run.id,
|
|
2326
|
+
role: "assistant",
|
|
2327
|
+
parts: [],
|
|
2328
|
+
createdAt: run.createdAt
|
|
2329
|
+
},
|
|
2330
|
+
status: "failed",
|
|
1814
2331
|
updatedAt: new Date().toISOString()
|
|
1815
|
-
}) : input.run;
|
|
1816
|
-
const next = AgentSnapshotSchema.parse({
|
|
1817
|
-
...entry.snapshot,
|
|
1818
|
-
version: entry.snapshot.version + 1,
|
|
1819
|
-
messages: [...entry.snapshot.messages, input.input],
|
|
1820
|
-
runs: coalescedRun ? replaceRun(entry.snapshot.runs, assignedRun) : [...entry.snapshot.runs, assignedRun]
|
|
1821
2332
|
});
|
|
1822
|
-
|
|
1823
|
-
|
|
2333
|
+
return applied(current, currentAdmissions, {
|
|
2334
|
+
runs: replaceRun(current.runs, next),
|
|
2335
|
+
messages: replaceMessage(current.messages, assistant)
|
|
2336
|
+
}, { type: "upsert-assistant", message: assistant });
|
|
2337
|
+
}
|
|
2338
|
+
return applied(current, currentAdmissions, {
|
|
2339
|
+
runs: replaceRun(current.runs, next)
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
if (operation.type === "terminal" && run) {
|
|
2343
|
+
const input = operation.input;
|
|
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)) {
|
|
2345
|
+
return conflict(run.revision);
|
|
2346
|
+
}
|
|
2347
|
+
const next = AgentRunSchema.parse({
|
|
2348
|
+
...run,
|
|
2349
|
+
state: terminalState(input.reason),
|
|
2350
|
+
terminalReason: input.reason,
|
|
2351
|
+
...input.policyName && { terminalPolicyName: input.policyName },
|
|
2352
|
+
revision: run.revision + 1,
|
|
2353
|
+
updatedAt: new Date().toISOString()
|
|
2354
|
+
});
|
|
2355
|
+
return applied(current, currentAdmissions, {
|
|
2356
|
+
runs: replaceRun(current.runs, next),
|
|
2357
|
+
messages: replaceMessage(current.messages, input.assistant)
|
|
2358
|
+
}, { type: "upsert-assistant", message: input.assistant });
|
|
2359
|
+
}
|
|
2360
|
+
if (operation.type === "compact") {
|
|
2361
|
+
const input = operation.input;
|
|
2362
|
+
if (current.conversationId !== conversationId)
|
|
2363
|
+
return { outcome: "not_found" };
|
|
2364
|
+
if (current.version !== input.expectedVersion)
|
|
2365
|
+
return conflict(current.version);
|
|
2366
|
+
const replaced = new Set(input.replacedMessageIds);
|
|
2367
|
+
if (!input.replacedMessageIds.every((id) => current.messages.some((m) => m.id === id))) {
|
|
2368
|
+
return { outcome: "not_found" };
|
|
2369
|
+
}
|
|
2370
|
+
const positions = current.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
2371
|
+
const first = positions[0];
|
|
2372
|
+
if (first === undefined || positions.some((position, offset) => position !== first + offset) || input.summary.conversationId !== input.conversationId || input.summary.runId !== undefined || input.summary.role !== "summary" || input.summary.status !== "committed" || current.messages.some((message) => message.id === input.summary.id) || current.runs.some((candidate) => candidate.assistantMessageId === input.summary.id)) {
|
|
2373
|
+
throw new TypeError("Compaction replacement must be one valid contiguous history range");
|
|
2374
|
+
}
|
|
2375
|
+
const messages = [
|
|
2376
|
+
...current.messages.slice(0, first),
|
|
2377
|
+
input.summary,
|
|
2378
|
+
...current.messages.slice(first + positions.length)
|
|
2379
|
+
];
|
|
2380
|
+
return applied(current, currentAdmissions, { messages }, {
|
|
2381
|
+
type: "replace-compacted-range",
|
|
2382
|
+
replacedMessageIds: input.replacedMessageIds,
|
|
2383
|
+
summary: input.summary
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2386
|
+
return { outcome: "not_found" };
|
|
2387
|
+
}
|
|
2388
|
+
function operationConversationId(operation) {
|
|
2389
|
+
return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
|
|
2390
|
+
}
|
|
2391
|
+
function createAgentRuntimeStore(driver) {
|
|
2392
|
+
const loadSnapshot = (conversationId) => driver.transaction(async (transaction) => {
|
|
2393
|
+
const [stored, messages] = await Promise.all([
|
|
2394
|
+
driver.state.load(transaction, conversationId),
|
|
2395
|
+
driver.history.load(transaction, conversationId)
|
|
2396
|
+
]);
|
|
2397
|
+
return snapshotOf(stored ?? emptyState(conversationId), messages);
|
|
2398
|
+
});
|
|
2399
|
+
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
2400
|
+
const conversationId = operationConversationId(operation);
|
|
2401
|
+
const [stored, messages] = await Promise.all([
|
|
2402
|
+
driver.state.load(transaction, conversationId),
|
|
2403
|
+
driver.history.load(transaction, conversationId)
|
|
2404
|
+
]);
|
|
2405
|
+
const state = AgentStoredStateSchema.parse(stored ?? emptyState(conversationId));
|
|
2406
|
+
const current = snapshotOf(state, messages);
|
|
2407
|
+
const duplicateIdentity = operation.type === "accept" ? state.admissions.find((candidate) => candidate.idempotencyKey === operation.input.idempotencyKey) : undefined;
|
|
2408
|
+
const duplicateInput = duplicateIdentity ? await driver.history.loadById(transaction, {
|
|
2409
|
+
conversationId,
|
|
2410
|
+
messageId: duplicateIdentity.inputMessageId
|
|
2411
|
+
}) : undefined;
|
|
2412
|
+
if (duplicateIdentity && duplicateInput && (duplicateInput.id !== duplicateIdentity.inputMessageId || duplicateInput.conversationId !== conversationId || duplicateInput.role !== "user" || duplicateInput.status !== "committed" || duplicateInput.runId !== undefined)) {
|
|
2413
|
+
throw new TypeError("Canonical duplicate input does not match its admission identity");
|
|
2414
|
+
}
|
|
2415
|
+
const reduced = reduceStore(current, state.admissions, operation, duplicateInput);
|
|
2416
|
+
if (reduced.outcome !== "applied")
|
|
2417
|
+
return reduced;
|
|
2418
|
+
const nextState = AgentStoredStateSchema.parse({
|
|
2419
|
+
schemaVersion: 1,
|
|
2420
|
+
conversationId,
|
|
2421
|
+
version: reduced.snapshot.version,
|
|
2422
|
+
runs: reduced.snapshot.runs,
|
|
2423
|
+
admissions: reduced.admissions
|
|
2424
|
+
});
|
|
2425
|
+
const outcome = await driver.state.compareAndSwap(transaction, {
|
|
2426
|
+
conversationId,
|
|
2427
|
+
expectedVersion: current.version,
|
|
2428
|
+
next: nextState,
|
|
2429
|
+
recoverable: recoverableDescriptors(nextState)
|
|
2430
|
+
});
|
|
2431
|
+
if (outcome.outcome === "conflict")
|
|
2432
|
+
return conflict(outcome.actualVersion);
|
|
2433
|
+
if (reduced.historyMutation) {
|
|
2434
|
+
await driver.history.apply(transaction, reduced.historyMutation);
|
|
2435
|
+
}
|
|
2436
|
+
return { outcome: "applied", snapshot: reduced.snapshot };
|
|
2437
|
+
});
|
|
2438
|
+
return {
|
|
2439
|
+
loadSnapshot,
|
|
2440
|
+
acceptInputAndAssignRun: (input) => mutate({
|
|
2441
|
+
type: "accept",
|
|
2442
|
+
input: AcceptInputAndAssignRunSchema.parse(input)
|
|
2443
|
+
}),
|
|
2444
|
+
acquireRun: (input) => mutate({ type: "acquire", input: AcquireAgentRunSchema.parse(input) }),
|
|
2445
|
+
checkpointRunAssistant: (input) => mutate({
|
|
2446
|
+
type: "checkpoint",
|
|
2447
|
+
input: CheckpointRunAssistantSchema.parse(input)
|
|
2448
|
+
}),
|
|
2449
|
+
requestRunInterrupt: (input) => mutate({
|
|
2450
|
+
type: "interrupt",
|
|
2451
|
+
input: RequestRunInterruptSchema.parse(input)
|
|
2452
|
+
}),
|
|
2453
|
+
recoverRun: (input) => mutate({ type: "recover", input: RecoverAgentRunSchema.parse(input) }),
|
|
2454
|
+
commitRunTerminal: (input) => mutate({ type: "terminal", input: CommitRunTerminalSchema.parse(input) }),
|
|
2455
|
+
replaceCompactedRange: (input) => mutate({
|
|
2456
|
+
type: "compact",
|
|
2457
|
+
input: ReplaceCompactedRangeSchema.parse(input)
|
|
2458
|
+
}),
|
|
2459
|
+
async scanRecoverable() {
|
|
2460
|
+
const snapshots = [];
|
|
2461
|
+
const seenConversationIds = new Set;
|
|
2462
|
+
let cursor;
|
|
2463
|
+
do {
|
|
2464
|
+
const page = AgentRecoverablePageSchema.parse(await driver.scanRecoverable({
|
|
2465
|
+
...cursor && { cursor },
|
|
2466
|
+
limit: 100
|
|
2467
|
+
}));
|
|
2468
|
+
const conversationIds = [
|
|
2469
|
+
...new Set(page.items.map((item) => item.conversationId))
|
|
2470
|
+
].filter((conversationId) => !seenConversationIds.has(conversationId));
|
|
2471
|
+
for (const conversationId of conversationIds)
|
|
2472
|
+
seenConversationIds.add(conversationId);
|
|
2473
|
+
snapshots.push(...await Promise.all(conversationIds.map(loadSnapshot)));
|
|
2474
|
+
cursor = page.nextCursor;
|
|
2475
|
+
} while (cursor !== undefined);
|
|
2476
|
+
return snapshots;
|
|
1824
2477
|
},
|
|
1825
|
-
async
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2478
|
+
async scanRecoverablePage(input) {
|
|
2479
|
+
const parsed = AgentRecoverableScanInputSchema.parse(input);
|
|
2480
|
+
return AgentRecoverablePageSchema.parse(await driver.scanRecoverable(parsed));
|
|
2481
|
+
}
|
|
2482
|
+
};
|
|
2483
|
+
}
|
|
2484
|
+
function cloneStateMap(source) {
|
|
2485
|
+
return new Map([...source].map(([key, value]) => [
|
|
2486
|
+
key,
|
|
2487
|
+
AgentStoredStateSchema.parse(structuredClone(value))
|
|
2488
|
+
]));
|
|
2489
|
+
}
|
|
2490
|
+
function cloneHistoryMap(source) {
|
|
2491
|
+
return new Map([...source].map(([key, value]) => [
|
|
2492
|
+
key,
|
|
2493
|
+
value.map((message) => AgentMessageSchema.parse(structuredClone(message)))
|
|
2494
|
+
]));
|
|
2495
|
+
}
|
|
2496
|
+
function createMemoryAgentRuntimeStore() {
|
|
2497
|
+
let states = new Map;
|
|
2498
|
+
let histories = new Map;
|
|
2499
|
+
let archivedMessages = new Map;
|
|
2500
|
+
let transactionTail = Promise.resolve();
|
|
2501
|
+
const driver = {
|
|
2502
|
+
async transaction(work) {
|
|
2503
|
+
const previous = transactionTail;
|
|
2504
|
+
const release = Promise.withResolvers();
|
|
2505
|
+
transactionTail = previous.catch(() => {
|
|
2506
|
+
return;
|
|
2507
|
+
}).then(() => release.promise);
|
|
2508
|
+
await previous.catch(() => {
|
|
2509
|
+
return;
|
|
1840
2510
|
});
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
2511
|
+
const transaction = {
|
|
2512
|
+
states: cloneStateMap(states),
|
|
2513
|
+
histories: cloneHistoryMap(histories),
|
|
2514
|
+
archivedMessages: new Map([...archivedMessages].map(([conversationId, messages]) => [
|
|
2515
|
+
conversationId,
|
|
2516
|
+
new Map([...messages].map(([messageId, message]) => [
|
|
2517
|
+
messageId,
|
|
2518
|
+
AgentMessageSchema.parse(structuredClone(message))
|
|
2519
|
+
]))
|
|
2520
|
+
]))
|
|
2521
|
+
};
|
|
2522
|
+
try {
|
|
2523
|
+
const result = await work(transaction);
|
|
2524
|
+
states = transaction.states;
|
|
2525
|
+
histories = transaction.histories;
|
|
2526
|
+
archivedMessages = transaction.archivedMessages;
|
|
2527
|
+
return result;
|
|
2528
|
+
} finally {
|
|
2529
|
+
release.resolve();
|
|
1855
2530
|
}
|
|
1856
|
-
const nextRun = AgentRunSchema.parse({
|
|
1857
|
-
...run,
|
|
1858
|
-
revision: run.revision + 1,
|
|
1859
|
-
updatedAt: new Date().toISOString()
|
|
1860
|
-
});
|
|
1861
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1862
|
-
...entry.snapshot,
|
|
1863
|
-
version: entry.snapshot.version + 1,
|
|
1864
|
-
messages: replaceMessage(entry.snapshot.messages, input.assistant),
|
|
1865
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1866
|
-
}));
|
|
1867
2531
|
},
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2532
|
+
state: {
|
|
2533
|
+
async load(transaction, conversationId) {
|
|
2534
|
+
const state = transaction.states.get(conversationId);
|
|
2535
|
+
return state ? AgentStoredStateSchema.parse(structuredClone(state)) : undefined;
|
|
2536
|
+
},
|
|
2537
|
+
async compareAndSwap(transaction, input) {
|
|
2538
|
+
const current = transaction.states.get(input.conversationId);
|
|
2539
|
+
const actualVersion = current?.version ?? 0;
|
|
2540
|
+
if (actualVersion !== input.expectedVersion) {
|
|
2541
|
+
return { outcome: "conflict", actualVersion };
|
|
2542
|
+
}
|
|
2543
|
+
transaction.states.set(input.conversationId, AgentStoredStateSchema.parse(structuredClone(input.next)));
|
|
2544
|
+
return { outcome: "applied" };
|
|
1876
2545
|
}
|
|
1877
|
-
const nextRun = AgentRunSchema.parse({
|
|
1878
|
-
...run,
|
|
1879
|
-
state: "interrupt_requested",
|
|
1880
|
-
revision: run.revision + 1,
|
|
1881
|
-
updatedAt: new Date().toISOString()
|
|
1882
|
-
});
|
|
1883
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1884
|
-
...entry.snapshot,
|
|
1885
|
-
version: entry.snapshot.version + 1,
|
|
1886
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1887
|
-
}));
|
|
1888
2546
|
},
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
return
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
|
|
2547
|
+
history: {
|
|
2548
|
+
async load(transaction, conversationId) {
|
|
2549
|
+
return (transaction.histories.get(conversationId) ?? []).map((message) => AgentMessageSchema.parse(structuredClone(message)));
|
|
2550
|
+
},
|
|
2551
|
+
async loadById(transaction, input) {
|
|
2552
|
+
const active = (transaction.histories.get(input.conversationId) ?? []).find((message2) => message2.id === input.messageId);
|
|
2553
|
+
const message = active ?? transaction.archivedMessages.get(input.conversationId)?.get(input.messageId);
|
|
2554
|
+
return message ? AgentMessageSchema.parse(structuredClone(message)) : undefined;
|
|
2555
|
+
},
|
|
2556
|
+
async apply(transaction, rawMutation) {
|
|
2557
|
+
const mutation = AgentHistoryMutationSchema.parse(rawMutation);
|
|
2558
|
+
const conversationId = mutation.type === "admit" ? mutation.input.conversationId : mutation.type === "upsert-assistant" ? mutation.message.conversationId : mutation.summary.conversationId;
|
|
2559
|
+
const current = transaction.histories.get(conversationId) ?? [];
|
|
2560
|
+
if (mutation.type === "admit") {
|
|
2561
|
+
transaction.histories.set(conversationId, [...current, mutation.input]);
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
if (mutation.type === "upsert-assistant") {
|
|
2565
|
+
transaction.histories.set(conversationId, replaceMessage(current, mutation.message));
|
|
2566
|
+
return;
|
|
2567
|
+
}
|
|
2568
|
+
const replaced = new Set(mutation.replacedMessageIds);
|
|
2569
|
+
const positions = current.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
2570
|
+
const first = positions[0];
|
|
2571
|
+
if (first === undefined)
|
|
2572
|
+
throw new Error("Compaction history range disappeared");
|
|
2573
|
+
const archive = transaction.archivedMessages.get(conversationId) ?? new Map;
|
|
2574
|
+
for (const message of current.filter((candidate) => replaced.has(candidate.id))) {
|
|
2575
|
+
archive.set(message.id, AgentMessageSchema.parse(structuredClone(message)));
|
|
2576
|
+
}
|
|
2577
|
+
transaction.archivedMessages.set(conversationId, archive);
|
|
2578
|
+
transaction.histories.set(conversationId, [
|
|
2579
|
+
...current.slice(0, first),
|
|
2580
|
+
mutation.summary,
|
|
2581
|
+
...current.slice(first + positions.length)
|
|
2582
|
+
]);
|
|
1900
2583
|
}
|
|
1901
|
-
const nextRun = AgentRunSchema.parse({
|
|
1902
|
-
schemaVersion: 1,
|
|
1903
|
-
id: run.id,
|
|
1904
|
-
conversationId: run.conversationId,
|
|
1905
|
-
inputMessageIds: run.inputMessageIds,
|
|
1906
|
-
assistantMessageId: run.assistantMessageId,
|
|
1907
|
-
state: input.action === "requeue" ? "queued" : "abandoned",
|
|
1908
|
-
revision: run.revision + 1,
|
|
1909
|
-
...input.action === "abandon" && { terminalReason: "abandoned" },
|
|
1910
|
-
createdAt: run.createdAt,
|
|
1911
|
-
updatedAt: new Date().toISOString()
|
|
1912
|
-
});
|
|
1913
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1914
|
-
...entry.snapshot,
|
|
1915
|
-
version: entry.snapshot.version + 1,
|
|
1916
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1917
|
-
}));
|
|
1918
2584
|
},
|
|
1919
|
-
async
|
|
1920
|
-
const
|
|
1921
|
-
const
|
|
1922
|
-
const
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
...run,
|
|
1930
|
-
state: terminalState(input.reason),
|
|
1931
|
-
terminalReason: input.reason,
|
|
1932
|
-
...input.policyName && { terminalPolicyName: input.policyName },
|
|
1933
|
-
revision: run.revision + 1,
|
|
1934
|
-
updatedAt: new Date().toISOString()
|
|
2585
|
+
async scanRecoverable(input) {
|
|
2586
|
+
const descriptors = [...states.values()].flatMap((state) => state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }))).sort((left, right) => left.conversationId.localeCompare(right.conversationId) || left.run.id.localeCompare(right.run.id));
|
|
2587
|
+
const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
|
|
2588
|
+
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id === cursorTuple[1]) + 1 : 0;
|
|
2589
|
+
const items = descriptors.slice(start, start + input.limit);
|
|
2590
|
+
const last = items.at(-1);
|
|
2591
|
+
const hasMore = start + items.length < descriptors.length;
|
|
2592
|
+
return AgentRecoverablePageSchema.parse({
|
|
2593
|
+
items,
|
|
2594
|
+
...hasMore && last && { nextCursor: recoverableCursor(last) }
|
|
1935
2595
|
});
|
|
1936
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1937
|
-
...entry.snapshot,
|
|
1938
|
-
version: entry.snapshot.version + 1,
|
|
1939
|
-
messages: replaceMessage(entry.snapshot.messages, input.assistant),
|
|
1940
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1941
|
-
}));
|
|
1942
|
-
},
|
|
1943
|
-
async replaceCompactedRange(rawInput) {
|
|
1944
|
-
const input = ReplaceCompactedRangeSchema.parse(rawInput);
|
|
1945
|
-
const entry = get(input.conversationId);
|
|
1946
|
-
if (entry.snapshot.version !== input.expectedVersion) {
|
|
1947
|
-
return conflict(entry.snapshot.version);
|
|
1948
|
-
}
|
|
1949
|
-
const replaced = new Set(input.replacedMessageIds);
|
|
1950
|
-
if (!input.replacedMessageIds.every((id) => entry.snapshot.messages.some((m) => m.id === id))) {
|
|
1951
|
-
return { outcome: "not_found" };
|
|
1952
|
-
}
|
|
1953
|
-
const positions = entry.snapshot.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
1954
|
-
const first = positions[0];
|
|
1955
|
-
if (first === undefined || positions.some((position, offset) => position !== first + offset) || input.summary.conversationId !== input.conversationId || input.summary.runId !== undefined || input.summary.role !== "summary" || input.summary.status !== "committed") {
|
|
1956
|
-
throw new TypeError("Compaction replacement must be one valid contiguous history range");
|
|
1957
|
-
}
|
|
1958
|
-
const before = entry.snapshot.messages.slice(0, first);
|
|
1959
|
-
const after = entry.snapshot.messages.slice(first + positions.length);
|
|
1960
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1961
|
-
...entry.snapshot,
|
|
1962
|
-
version: entry.snapshot.version + 1,
|
|
1963
|
-
messages: [...before, input.summary, ...after]
|
|
1964
|
-
}));
|
|
1965
|
-
},
|
|
1966
|
-
async scanRecoverable() {
|
|
1967
|
-
return [...conversations.values()].filter((entry) => entry.snapshot.runs.some((run) => ["queued", "running", "interrupt_requested"].includes(run.state))).map((entry) => cloneSnapshot(entry.snapshot));
|
|
1968
2596
|
}
|
|
1969
2597
|
};
|
|
2598
|
+
return createAgentRuntimeStore(driver);
|
|
1970
2599
|
}
|
|
1971
2600
|
export {
|
|
1972
2601
|
AcceptInputAndAssignRunSchema,
|
|
1973
2602
|
AcquireAgentRunSchema,
|
|
2603
|
+
AgentAdmissionEventSchema,
|
|
2604
|
+
AgentAdmissionIdentitySchema,
|
|
2605
|
+
AgentAssistantPlaceholderSchema,
|
|
1974
2606
|
AgentCheckpointEventSchema,
|
|
1975
2607
|
AgentControlPartSchema,
|
|
1976
2608
|
AgentCostValueSchema,
|
|
1977
2609
|
AgentFilePartSchema,
|
|
2610
|
+
AgentHistoryMutationSchema,
|
|
1978
2611
|
AgentJsonObjectSchema,
|
|
1979
2612
|
AgentMessagePartSchema,
|
|
1980
2613
|
AgentMessageRoleSchema,
|
|
@@ -1982,6 +2615,7 @@ export {
|
|
|
1982
2615
|
AgentMessageStatusSchema,
|
|
1983
2616
|
AgentModelCapabilitySchema,
|
|
1984
2617
|
AgentModelDescriptorSchema,
|
|
2618
|
+
AgentModelRegistrySnapshotSchema,
|
|
1985
2619
|
AgentOpaquePartSchema,
|
|
1986
2620
|
AgentProviderEnvelopeSchema,
|
|
1987
2621
|
AgentReasoningDeltaEventSchema,
|
|
@@ -1990,10 +2624,14 @@ export {
|
|
|
1990
2624
|
AgentReasoningStartEventSchema,
|
|
1991
2625
|
AgentRecordIdSchema,
|
|
1992
2626
|
AgentRecordVersionSchema,
|
|
2627
|
+
AgentRecoverableDescriptorSchema,
|
|
2628
|
+
AgentRecoverablePageSchema,
|
|
1993
2629
|
AgentRunEventSchema,
|
|
2630
|
+
AgentRunMetricsSchema,
|
|
1994
2631
|
AgentRunSchema,
|
|
1995
2632
|
AgentRunStateEventSchema,
|
|
1996
2633
|
AgentRunStateSchema,
|
|
2634
|
+
AgentRuntimeEventCursorSchema,
|
|
1997
2635
|
AgentRuntimeEventSchema,
|
|
1998
2636
|
AgentSnapshotSchema,
|
|
1999
2637
|
AgentSourcePartSchema,
|
|
@@ -2002,6 +2640,7 @@ export {
|
|
|
2002
2640
|
AgentStoreDuplicateSchema,
|
|
2003
2641
|
AgentStoreMutationResultSchema,
|
|
2004
2642
|
AgentStoreNotFoundSchema,
|
|
2643
|
+
AgentStoredStateSchema,
|
|
2005
2644
|
AgentTerminalEventSchema,
|
|
2006
2645
|
AgentTerminalReasonSchema,
|
|
2007
2646
|
AgentTextPartSchema,
|
|
@@ -2018,14 +2657,21 @@ export {
|
|
|
2018
2657
|
RecoverAgentRunSchema,
|
|
2019
2658
|
ReplaceCompactedRangeSchema,
|
|
2020
2659
|
RequestRunInterruptSchema,
|
|
2660
|
+
advanceAgentRuntimeEventCursor,
|
|
2661
|
+
agentDurableEventId,
|
|
2021
2662
|
composeAgentPrompt,
|
|
2022
2663
|
createAgentObservability,
|
|
2023
2664
|
createAgentRuntime,
|
|
2665
|
+
createAgentRuntimeEventSink,
|
|
2666
|
+
createAgentRuntimeStore,
|
|
2024
2667
|
createAgentSessionCoordinator,
|
|
2025
2668
|
createAgentToolFenceLifecycle,
|
|
2026
2669
|
createMemoryAgentRuntimeStore,
|
|
2027
2670
|
defineAgentProtocol,
|
|
2028
2671
|
defineModelRegistry,
|
|
2029
2672
|
projectAgentHistory,
|
|
2030
|
-
|
|
2673
|
+
projectAgentHistoryDetailed,
|
|
2674
|
+
selectAgentHistory,
|
|
2675
|
+
structuredCompaction,
|
|
2676
|
+
validateAgentModelSnapshot
|
|
2031
2677
|
};
|