stitchkit 0.56.5 → 0.57.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/events.d.ts +653 -0
- package/dist/agent-runtime/events.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +34 -3
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schemas.d.ts +73 -0
- package/dist/agent-runtime/schemas.d.ts.map +1 -1
- package/dist/agent-runtime/store-driver.d.ts +406 -0
- package/dist/agent-runtime/store-driver.d.ts.map +1 -0
- package/dist/agent-runtime/store.d.ts +162 -2
- package/dist/agent-runtime/store.d.ts.map +1 -1
- package/dist/agent-runtime.d.ts +4 -3
- package/dist/agent-runtime.d.ts.map +1 -1
- package/dist/agent-runtime.js +806 -452
- package/dist/index-1f4fcj0b.js +160 -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 +1 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +166 -0
- package/llms-full.txt +99 -23
- 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-1f4fcj0b.js";
|
|
5
33
|
import"./index-6djpbnda.js";
|
|
6
34
|
import"./index-cby4ar3v.js";
|
|
7
35
|
import {
|
|
@@ -226,210 +254,78 @@ function createAgentSessionCoordinator() {
|
|
|
226
254
|
};
|
|
227
255
|
}
|
|
228
256
|
// src/agent-runtime/events.ts
|
|
229
|
-
import { z as z2 } from "zod";
|
|
230
|
-
|
|
231
|
-
// src/agent-runtime/schemas.ts
|
|
232
257
|
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),
|
|
258
|
+
var AgentAdmissionEventSchema = z.object({
|
|
259
|
+
type: z.literal("admission"),
|
|
260
|
+
eventId: AgentRecordIdSchema,
|
|
352
261
|
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()
|
|
262
|
+
runId: AgentRecordIdSchema,
|
|
263
|
+
snapshotVersion: AgentRecordVersionSchema,
|
|
264
|
+
input: AgentMessageSchema,
|
|
265
|
+
run: AgentRunSchema,
|
|
266
|
+
assistant: z.union([AgentAssistantPlaceholderSchema, AgentMessageSchema]),
|
|
267
|
+
emittedAt: AgentTimestampSchema
|
|
373
268
|
});
|
|
374
|
-
|
|
375
|
-
// src/agent-runtime/events.ts
|
|
376
|
-
var EventIdentitySchema = z2.object({
|
|
269
|
+
var EventIdentitySchema = z.object({
|
|
377
270
|
conversationId: AgentRecordIdSchema,
|
|
378
271
|
runId: AgentRecordIdSchema,
|
|
379
272
|
emittedAt: AgentTimestampSchema
|
|
380
273
|
});
|
|
381
274
|
var AgentTransientDeltaEventSchema = EventIdentitySchema.extend({
|
|
382
|
-
type:
|
|
383
|
-
runtimeEpoch:
|
|
384
|
-
sequence:
|
|
385
|
-
textDelta:
|
|
275
|
+
type: z.literal("assistant-delta"),
|
|
276
|
+
runtimeEpoch: z.string().min(1),
|
|
277
|
+
sequence: z.int().nonnegative(),
|
|
278
|
+
textDelta: z.string()
|
|
386
279
|
});
|
|
387
280
|
var AgentTransientReasoningIdentitySchema = EventIdentitySchema.extend({
|
|
388
|
-
runtimeEpoch:
|
|
389
|
-
sequence:
|
|
281
|
+
runtimeEpoch: z.string().min(1),
|
|
282
|
+
sequence: z.int().nonnegative(),
|
|
390
283
|
provider: AgentProviderEnvelopeSchema.optional()
|
|
391
284
|
});
|
|
392
285
|
var AgentReasoningStartEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
393
|
-
type:
|
|
286
|
+
type: z.literal("reasoning-start")
|
|
394
287
|
});
|
|
395
288
|
var AgentReasoningDeltaEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
396
|
-
type:
|
|
397
|
-
textDelta:
|
|
289
|
+
type: z.literal("reasoning-delta"),
|
|
290
|
+
textDelta: z.string()
|
|
398
291
|
});
|
|
399
292
|
var AgentReasoningEndEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
400
|
-
type:
|
|
293
|
+
type: z.literal("reasoning-end")
|
|
401
294
|
});
|
|
402
295
|
var AgentCheckpointEventSchema = EventIdentitySchema.extend({
|
|
403
|
-
type:
|
|
296
|
+
type: z.literal("assistant-checkpoint"),
|
|
404
297
|
eventId: AgentRecordIdSchema,
|
|
405
298
|
snapshotVersion: AgentRecordVersionSchema,
|
|
406
|
-
message: AgentMessageSchema
|
|
299
|
+
message: AgentMessageSchema,
|
|
300
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
407
301
|
});
|
|
408
302
|
var AgentRunStateEventSchema = EventIdentitySchema.extend({
|
|
409
|
-
type:
|
|
303
|
+
type: z.literal("run-state"),
|
|
410
304
|
eventId: AgentRecordIdSchema,
|
|
411
305
|
snapshotVersion: AgentRecordVersionSchema,
|
|
412
306
|
state: AgentRunStateSchema
|
|
413
307
|
});
|
|
414
308
|
var AgentToolStatusEventSchema = EventIdentitySchema.extend({
|
|
415
|
-
type:
|
|
416
|
-
runtimeEpoch:
|
|
417
|
-
sequence:
|
|
309
|
+
type: z.literal("tool-status"),
|
|
310
|
+
runtimeEpoch: z.string().min(1),
|
|
311
|
+
sequence: z.int().nonnegative(),
|
|
418
312
|
callId: AgentRecordIdSchema,
|
|
419
|
-
toolName:
|
|
420
|
-
status:
|
|
421
|
-
input:
|
|
422
|
-
output:
|
|
313
|
+
toolName: z.string().min(1),
|
|
314
|
+
status: z.enum(["started", "completed", "failed", "interrupted"]),
|
|
315
|
+
input: z.json().optional(),
|
|
316
|
+
output: z.json().optional()
|
|
423
317
|
});
|
|
424
318
|
var AgentTerminalEventSchema = EventIdentitySchema.extend({
|
|
425
|
-
type:
|
|
319
|
+
type: z.literal("terminal"),
|
|
426
320
|
eventId: AgentRecordIdSchema,
|
|
427
321
|
snapshotVersion: AgentRecordVersionSchema,
|
|
428
322
|
reason: AgentTerminalReasonSchema,
|
|
429
|
-
policyName:
|
|
430
|
-
message: AgentMessageSchema
|
|
323
|
+
policyName: z.string().min(1).optional(),
|
|
324
|
+
message: AgentMessageSchema,
|
|
325
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
431
326
|
});
|
|
432
|
-
var AgentRuntimeEventSchema =
|
|
327
|
+
var AgentRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
328
|
+
AgentAdmissionEventSchema,
|
|
433
329
|
AgentTransientDeltaEventSchema,
|
|
434
330
|
AgentReasoningStartEventSchema,
|
|
435
331
|
AgentReasoningDeltaEventSchema,
|
|
@@ -565,15 +461,15 @@ function createAgentToolFenceLifecycle(config) {
|
|
|
565
461
|
};
|
|
566
462
|
}
|
|
567
463
|
// 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:
|
|
464
|
+
import { z as z2 } from "zod";
|
|
465
|
+
var AgentModelCapabilitySchema = z2.enum(["tools", "vision", "reasoning", "files"]);
|
|
466
|
+
var AgentModelDescriptorSchema = z2.object({
|
|
467
|
+
provider: z2.string().min(1),
|
|
468
|
+
modelId: z2.string().min(1),
|
|
469
|
+
contextWindow: z2.int().positive(),
|
|
470
|
+
capabilities: z2.array(AgentModelCapabilitySchema),
|
|
471
|
+
observedAt: z2.iso.datetime({ offset: true }).optional(),
|
|
472
|
+
source: z2.string().min(1).optional()
|
|
577
473
|
});
|
|
578
474
|
function defineModelRegistry(config) {
|
|
579
475
|
const descriptors = new Map;
|
|
@@ -613,25 +509,25 @@ function defineModelRegistry(config) {
|
|
|
613
509
|
};
|
|
614
510
|
}
|
|
615
511
|
// src/agent-runtime/observability.ts
|
|
616
|
-
import { z as
|
|
617
|
-
var AgentRunEventSchema =
|
|
618
|
-
schemaVersion:
|
|
512
|
+
import { z as z3 } from "zod";
|
|
513
|
+
var AgentRunEventSchema = z3.object({
|
|
514
|
+
schemaVersion: z3.literal(1),
|
|
619
515
|
eventId: AgentRecordIdSchema,
|
|
620
|
-
type:
|
|
516
|
+
type: z3.enum(["run-started", "step-finished", "run-terminal"]),
|
|
621
517
|
conversationId: AgentRecordIdSchema,
|
|
622
518
|
runId: AgentRecordIdSchema,
|
|
623
|
-
traceId:
|
|
624
|
-
spanId:
|
|
625
|
-
parentSpanId:
|
|
519
|
+
traceId: z3.string().min(1),
|
|
520
|
+
spanId: z3.string().min(1),
|
|
521
|
+
parentSpanId: z3.string().min(1).optional(),
|
|
626
522
|
state: AgentRunStateSchema,
|
|
627
523
|
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
628
|
-
modelId:
|
|
629
|
-
step:
|
|
630
|
-
queueWaitMs:
|
|
631
|
-
durationMs:
|
|
632
|
-
ttftMs:
|
|
524
|
+
modelId: z3.string().min(1).optional(),
|
|
525
|
+
step: z3.int().nonnegative().optional(),
|
|
526
|
+
queueWaitMs: z3.number().nonnegative().optional(),
|
|
527
|
+
durationMs: z3.number().nonnegative().optional(),
|
|
528
|
+
ttftMs: z3.number().nonnegative().optional(),
|
|
633
529
|
usage: AgentUsageSchema.optional(),
|
|
634
|
-
internalCause:
|
|
530
|
+
internalCause: z3.unknown().optional(),
|
|
635
531
|
emittedAt: AgentTimestampSchema
|
|
636
532
|
});
|
|
637
533
|
function createAgentObservability(config) {
|
|
@@ -656,10 +552,10 @@ function createAgentObservability(config) {
|
|
|
656
552
|
};
|
|
657
553
|
}
|
|
658
554
|
// src/agent-runtime/prompt.ts
|
|
659
|
-
import { z as
|
|
660
|
-
var AgentTokenCountSchema =
|
|
661
|
-
value:
|
|
662
|
-
provenance:
|
|
555
|
+
import { z as z4 } from "zod";
|
|
556
|
+
var AgentTokenCountSchema = z4.object({
|
|
557
|
+
value: z4.int().nonnegative().optional(),
|
|
558
|
+
provenance: z4.enum(["measured", "estimated", "unavailable"])
|
|
663
559
|
});
|
|
664
560
|
function knownValue(value) {
|
|
665
561
|
return value.provenance === "unavailable" ? undefined : value.value;
|
|
@@ -738,7 +634,7 @@ import {
|
|
|
738
634
|
stepCountIs,
|
|
739
635
|
streamText
|
|
740
636
|
} from "ai";
|
|
741
|
-
import { z as
|
|
637
|
+
import { z as z5 } from "zod";
|
|
742
638
|
class AgentRuntimeConflictError extends Error {
|
|
743
639
|
constructor(operation) {
|
|
744
640
|
super(`Agent runtime store conflict during ${operation}`);
|
|
@@ -757,7 +653,7 @@ function findRun(runs, runId) {
|
|
|
757
653
|
return run;
|
|
758
654
|
}
|
|
759
655
|
function jsonValue(value) {
|
|
760
|
-
const parsed =
|
|
656
|
+
const parsed = z5.json().safeParse(value);
|
|
761
657
|
return parsed.success ? parsed.data : { message: "Non-JSON tool output omitted" };
|
|
762
658
|
}
|
|
763
659
|
function providerEnvelope(value) {
|
|
@@ -1002,6 +898,12 @@ function createAgentRuntime(config) {
|
|
|
1002
898
|
assistant
|
|
1003
899
|
}), "assistant checkpoint");
|
|
1004
900
|
run = findRun(snapshot.runs, run.id);
|
|
901
|
+
const checkpointMetrics = {
|
|
902
|
+
partial: true,
|
|
903
|
+
durationMs: performance.now() - runStartedAt,
|
|
904
|
+
...usage && { usage },
|
|
905
|
+
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
906
|
+
};
|
|
1005
907
|
await publish({
|
|
1006
908
|
type: "assistant-checkpoint",
|
|
1007
909
|
eventId: generateId(),
|
|
@@ -1009,6 +911,7 @@ function createAgentRuntime(config) {
|
|
|
1009
911
|
runId: run.id,
|
|
1010
912
|
snapshotVersion: snapshot.version,
|
|
1011
913
|
message: assistant,
|
|
914
|
+
metrics: checkpointMetrics,
|
|
1012
915
|
emittedAt: now().toISOString()
|
|
1013
916
|
});
|
|
1014
917
|
};
|
|
@@ -1378,6 +1281,12 @@ function createAgentRuntime(config) {
|
|
|
1378
1281
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1379
1282
|
}), "terminal commit");
|
|
1380
1283
|
run = findRun(snapshot.runs, run.id);
|
|
1284
|
+
const terminalMetrics = {
|
|
1285
|
+
partial: false,
|
|
1286
|
+
durationMs: performance.now() - runStartedAt,
|
|
1287
|
+
...usage && { usage },
|
|
1288
|
+
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt }
|
|
1289
|
+
};
|
|
1381
1290
|
config.observe?.emit({
|
|
1382
1291
|
schemaVersion: 1,
|
|
1383
1292
|
eventId: generateId(),
|
|
@@ -1390,7 +1299,7 @@ function createAgentRuntime(config) {
|
|
|
1390
1299
|
state: run.state,
|
|
1391
1300
|
terminalReason,
|
|
1392
1301
|
...selectedModel && { modelId: selectedModel.descriptor.modelId },
|
|
1393
|
-
durationMs:
|
|
1302
|
+
durationMs: terminalMetrics.durationMs,
|
|
1394
1303
|
...usage && { usage },
|
|
1395
1304
|
...internalCause !== undefined && { internalCause },
|
|
1396
1305
|
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt },
|
|
@@ -1405,6 +1314,7 @@ function createAgentRuntime(config) {
|
|
|
1405
1314
|
reason: terminalReason,
|
|
1406
1315
|
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
1407
1316
|
message: assistant,
|
|
1317
|
+
metrics: terminalMetrics,
|
|
1408
1318
|
emittedAt: now().toISOString()
|
|
1409
1319
|
});
|
|
1410
1320
|
return {
|
|
@@ -1412,9 +1322,38 @@ function createAgentRuntime(config) {
|
|
|
1412
1322
|
message: assistant,
|
|
1413
1323
|
reason: terminalReason,
|
|
1414
1324
|
snapshotVersion: snapshot.version,
|
|
1325
|
+
metrics: terminalMetrics,
|
|
1415
1326
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1416
1327
|
};
|
|
1417
1328
|
};
|
|
1329
|
+
const resume = (rawInput) => {
|
|
1330
|
+
const context = config.protocol.parseContext(rawInput.context);
|
|
1331
|
+
const accepted = Promise.withResolvers();
|
|
1332
|
+
const result = Promise.withResolvers();
|
|
1333
|
+
(async () => {
|
|
1334
|
+
try {
|
|
1335
|
+
const snapshot = await config.store.loadSnapshot(rawInput.conversationId);
|
|
1336
|
+
const recoveredRun = findRun(snapshot.runs, rawInput.runId);
|
|
1337
|
+
if (recoveredRun.state !== "queued") {
|
|
1338
|
+
throw new Error("Only a queued recovered agent run can be resumed");
|
|
1339
|
+
}
|
|
1340
|
+
accepted.resolve();
|
|
1341
|
+
const ticket = coordinator.submit({
|
|
1342
|
+
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
1343
|
+
policy: "queue",
|
|
1344
|
+
create: (signal) => ({
|
|
1345
|
+
runId: recoveredRun.id,
|
|
1346
|
+
execute: () => executeRun({ acceptedRun: recoveredRun, context, signal })
|
|
1347
|
+
})
|
|
1348
|
+
});
|
|
1349
|
+
ticket.result.then(result.resolve, result.reject);
|
|
1350
|
+
} catch (error) {
|
|
1351
|
+
accepted.reject(error);
|
|
1352
|
+
result.reject(error);
|
|
1353
|
+
}
|
|
1354
|
+
})();
|
|
1355
|
+
return { accepted: accepted.promise, result: result.promise };
|
|
1356
|
+
};
|
|
1418
1357
|
return {
|
|
1419
1358
|
submit(rawInput) {
|
|
1420
1359
|
const metadata = rawInput.metadata === undefined ? undefined : config.protocol.parseInputMetadata(rawInput.metadata);
|
|
@@ -1505,10 +1444,41 @@ function createAgentRuntime(config) {
|
|
|
1505
1444
|
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
1506
1445
|
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
1507
1446
|
const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1508
|
-
|
|
1447
|
+
const actualInputMessageId = acceptance.outcome === "duplicate" ? acceptance.inputMessageId : userMessage2.id;
|
|
1448
|
+
const acceptedInput = acceptance.outcome === "duplicate" ? acceptance.input : acceptedSnapshot.messages.find((candidate) => candidate.id === actualInputMessageId);
|
|
1449
|
+
if (!acceptedInput) {
|
|
1450
|
+
throw new AgentRuntimeConflictError("admission input projection");
|
|
1451
|
+
}
|
|
1452
|
+
const assistantPlaceholder = AgentAssistantPlaceholderSchema.parse({
|
|
1453
|
+
schemaVersion: 1,
|
|
1454
|
+
id: acceptedRun.assistantMessageId,
|
|
1455
|
+
conversationId: acceptedRun.conversationId,
|
|
1456
|
+
runId: acceptedRun.id,
|
|
1457
|
+
status: "pending",
|
|
1458
|
+
createdAt: acceptedRun.createdAt,
|
|
1459
|
+
updatedAt: acceptedRun.updatedAt
|
|
1460
|
+
});
|
|
1461
|
+
const acceptedAssistant = acceptance.outcome === "duplicate" ? acceptedSnapshot.messages.find((candidate) => candidate.id === acceptedRun.assistantMessageId) ?? assistantPlaceholder : assistantPlaceholder;
|
|
1462
|
+
const admission = {
|
|
1463
|
+
inputMessageId: acceptedInput.id,
|
|
1509
1464
|
runId: acceptedRun.id,
|
|
1510
|
-
assistantMessageId:
|
|
1465
|
+
assistantMessageId: assistantPlaceholder.id,
|
|
1466
|
+
input: acceptedInput,
|
|
1467
|
+
run: acceptedRun,
|
|
1468
|
+
assistant: acceptedAssistant,
|
|
1511
1469
|
snapshotVersion: acceptedSnapshot.version
|
|
1470
|
+
};
|
|
1471
|
+
outerAdmission.resolve(admission);
|
|
1472
|
+
await publish({
|
|
1473
|
+
type: "admission",
|
|
1474
|
+
eventId: generateId(),
|
|
1475
|
+
conversationId: acceptedRun.conversationId,
|
|
1476
|
+
runId: acceptedRun.id,
|
|
1477
|
+
snapshotVersion: acceptedSnapshot.version,
|
|
1478
|
+
input: acceptedInput,
|
|
1479
|
+
run: acceptedRun,
|
|
1480
|
+
assistant: acceptedAssistant,
|
|
1481
|
+
emittedAt: now().toISOString()
|
|
1512
1482
|
});
|
|
1513
1483
|
await publish({
|
|
1514
1484
|
type: "run-state",
|
|
@@ -1597,34 +1567,7 @@ function createAgentRuntime(config) {
|
|
|
1597
1567
|
})();
|
|
1598
1568
|
return publicTicket;
|
|
1599
1569
|
},
|
|
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
|
-
},
|
|
1570
|
+
resume,
|
|
1628
1571
|
async interrupt(input) {
|
|
1629
1572
|
const snapshot = await config.store.loadSnapshot(input.conversationId);
|
|
1630
1573
|
const run = findRun(snapshot.runs, input.runId);
|
|
@@ -1648,101 +1591,298 @@ function createAgentRuntime(config) {
|
|
|
1648
1591
|
}
|
|
1649
1592
|
return requested;
|
|
1650
1593
|
},
|
|
1594
|
+
async recover(options) {
|
|
1595
|
+
if (!config.store.scanRecoverablePage) {
|
|
1596
|
+
throw new Error("The configured agent store does not support bounded recovery scans");
|
|
1597
|
+
}
|
|
1598
|
+
const pageSize = options.pageSize ?? 100;
|
|
1599
|
+
const maxRuns = options.maxRuns ?? 1000;
|
|
1600
|
+
if (!Number.isSafeInteger(pageSize) || pageSize < 1 || pageSize > 1000) {
|
|
1601
|
+
throw new TypeError("Recovery pageSize must be an integer between 1 and 1000");
|
|
1602
|
+
}
|
|
1603
|
+
if (!Number.isSafeInteger(maxRuns) || maxRuns < 1) {
|
|
1604
|
+
throw new TypeError("Recovery maxRuns must be a positive safe integer");
|
|
1605
|
+
}
|
|
1606
|
+
const outcomes = [];
|
|
1607
|
+
let cursor;
|
|
1608
|
+
while (outcomes.length < maxRuns && !options.signal?.aborted) {
|
|
1609
|
+
const page = await config.store.scanRecoverablePage({
|
|
1610
|
+
...cursor && { cursor },
|
|
1611
|
+
limit: Math.min(pageSize, maxRuns - outcomes.length)
|
|
1612
|
+
});
|
|
1613
|
+
for (const item of page.items) {
|
|
1614
|
+
if (options.signal?.aborted)
|
|
1615
|
+
break;
|
|
1616
|
+
try {
|
|
1617
|
+
if (item.run.state === "queued") {
|
|
1618
|
+
const snapshot = await config.store.loadSnapshot(item.conversationId);
|
|
1619
|
+
const blockedByAcquiredPredecessor = snapshot.runs.some((run) => run.id !== item.run.id && (run.state === "running" || run.state === "interrupt_requested"));
|
|
1620
|
+
if (blockedByAcquiredPredecessor) {
|
|
1621
|
+
outcomes.push({
|
|
1622
|
+
conversationId: item.conversationId,
|
|
1623
|
+
runId: item.run.id,
|
|
1624
|
+
outcome: "skipped"
|
|
1625
|
+
});
|
|
1626
|
+
continue;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
const decision = await options.decide?.(item) ?? (item.run.state === "queued" ? { action: "resume" } : { action: "skip" });
|
|
1630
|
+
if (decision.action === "skip") {
|
|
1631
|
+
outcomes.push({
|
|
1632
|
+
conversationId: item.conversationId,
|
|
1633
|
+
runId: item.run.id,
|
|
1634
|
+
outcome: "skipped"
|
|
1635
|
+
});
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
if (decision.action === "abandon") {
|
|
1639
|
+
const abandoned = await config.store.recoverRun({
|
|
1640
|
+
conversationId: item.conversationId,
|
|
1641
|
+
runId: item.run.id,
|
|
1642
|
+
expectedRevision: item.run.revision,
|
|
1643
|
+
action: "abandon"
|
|
1644
|
+
});
|
|
1645
|
+
if (abandoned.outcome !== "applied") {
|
|
1646
|
+
throw new AgentRuntimeConflictError("recovery abandon");
|
|
1647
|
+
}
|
|
1648
|
+
outcomes.push({
|
|
1649
|
+
conversationId: item.conversationId,
|
|
1650
|
+
runId: item.run.id,
|
|
1651
|
+
outcome: "abandoned"
|
|
1652
|
+
});
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (decision.action === "requeue") {
|
|
1656
|
+
const requeued = await config.store.recoverRun({
|
|
1657
|
+
conversationId: item.conversationId,
|
|
1658
|
+
runId: item.run.id,
|
|
1659
|
+
expectedRevision: item.run.revision,
|
|
1660
|
+
action: "requeue",
|
|
1661
|
+
replaySafe: true
|
|
1662
|
+
});
|
|
1663
|
+
if (requeued.outcome !== "applied") {
|
|
1664
|
+
throw new AgentRuntimeConflictError("recovery requeue");
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
const context = await options.resolveContext(item);
|
|
1668
|
+
const resumed = resume({
|
|
1669
|
+
conversationId: item.conversationId,
|
|
1670
|
+
runId: item.run.id,
|
|
1671
|
+
context
|
|
1672
|
+
});
|
|
1673
|
+
resumed.result.catch(() => {
|
|
1674
|
+
return;
|
|
1675
|
+
});
|
|
1676
|
+
await resumed.accepted;
|
|
1677
|
+
outcomes.push({
|
|
1678
|
+
conversationId: item.conversationId,
|
|
1679
|
+
runId: item.run.id,
|
|
1680
|
+
outcome: decision.action === "requeue" ? "requeued" : "resumed"
|
|
1681
|
+
});
|
|
1682
|
+
} catch (error) {
|
|
1683
|
+
outcomes.push({
|
|
1684
|
+
conversationId: item.conversationId,
|
|
1685
|
+
runId: item.run.id,
|
|
1686
|
+
outcome: "failed",
|
|
1687
|
+
error
|
|
1688
|
+
});
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
cursor = page.nextCursor;
|
|
1692
|
+
if (!cursor || page.items.length === 0)
|
|
1693
|
+
break;
|
|
1694
|
+
}
|
|
1695
|
+
return outcomes;
|
|
1696
|
+
},
|
|
1651
1697
|
stop: (conversationKey, reason) => coordinator.stop(conversationKey, reason),
|
|
1652
1698
|
close: (options) => coordinator.close(options)
|
|
1653
1699
|
};
|
|
1654
1700
|
}
|
|
1655
1701
|
// src/agent-runtime/store.ts
|
|
1656
|
-
import { z as
|
|
1657
|
-
var AgentStoreConflictSchema =
|
|
1658
|
-
outcome:
|
|
1702
|
+
import { z as z6 } from "zod";
|
|
1703
|
+
var AgentStoreConflictSchema = z6.object({
|
|
1704
|
+
outcome: z6.literal("conflict"),
|
|
1659
1705
|
actualVersion: AgentRecordVersionSchema
|
|
1660
1706
|
});
|
|
1661
|
-
var AgentStoreNotFoundSchema =
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
var AgentStoreAppliedSchema = z7.object({
|
|
1665
|
-
outcome: z7.literal("applied"),
|
|
1707
|
+
var AgentStoreNotFoundSchema = z6.object({ outcome: z6.literal("not_found") });
|
|
1708
|
+
var AgentStoreAppliedSchema = z6.object({
|
|
1709
|
+
outcome: z6.literal("applied"),
|
|
1666
1710
|
snapshot: AgentSnapshotSchema
|
|
1667
1711
|
});
|
|
1668
|
-
var AgentStoreDuplicateSchema =
|
|
1669
|
-
outcome:
|
|
1712
|
+
var AgentStoreDuplicateSchema = z6.object({
|
|
1713
|
+
outcome: z6.literal("duplicate"),
|
|
1714
|
+
input: AgentMessageSchema,
|
|
1715
|
+
inputMessageId: AgentRecordIdSchema,
|
|
1670
1716
|
runId: AgentRecordIdSchema,
|
|
1717
|
+
assistantMessageId: AgentRecordIdSchema,
|
|
1671
1718
|
snapshot: AgentSnapshotSchema
|
|
1672
1719
|
});
|
|
1673
|
-
var AgentStoreMutationResultSchema =
|
|
1720
|
+
var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
1674
1721
|
AgentStoreAppliedSchema,
|
|
1675
1722
|
AgentStoreDuplicateSchema,
|
|
1676
1723
|
AgentStoreConflictSchema,
|
|
1677
1724
|
AgentStoreNotFoundSchema
|
|
1678
1725
|
]);
|
|
1679
|
-
var AcceptInputAndAssignRunSchema =
|
|
1680
|
-
idempotencyKey:
|
|
1726
|
+
var AcceptInputAndAssignRunSchema = z6.object({
|
|
1727
|
+
idempotencyKey: z6.string().min(1),
|
|
1681
1728
|
expectedVersion: AgentRecordVersionSchema.optional(),
|
|
1682
1729
|
input: AgentMessageSchema,
|
|
1683
1730
|
run: AgentRunSchema,
|
|
1684
1731
|
coalesceIntoRunId: AgentRecordIdSchema.optional()
|
|
1685
1732
|
});
|
|
1686
|
-
var AcquireAgentRunSchema =
|
|
1733
|
+
var AcquireAgentRunSchema = z6.object({
|
|
1687
1734
|
conversationId: AgentRecordIdSchema,
|
|
1688
1735
|
runId: AgentRecordIdSchema,
|
|
1689
1736
|
expectedRevision: AgentRecordVersionSchema,
|
|
1690
|
-
ownerId:
|
|
1737
|
+
ownerId: z6.string().min(1)
|
|
1691
1738
|
});
|
|
1692
|
-
var CheckpointRunAssistantSchema =
|
|
1739
|
+
var CheckpointRunAssistantSchema = z6.object({
|
|
1693
1740
|
conversationId: AgentRecordIdSchema,
|
|
1694
1741
|
runId: AgentRecordIdSchema,
|
|
1695
1742
|
expectedRevision: AgentRecordVersionSchema,
|
|
1696
|
-
ownerId:
|
|
1743
|
+
ownerId: z6.string().min(1),
|
|
1697
1744
|
assistant: AgentMessageSchema
|
|
1698
1745
|
});
|
|
1699
|
-
var CommitRunTerminalSchema =
|
|
1746
|
+
var CommitRunTerminalSchema = z6.object({
|
|
1700
1747
|
conversationId: AgentRecordIdSchema,
|
|
1701
1748
|
runId: AgentRecordIdSchema,
|
|
1702
1749
|
expectedRevision: AgentRecordVersionSchema,
|
|
1703
|
-
ownerId:
|
|
1750
|
+
ownerId: z6.string().min(1),
|
|
1704
1751
|
assistant: AgentMessageSchema,
|
|
1705
1752
|
reason: AgentTerminalReasonSchema,
|
|
1706
|
-
policyName:
|
|
1753
|
+
policyName: z6.string().min(1).optional()
|
|
1707
1754
|
});
|
|
1708
|
-
var RequestRunInterruptSchema =
|
|
1755
|
+
var RequestRunInterruptSchema = z6.object({
|
|
1709
1756
|
conversationId: AgentRecordIdSchema,
|
|
1710
1757
|
runId: AgentRecordIdSchema,
|
|
1711
1758
|
expectedRevision: AgentRecordVersionSchema
|
|
1712
1759
|
});
|
|
1713
|
-
var RecoverAgentRunSchema =
|
|
1760
|
+
var RecoverAgentRunSchema = z6.object({
|
|
1714
1761
|
conversationId: AgentRecordIdSchema,
|
|
1715
1762
|
runId: AgentRecordIdSchema,
|
|
1716
1763
|
expectedRevision: AgentRecordVersionSchema,
|
|
1717
|
-
action:
|
|
1718
|
-
replaySafe:
|
|
1764
|
+
action: z6.enum(["requeue", "abandon"]),
|
|
1765
|
+
replaySafe: z6.boolean().optional()
|
|
1719
1766
|
});
|
|
1720
|
-
var ReplaceCompactedRangeSchema =
|
|
1767
|
+
var ReplaceCompactedRangeSchema = z6.object({
|
|
1721
1768
|
conversationId: AgentRecordIdSchema,
|
|
1722
1769
|
expectedVersion: AgentRecordVersionSchema,
|
|
1723
|
-
replacedMessageIds:
|
|
1770
|
+
replacedMessageIds: z6.array(AgentRecordIdSchema).min(1),
|
|
1724
1771
|
summary: AgentMessageSchema
|
|
1725
1772
|
});
|
|
1726
|
-
|
|
1727
|
-
|
|
1773
|
+
// src/agent-runtime/store-driver.ts
|
|
1774
|
+
import { z as z7 } from "zod";
|
|
1775
|
+
var AgentAdmissionIdentitySchema = z7.object({
|
|
1776
|
+
idempotencyKey: z7.string().min(1),
|
|
1777
|
+
inputMessageId: AgentRecordIdSchema,
|
|
1778
|
+
runId: AgentRecordIdSchema,
|
|
1779
|
+
assistantMessageId: AgentRecordIdSchema
|
|
1780
|
+
});
|
|
1781
|
+
var AgentStoredStateSchema = z7.object({
|
|
1782
|
+
schemaVersion: z7.literal(1),
|
|
1783
|
+
conversationId: AgentRecordIdSchema,
|
|
1784
|
+
version: AgentRecordVersionSchema,
|
|
1785
|
+
runs: z7.array(AgentRunSchema),
|
|
1786
|
+
admissions: z7.array(AgentAdmissionIdentitySchema)
|
|
1787
|
+
});
|
|
1788
|
+
var AgentHistoryMutationSchema = z7.discriminatedUnion("type", [
|
|
1789
|
+
z7.object({ type: z7.literal("admit"), input: AgentMessageSchema }),
|
|
1790
|
+
z7.object({
|
|
1791
|
+
type: z7.literal("upsert-assistant"),
|
|
1792
|
+
message: AgentMessageSchema
|
|
1793
|
+
}),
|
|
1794
|
+
z7.object({
|
|
1795
|
+
type: z7.literal("replace-compacted-range"),
|
|
1796
|
+
replacedMessageIds: z7.array(AgentRecordIdSchema).min(1),
|
|
1797
|
+
summary: AgentMessageSchema
|
|
1798
|
+
})
|
|
1799
|
+
]);
|
|
1800
|
+
var AgentRecoverableDescriptorSchema = z7.object({
|
|
1801
|
+
conversationId: AgentRecordIdSchema,
|
|
1802
|
+
run: AgentRunSchema
|
|
1803
|
+
});
|
|
1804
|
+
var AgentRecoverablePageSchema = z7.object({
|
|
1805
|
+
items: z7.array(AgentRecoverableDescriptorSchema),
|
|
1806
|
+
nextCursor: z7.string().min(1).optional()
|
|
1807
|
+
});
|
|
1808
|
+
var AgentRecoverableScanInputSchema = z7.object({
|
|
1809
|
+
cursor: z7.string().min(1).optional(),
|
|
1810
|
+
limit: z7.number().int().min(1).max(1000)
|
|
1811
|
+
});
|
|
1812
|
+
function emptyState(conversationId) {
|
|
1813
|
+
return AgentStoredStateSchema.parse({
|
|
1728
1814
|
schemaVersion: 1,
|
|
1729
1815
|
conversationId,
|
|
1730
1816
|
version: 0,
|
|
1731
|
-
|
|
1732
|
-
|
|
1817
|
+
runs: [],
|
|
1818
|
+
admissions: []
|
|
1733
1819
|
});
|
|
1734
1820
|
}
|
|
1735
|
-
function
|
|
1736
|
-
|
|
1821
|
+
function snapshotOf(state, messages) {
|
|
1822
|
+
validateAggregate(state, messages);
|
|
1823
|
+
return AgentSnapshotSchema.parse({
|
|
1824
|
+
schemaVersion: 1,
|
|
1825
|
+
conversationId: state.conversationId,
|
|
1826
|
+
version: state.version,
|
|
1827
|
+
messages,
|
|
1828
|
+
runs: state.runs
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
function validateAggregate(state, messages) {
|
|
1832
|
+
const runIds = new Set;
|
|
1833
|
+
const assistantIds = new Set;
|
|
1834
|
+
const messageIds = new Set;
|
|
1835
|
+
const idempotencyKeys = new Set;
|
|
1836
|
+
const admittedInputIds = new Set;
|
|
1837
|
+
for (const run of state.runs) {
|
|
1838
|
+
if (run.conversationId !== state.conversationId || runIds.has(run.id) || assistantIds.has(run.assistantMessageId)) {
|
|
1839
|
+
throw new TypeError("Stored agent state contains inconsistent run identities");
|
|
1840
|
+
}
|
|
1841
|
+
runIds.add(run.id);
|
|
1842
|
+
assistantIds.add(run.assistantMessageId);
|
|
1843
|
+
}
|
|
1844
|
+
for (const message of messages) {
|
|
1845
|
+
if (message.conversationId !== state.conversationId || messageIds.has(message.id)) {
|
|
1846
|
+
throw new TypeError("Stored agent history contains inconsistent message identities");
|
|
1847
|
+
}
|
|
1848
|
+
messageIds.add(message.id);
|
|
1849
|
+
if (assistantIds.has(message.id) && message.runId === undefined) {
|
|
1850
|
+
throw new TypeError("Stored history occupies a reserved assistant identity");
|
|
1851
|
+
}
|
|
1852
|
+
if (message.runId !== undefined) {
|
|
1853
|
+
const run = state.runs.find((candidate) => candidate.id === message.runId);
|
|
1854
|
+
if (!run || message.role !== "assistant" || run.assistantMessageId !== message.id) {
|
|
1855
|
+
throw new TypeError("Stored assistant history does not match its reserved run identity");
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
for (const admission of state.admissions) {
|
|
1860
|
+
const run = state.runs.find((candidate) => candidate.id === admission.runId);
|
|
1861
|
+
if (idempotencyKeys.has(admission.idempotencyKey) || admittedInputIds.has(admission.inputMessageId) || !run || run.assistantMessageId !== admission.assistantMessageId || !run.inputMessageIds.includes(admission.inputMessageId)) {
|
|
1862
|
+
throw new TypeError("Stored admission identity is inconsistent with its assigned run");
|
|
1863
|
+
}
|
|
1864
|
+
idempotencyKeys.add(admission.idempotencyKey);
|
|
1865
|
+
admittedInputIds.add(admission.inputMessageId);
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
function recoverableDescriptors(state) {
|
|
1869
|
+
return state.runs.filter((run) => ["queued", "running", "interrupt_requested"].includes(run.state)).map((run) => ({ conversationId: state.conversationId, run }));
|
|
1870
|
+
}
|
|
1871
|
+
var RecoverableCursorSchema = z7.tuple([AgentRecordIdSchema, AgentRecordIdSchema]);
|
|
1872
|
+
function recoverableCursor(input) {
|
|
1873
|
+
return JSON.stringify([input.conversationId, input.run.id]);
|
|
1874
|
+
}
|
|
1875
|
+
function parseRecoverableCursor(cursor) {
|
|
1876
|
+
return RecoverableCursorSchema.parse(JSON.parse(cursor));
|
|
1737
1877
|
}
|
|
1738
1878
|
function replaceRun(runs, next) {
|
|
1739
1879
|
return runs.map((run) => run.id === next.id ? next : run);
|
|
1740
1880
|
}
|
|
1741
1881
|
function replaceMessage(messages, next) {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
return
|
|
1882
|
+
return messages.some((message) => message.id === next.id) ? messages.map((message) => message.id === next.id ? next : message) : [...messages, next];
|
|
1883
|
+
}
|
|
1884
|
+
function conflict(actualVersion) {
|
|
1885
|
+
return { outcome: "conflict", actualVersion };
|
|
1746
1886
|
}
|
|
1747
1887
|
function terminalState(reason) {
|
|
1748
1888
|
if (reason === "success" || reason === "policy_stop")
|
|
@@ -1764,217 +1904,426 @@ function terminalMessageStatus(reason) {
|
|
|
1764
1904
|
}
|
|
1765
1905
|
return "failed";
|
|
1766
1906
|
}
|
|
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
|
-
};
|
|
1907
|
+
function applied(current, admissions, input, historyMutation) {
|
|
1785
1908
|
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");
|
|
1909
|
+
outcome: "applied",
|
|
1910
|
+
snapshot: AgentSnapshotSchema.parse({
|
|
1911
|
+
...current,
|
|
1912
|
+
version: current.version + 1,
|
|
1913
|
+
runs: input.runs ?? current.runs,
|
|
1914
|
+
messages: input.messages ?? current.messages
|
|
1915
|
+
}),
|
|
1916
|
+
admissions,
|
|
1917
|
+
...historyMutation && { historyMutation }
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
function reduceStore(current, currentAdmissions, operation, duplicateInput) {
|
|
1921
|
+
if (operation.type === "accept") {
|
|
1922
|
+
const input = operation.input;
|
|
1923
|
+
const duplicate = currentAdmissions.find((candidate) => candidate.idempotencyKey === input.idempotencyKey);
|
|
1924
|
+
if (duplicate) {
|
|
1925
|
+
if (!duplicateInput) {
|
|
1926
|
+
throw new Error("Duplicate admission input is unavailable from canonical history");
|
|
1809
1927
|
}
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1928
|
+
return {
|
|
1929
|
+
outcome: "duplicate",
|
|
1930
|
+
input: duplicateInput,
|
|
1931
|
+
inputMessageId: duplicate.inputMessageId,
|
|
1932
|
+
runId: duplicate.runId,
|
|
1933
|
+
assistantMessageId: duplicate.assistantMessageId,
|
|
1934
|
+
snapshot: current
|
|
1935
|
+
};
|
|
1936
|
+
}
|
|
1937
|
+
if (input.expectedVersion !== undefined && input.expectedVersion !== current.version) {
|
|
1938
|
+
return conflict(current.version);
|
|
1939
|
+
}
|
|
1940
|
+
const coalescedRun = input.coalesceIntoRunId ? current.runs.find((candidate) => candidate.id === input.coalesceIntoRunId) : undefined;
|
|
1941
|
+
if (input.coalesceIntoRunId !== undefined && (!coalescedRun || coalescedRun.conversationId !== input.input.conversationId || coalescedRun.state !== "queued" || coalescedRun.ownerId !== undefined || coalescedRun.terminalReason !== undefined)) {
|
|
1942
|
+
return coalescedRun ? conflict(coalescedRun.revision) : { outcome: "not_found" };
|
|
1943
|
+
}
|
|
1944
|
+
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))) {
|
|
1945
|
+
throw new TypeError("Input and queued run do not form one valid assignment");
|
|
1946
|
+
}
|
|
1947
|
+
const assignedRun = coalescedRun ? AgentRunSchema.parse({
|
|
1948
|
+
...coalescedRun,
|
|
1949
|
+
inputMessageIds: [...coalescedRun.inputMessageIds, input.input.id],
|
|
1950
|
+
revision: coalescedRun.revision + 1,
|
|
1951
|
+
updatedAt: new Date().toISOString()
|
|
1952
|
+
}) : input.run;
|
|
1953
|
+
const admission = AgentAdmissionIdentitySchema.parse({
|
|
1954
|
+
idempotencyKey: input.idempotencyKey,
|
|
1955
|
+
inputMessageId: input.input.id,
|
|
1956
|
+
runId: assignedRun.id,
|
|
1957
|
+
assistantMessageId: assignedRun.assistantMessageId
|
|
1958
|
+
});
|
|
1959
|
+
return applied(current, [...currentAdmissions, admission], {
|
|
1960
|
+
messages: [...current.messages, input.input],
|
|
1961
|
+
runs: coalescedRun ? replaceRun(current.runs, assignedRun) : [...current.runs, assignedRun]
|
|
1962
|
+
}, { type: "admit", input: input.input });
|
|
1963
|
+
}
|
|
1964
|
+
const conversationId = operation.input.conversationId;
|
|
1965
|
+
const run = operation.type === "compact" ? undefined : current.runs.find((candidate) => candidate.id === operation.input.runId);
|
|
1966
|
+
if (operation.type !== "compact" && !run)
|
|
1967
|
+
return { outcome: "not_found" };
|
|
1968
|
+
if (operation.type === "acquire" && run) {
|
|
1969
|
+
const runPosition = current.runs.findIndex((candidate) => candidate.id === run.id);
|
|
1970
|
+
const acquisitionBlocked = current.runs.some((candidate, index) => candidate.id !== run.id && (candidate.state === "running" || candidate.state === "interrupt_requested" || index < runPosition && candidate.state === "queued"));
|
|
1971
|
+
if (run.revision !== operation.input.expectedRevision || run.state !== "queued" || acquisitionBlocked) {
|
|
1972
|
+
return conflict(run.revision);
|
|
1973
|
+
}
|
|
1974
|
+
const next = AgentRunSchema.parse({
|
|
1975
|
+
...run,
|
|
1976
|
+
state: "running",
|
|
1977
|
+
ownerId: operation.input.ownerId,
|
|
1978
|
+
revision: run.revision + 1,
|
|
1979
|
+
updatedAt: new Date().toISOString()
|
|
1980
|
+
});
|
|
1981
|
+
return applied(current, currentAdmissions, {
|
|
1982
|
+
runs: replaceRun(current.runs, next)
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
if (operation.type === "checkpoint" && run) {
|
|
1986
|
+
const input = operation.input;
|
|
1987
|
+
if (run.revision !== input.expectedRevision || run.state !== "running" || run.ownerId !== input.ownerId || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== "streaming") {
|
|
1988
|
+
return conflict(run.revision);
|
|
1989
|
+
}
|
|
1990
|
+
const next = AgentRunSchema.parse({
|
|
1991
|
+
...run,
|
|
1992
|
+
revision: run.revision + 1,
|
|
1993
|
+
updatedAt: new Date().toISOString()
|
|
1994
|
+
});
|
|
1995
|
+
return applied(current, currentAdmissions, {
|
|
1996
|
+
runs: replaceRun(current.runs, next),
|
|
1997
|
+
messages: replaceMessage(current.messages, input.assistant)
|
|
1998
|
+
}, { type: "upsert-assistant", message: input.assistant });
|
|
1999
|
+
}
|
|
2000
|
+
if (operation.type === "interrupt" && run) {
|
|
2001
|
+
if (run.revision !== operation.input.expectedRevision || run.state !== "running") {
|
|
2002
|
+
return conflict(run.revision);
|
|
2003
|
+
}
|
|
2004
|
+
const next = AgentRunSchema.parse({
|
|
2005
|
+
...run,
|
|
2006
|
+
state: "interrupt_requested",
|
|
2007
|
+
revision: run.revision + 1,
|
|
2008
|
+
updatedAt: new Date().toISOString()
|
|
2009
|
+
});
|
|
2010
|
+
return applied(current, currentAdmissions, {
|
|
2011
|
+
runs: replaceRun(current.runs, next)
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
if (operation.type === "recover" && run) {
|
|
2015
|
+
const input = operation.input;
|
|
2016
|
+
if (run.revision !== input.expectedRevision || !["queued", "running", "interrupt_requested"].includes(run.state)) {
|
|
2017
|
+
return conflict(run.revision);
|
|
2018
|
+
}
|
|
2019
|
+
if (input.action === "requeue" && run.state !== "queued" && input.replaySafe !== true) {
|
|
2020
|
+
throw new TypeError("Recovering an acquired run requires explicit replaySafe evidence");
|
|
2021
|
+
}
|
|
2022
|
+
const next = AgentRunSchema.parse({
|
|
2023
|
+
schemaVersion: 1,
|
|
2024
|
+
id: run.id,
|
|
2025
|
+
conversationId: run.conversationId,
|
|
2026
|
+
inputMessageIds: run.inputMessageIds,
|
|
2027
|
+
assistantMessageId: run.assistantMessageId,
|
|
2028
|
+
state: input.action === "requeue" ? "queued" : "abandoned",
|
|
2029
|
+
revision: run.revision + 1,
|
|
2030
|
+
...input.action === "abandon" && { terminalReason: "abandoned" },
|
|
2031
|
+
createdAt: run.createdAt,
|
|
2032
|
+
updatedAt: new Date().toISOString()
|
|
2033
|
+
});
|
|
2034
|
+
if (input.action === "abandon") {
|
|
2035
|
+
const existingAssistant = current.messages.find((message) => message.id === run.assistantMessageId);
|
|
2036
|
+
const assistant = AgentMessageSchema.parse({
|
|
2037
|
+
...existingAssistant ?? {
|
|
2038
|
+
schemaVersion: 1,
|
|
2039
|
+
id: run.assistantMessageId,
|
|
2040
|
+
conversationId: run.conversationId,
|
|
2041
|
+
runId: run.id,
|
|
2042
|
+
role: "assistant",
|
|
2043
|
+
parts: [],
|
|
2044
|
+
createdAt: run.createdAt
|
|
2045
|
+
},
|
|
2046
|
+
status: "failed",
|
|
1814
2047
|
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
2048
|
});
|
|
1822
|
-
|
|
1823
|
-
|
|
2049
|
+
return applied(current, currentAdmissions, {
|
|
2050
|
+
runs: replaceRun(current.runs, next),
|
|
2051
|
+
messages: replaceMessage(current.messages, assistant)
|
|
2052
|
+
}, { type: "upsert-assistant", message: assistant });
|
|
2053
|
+
}
|
|
2054
|
+
return applied(current, currentAdmissions, {
|
|
2055
|
+
runs: replaceRun(current.runs, next)
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
if (operation.type === "terminal" && run) {
|
|
2059
|
+
const input = operation.input;
|
|
2060
|
+
if (run.revision !== input.expectedRevision || run.state !== "running" && run.state !== "interrupt_requested" || run.ownerId !== input.ownerId || input.assistant.runId !== run.id || input.assistant.id !== run.assistantMessageId || input.assistant.conversationId !== run.conversationId || input.assistant.role !== "assistant" || input.assistant.status !== terminalMessageStatus(input.reason)) {
|
|
2061
|
+
return conflict(run.revision);
|
|
2062
|
+
}
|
|
2063
|
+
const next = AgentRunSchema.parse({
|
|
2064
|
+
...run,
|
|
2065
|
+
state: terminalState(input.reason),
|
|
2066
|
+
terminalReason: input.reason,
|
|
2067
|
+
...input.policyName && { terminalPolicyName: input.policyName },
|
|
2068
|
+
revision: run.revision + 1,
|
|
2069
|
+
updatedAt: new Date().toISOString()
|
|
2070
|
+
});
|
|
2071
|
+
return applied(current, currentAdmissions, {
|
|
2072
|
+
runs: replaceRun(current.runs, next),
|
|
2073
|
+
messages: replaceMessage(current.messages, input.assistant)
|
|
2074
|
+
}, { type: "upsert-assistant", message: input.assistant });
|
|
2075
|
+
}
|
|
2076
|
+
if (operation.type === "compact") {
|
|
2077
|
+
const input = operation.input;
|
|
2078
|
+
if (current.conversationId !== conversationId)
|
|
2079
|
+
return { outcome: "not_found" };
|
|
2080
|
+
if (current.version !== input.expectedVersion)
|
|
2081
|
+
return conflict(current.version);
|
|
2082
|
+
const replaced = new Set(input.replacedMessageIds);
|
|
2083
|
+
if (!input.replacedMessageIds.every((id) => current.messages.some((m) => m.id === id))) {
|
|
2084
|
+
return { outcome: "not_found" };
|
|
2085
|
+
}
|
|
2086
|
+
const positions = current.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
2087
|
+
const first = positions[0];
|
|
2088
|
+
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)) {
|
|
2089
|
+
throw new TypeError("Compaction replacement must be one valid contiguous history range");
|
|
2090
|
+
}
|
|
2091
|
+
const messages = [
|
|
2092
|
+
...current.messages.slice(0, first),
|
|
2093
|
+
input.summary,
|
|
2094
|
+
...current.messages.slice(first + positions.length)
|
|
2095
|
+
];
|
|
2096
|
+
return applied(current, currentAdmissions, { messages }, {
|
|
2097
|
+
type: "replace-compacted-range",
|
|
2098
|
+
replacedMessageIds: input.replacedMessageIds,
|
|
2099
|
+
summary: input.summary
|
|
2100
|
+
});
|
|
2101
|
+
}
|
|
2102
|
+
return { outcome: "not_found" };
|
|
2103
|
+
}
|
|
2104
|
+
function operationConversationId(operation) {
|
|
2105
|
+
return operation.type === "accept" ? operation.input.input.conversationId : operation.input.conversationId;
|
|
2106
|
+
}
|
|
2107
|
+
function createAgentRuntimeStore(driver) {
|
|
2108
|
+
const loadSnapshot = (conversationId) => driver.transaction(async (transaction) => {
|
|
2109
|
+
const [stored, messages] = await Promise.all([
|
|
2110
|
+
driver.state.load(transaction, conversationId),
|
|
2111
|
+
driver.history.load(transaction, conversationId)
|
|
2112
|
+
]);
|
|
2113
|
+
return snapshotOf(stored ?? emptyState(conversationId), messages);
|
|
2114
|
+
});
|
|
2115
|
+
const mutate = (operation) => driver.transaction(async (transaction) => {
|
|
2116
|
+
const conversationId = operationConversationId(operation);
|
|
2117
|
+
const [stored, messages] = await Promise.all([
|
|
2118
|
+
driver.state.load(transaction, conversationId),
|
|
2119
|
+
driver.history.load(transaction, conversationId)
|
|
2120
|
+
]);
|
|
2121
|
+
const state = AgentStoredStateSchema.parse(stored ?? emptyState(conversationId));
|
|
2122
|
+
const current = snapshotOf(state, messages);
|
|
2123
|
+
const duplicateIdentity = operation.type === "accept" ? state.admissions.find((candidate) => candidate.idempotencyKey === operation.input.idempotencyKey) : undefined;
|
|
2124
|
+
const duplicateInput = duplicateIdentity ? await driver.history.loadById(transaction, {
|
|
2125
|
+
conversationId,
|
|
2126
|
+
messageId: duplicateIdentity.inputMessageId
|
|
2127
|
+
}) : undefined;
|
|
2128
|
+
if (duplicateIdentity && duplicateInput && (duplicateInput.id !== duplicateIdentity.inputMessageId || duplicateInput.conversationId !== conversationId || duplicateInput.role !== "user" || duplicateInput.status !== "committed" || duplicateInput.runId !== undefined)) {
|
|
2129
|
+
throw new TypeError("Canonical duplicate input does not match its admission identity");
|
|
2130
|
+
}
|
|
2131
|
+
const reduced = reduceStore(current, state.admissions, operation, duplicateInput);
|
|
2132
|
+
if (reduced.outcome !== "applied")
|
|
2133
|
+
return reduced;
|
|
2134
|
+
const nextState = AgentStoredStateSchema.parse({
|
|
2135
|
+
schemaVersion: 1,
|
|
2136
|
+
conversationId,
|
|
2137
|
+
version: reduced.snapshot.version,
|
|
2138
|
+
runs: reduced.snapshot.runs,
|
|
2139
|
+
admissions: reduced.admissions
|
|
2140
|
+
});
|
|
2141
|
+
const outcome = await driver.state.compareAndSwap(transaction, {
|
|
2142
|
+
conversationId,
|
|
2143
|
+
expectedVersion: current.version,
|
|
2144
|
+
next: nextState,
|
|
2145
|
+
recoverable: recoverableDescriptors(nextState)
|
|
2146
|
+
});
|
|
2147
|
+
if (outcome.outcome === "conflict")
|
|
2148
|
+
return conflict(outcome.actualVersion);
|
|
2149
|
+
if (reduced.historyMutation) {
|
|
2150
|
+
await driver.history.apply(transaction, reduced.historyMutation);
|
|
2151
|
+
}
|
|
2152
|
+
return { outcome: "applied", snapshot: reduced.snapshot };
|
|
2153
|
+
});
|
|
2154
|
+
return {
|
|
2155
|
+
loadSnapshot,
|
|
2156
|
+
acceptInputAndAssignRun: (input) => mutate({
|
|
2157
|
+
type: "accept",
|
|
2158
|
+
input: AcceptInputAndAssignRunSchema.parse(input)
|
|
2159
|
+
}),
|
|
2160
|
+
acquireRun: (input) => mutate({ type: "acquire", input: AcquireAgentRunSchema.parse(input) }),
|
|
2161
|
+
checkpointRunAssistant: (input) => mutate({
|
|
2162
|
+
type: "checkpoint",
|
|
2163
|
+
input: CheckpointRunAssistantSchema.parse(input)
|
|
2164
|
+
}),
|
|
2165
|
+
requestRunInterrupt: (input) => mutate({
|
|
2166
|
+
type: "interrupt",
|
|
2167
|
+
input: RequestRunInterruptSchema.parse(input)
|
|
2168
|
+
}),
|
|
2169
|
+
recoverRun: (input) => mutate({ type: "recover", input: RecoverAgentRunSchema.parse(input) }),
|
|
2170
|
+
commitRunTerminal: (input) => mutate({ type: "terminal", input: CommitRunTerminalSchema.parse(input) }),
|
|
2171
|
+
replaceCompactedRange: (input) => mutate({
|
|
2172
|
+
type: "compact",
|
|
2173
|
+
input: ReplaceCompactedRangeSchema.parse(input)
|
|
2174
|
+
}),
|
|
2175
|
+
async scanRecoverable() {
|
|
2176
|
+
const snapshots = [];
|
|
2177
|
+
const seenConversationIds = new Set;
|
|
2178
|
+
let cursor;
|
|
2179
|
+
do {
|
|
2180
|
+
const page = AgentRecoverablePageSchema.parse(await driver.scanRecoverable({
|
|
2181
|
+
...cursor && { cursor },
|
|
2182
|
+
limit: 100
|
|
2183
|
+
}));
|
|
2184
|
+
const conversationIds = [
|
|
2185
|
+
...new Set(page.items.map((item) => item.conversationId))
|
|
2186
|
+
].filter((conversationId) => !seenConversationIds.has(conversationId));
|
|
2187
|
+
for (const conversationId of conversationIds)
|
|
2188
|
+
seenConversationIds.add(conversationId);
|
|
2189
|
+
snapshots.push(...await Promise.all(conversationIds.map(loadSnapshot)));
|
|
2190
|
+
cursor = page.nextCursor;
|
|
2191
|
+
} while (cursor !== undefined);
|
|
2192
|
+
return snapshots;
|
|
1824
2193
|
},
|
|
1825
|
-
async
|
|
1826
|
-
const
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
2194
|
+
async scanRecoverablePage(input) {
|
|
2195
|
+
const parsed = AgentRecoverableScanInputSchema.parse(input);
|
|
2196
|
+
return AgentRecoverablePageSchema.parse(await driver.scanRecoverable(parsed));
|
|
2197
|
+
}
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
function cloneStateMap(source) {
|
|
2201
|
+
return new Map([...source].map(([key, value]) => [
|
|
2202
|
+
key,
|
|
2203
|
+
AgentStoredStateSchema.parse(structuredClone(value))
|
|
2204
|
+
]));
|
|
2205
|
+
}
|
|
2206
|
+
function cloneHistoryMap(source) {
|
|
2207
|
+
return new Map([...source].map(([key, value]) => [
|
|
2208
|
+
key,
|
|
2209
|
+
value.map((message) => AgentMessageSchema.parse(structuredClone(message)))
|
|
2210
|
+
]));
|
|
2211
|
+
}
|
|
2212
|
+
function createMemoryAgentRuntimeStore() {
|
|
2213
|
+
let states = new Map;
|
|
2214
|
+
let histories = new Map;
|
|
2215
|
+
let archivedMessages = new Map;
|
|
2216
|
+
let transactionTail = Promise.resolve();
|
|
2217
|
+
const driver = {
|
|
2218
|
+
async transaction(work) {
|
|
2219
|
+
const previous = transactionTail;
|
|
2220
|
+
const release = Promise.withResolvers();
|
|
2221
|
+
transactionTail = previous.catch(() => {
|
|
2222
|
+
return;
|
|
2223
|
+
}).then(() => release.promise);
|
|
2224
|
+
await previous.catch(() => {
|
|
2225
|
+
return;
|
|
1840
2226
|
});
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
2227
|
+
const transaction = {
|
|
2228
|
+
states: cloneStateMap(states),
|
|
2229
|
+
histories: cloneHistoryMap(histories),
|
|
2230
|
+
archivedMessages: new Map([...archivedMessages].map(([conversationId, messages]) => [
|
|
2231
|
+
conversationId,
|
|
2232
|
+
new Map([...messages].map(([messageId, message]) => [
|
|
2233
|
+
messageId,
|
|
2234
|
+
AgentMessageSchema.parse(structuredClone(message))
|
|
2235
|
+
]))
|
|
2236
|
+
]))
|
|
2237
|
+
};
|
|
2238
|
+
try {
|
|
2239
|
+
const result = await work(transaction);
|
|
2240
|
+
states = transaction.states;
|
|
2241
|
+
histories = transaction.histories;
|
|
2242
|
+
archivedMessages = transaction.archivedMessages;
|
|
2243
|
+
return result;
|
|
2244
|
+
} finally {
|
|
2245
|
+
release.resolve();
|
|
1855
2246
|
}
|
|
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
2247
|
},
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2248
|
+
state: {
|
|
2249
|
+
async load(transaction, conversationId) {
|
|
2250
|
+
const state = transaction.states.get(conversationId);
|
|
2251
|
+
return state ? AgentStoredStateSchema.parse(structuredClone(state)) : undefined;
|
|
2252
|
+
},
|
|
2253
|
+
async compareAndSwap(transaction, input) {
|
|
2254
|
+
const current = transaction.states.get(input.conversationId);
|
|
2255
|
+
const actualVersion = current?.version ?? 0;
|
|
2256
|
+
if (actualVersion !== input.expectedVersion) {
|
|
2257
|
+
return { outcome: "conflict", actualVersion };
|
|
2258
|
+
}
|
|
2259
|
+
transaction.states.set(input.conversationId, AgentStoredStateSchema.parse(structuredClone(input.next)));
|
|
2260
|
+
return { outcome: "applied" };
|
|
1876
2261
|
}
|
|
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
2262
|
},
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
return
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
|
|
2263
|
+
history: {
|
|
2264
|
+
async load(transaction, conversationId) {
|
|
2265
|
+
return (transaction.histories.get(conversationId) ?? []).map((message) => AgentMessageSchema.parse(structuredClone(message)));
|
|
2266
|
+
},
|
|
2267
|
+
async loadById(transaction, input) {
|
|
2268
|
+
const active = (transaction.histories.get(input.conversationId) ?? []).find((message2) => message2.id === input.messageId);
|
|
2269
|
+
const message = active ?? transaction.archivedMessages.get(input.conversationId)?.get(input.messageId);
|
|
2270
|
+
return message ? AgentMessageSchema.parse(structuredClone(message)) : undefined;
|
|
2271
|
+
},
|
|
2272
|
+
async apply(transaction, rawMutation) {
|
|
2273
|
+
const mutation = AgentHistoryMutationSchema.parse(rawMutation);
|
|
2274
|
+
const conversationId = mutation.type === "admit" ? mutation.input.conversationId : mutation.type === "upsert-assistant" ? mutation.message.conversationId : mutation.summary.conversationId;
|
|
2275
|
+
const current = transaction.histories.get(conversationId) ?? [];
|
|
2276
|
+
if (mutation.type === "admit") {
|
|
2277
|
+
transaction.histories.set(conversationId, [...current, mutation.input]);
|
|
2278
|
+
return;
|
|
2279
|
+
}
|
|
2280
|
+
if (mutation.type === "upsert-assistant") {
|
|
2281
|
+
transaction.histories.set(conversationId, replaceMessage(current, mutation.message));
|
|
2282
|
+
return;
|
|
2283
|
+
}
|
|
2284
|
+
const replaced = new Set(mutation.replacedMessageIds);
|
|
2285
|
+
const positions = current.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
2286
|
+
const first = positions[0];
|
|
2287
|
+
if (first === undefined)
|
|
2288
|
+
throw new Error("Compaction history range disappeared");
|
|
2289
|
+
const archive = transaction.archivedMessages.get(conversationId) ?? new Map;
|
|
2290
|
+
for (const message of current.filter((candidate) => replaced.has(candidate.id))) {
|
|
2291
|
+
archive.set(message.id, AgentMessageSchema.parse(structuredClone(message)));
|
|
2292
|
+
}
|
|
2293
|
+
transaction.archivedMessages.set(conversationId, archive);
|
|
2294
|
+
transaction.histories.set(conversationId, [
|
|
2295
|
+
...current.slice(0, first),
|
|
2296
|
+
mutation.summary,
|
|
2297
|
+
...current.slice(first + positions.length)
|
|
2298
|
+
]);
|
|
1900
2299
|
}
|
|
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
2300
|
},
|
|
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()
|
|
2301
|
+
async scanRecoverable(input) {
|
|
2302
|
+
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));
|
|
2303
|
+
const cursorTuple = input.cursor ? parseRecoverableCursor(input.cursor) : undefined;
|
|
2304
|
+
const start = cursorTuple ? descriptors.findIndex((item) => item.conversationId === cursorTuple[0] && item.run.id === cursorTuple[1]) + 1 : 0;
|
|
2305
|
+
const items = descriptors.slice(start, start + input.limit);
|
|
2306
|
+
const last = items.at(-1);
|
|
2307
|
+
const hasMore = start + items.length < descriptors.length;
|
|
2308
|
+
return AgentRecoverablePageSchema.parse({
|
|
2309
|
+
items,
|
|
2310
|
+
...hasMore && last && { nextCursor: recoverableCursor(last) }
|
|
1935
2311
|
});
|
|
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
2312
|
}
|
|
1969
2313
|
};
|
|
2314
|
+
return createAgentRuntimeStore(driver);
|
|
1970
2315
|
}
|
|
1971
2316
|
export {
|
|
1972
2317
|
AcceptInputAndAssignRunSchema,
|
|
1973
2318
|
AcquireAgentRunSchema,
|
|
2319
|
+
AgentAdmissionEventSchema,
|
|
2320
|
+
AgentAdmissionIdentitySchema,
|
|
2321
|
+
AgentAssistantPlaceholderSchema,
|
|
1974
2322
|
AgentCheckpointEventSchema,
|
|
1975
2323
|
AgentControlPartSchema,
|
|
1976
2324
|
AgentCostValueSchema,
|
|
1977
2325
|
AgentFilePartSchema,
|
|
2326
|
+
AgentHistoryMutationSchema,
|
|
1978
2327
|
AgentJsonObjectSchema,
|
|
1979
2328
|
AgentMessagePartSchema,
|
|
1980
2329
|
AgentMessageRoleSchema,
|
|
@@ -1990,7 +2339,10 @@ export {
|
|
|
1990
2339
|
AgentReasoningStartEventSchema,
|
|
1991
2340
|
AgentRecordIdSchema,
|
|
1992
2341
|
AgentRecordVersionSchema,
|
|
2342
|
+
AgentRecoverableDescriptorSchema,
|
|
2343
|
+
AgentRecoverablePageSchema,
|
|
1993
2344
|
AgentRunEventSchema,
|
|
2345
|
+
AgentRunMetricsSchema,
|
|
1994
2346
|
AgentRunSchema,
|
|
1995
2347
|
AgentRunStateEventSchema,
|
|
1996
2348
|
AgentRunStateSchema,
|
|
@@ -2002,6 +2354,7 @@ export {
|
|
|
2002
2354
|
AgentStoreDuplicateSchema,
|
|
2003
2355
|
AgentStoreMutationResultSchema,
|
|
2004
2356
|
AgentStoreNotFoundSchema,
|
|
2357
|
+
AgentStoredStateSchema,
|
|
2005
2358
|
AgentTerminalEventSchema,
|
|
2006
2359
|
AgentTerminalReasonSchema,
|
|
2007
2360
|
AgentTextPartSchema,
|
|
@@ -2021,6 +2374,7 @@ export {
|
|
|
2021
2374
|
composeAgentPrompt,
|
|
2022
2375
|
createAgentObservability,
|
|
2023
2376
|
createAgentRuntime,
|
|
2377
|
+
createAgentRuntimeStore,
|
|
2024
2378
|
createAgentSessionCoordinator,
|
|
2025
2379
|
createAgentToolFenceLifecycle,
|
|
2026
2380
|
createMemoryAgentRuntimeStore,
|