stitchkit 0.56.4 → 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 +730 -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 +851 -445
- 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 +102 -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,196 +254,82 @@ 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,
|
|
258
|
+
var AgentAdmissionEventSchema = z.object({
|
|
259
|
+
type: z.literal("admission"),
|
|
260
|
+
eventId: AgentRecordIdSchema,
|
|
339
261
|
conversationId: AgentRecordIdSchema,
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
terminalPolicyName: z.string().min(1).optional(),
|
|
347
|
-
createdAt: AgentTimestampSchema,
|
|
348
|
-
updatedAt: AgentTimestampSchema
|
|
262
|
+
runId: AgentRecordIdSchema,
|
|
263
|
+
snapshotVersion: AgentRecordVersionSchema,
|
|
264
|
+
input: AgentMessageSchema,
|
|
265
|
+
run: AgentRunSchema,
|
|
266
|
+
assistant: z.union([AgentAssistantPlaceholderSchema, AgentMessageSchema]),
|
|
267
|
+
emittedAt: AgentTimestampSchema
|
|
349
268
|
});
|
|
350
|
-
var
|
|
351
|
-
schemaVersion: z.literal(1),
|
|
269
|
+
var EventIdentitySchema = z.object({
|
|
352
270
|
conversationId: AgentRecordIdSchema,
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
runs: z.array(AgentRunSchema)
|
|
271
|
+
runId: AgentRecordIdSchema,
|
|
272
|
+
emittedAt: AgentTimestampSchema
|
|
356
273
|
});
|
|
357
|
-
var
|
|
358
|
-
|
|
359
|
-
|
|
274
|
+
var AgentTransientDeltaEventSchema = EventIdentitySchema.extend({
|
|
275
|
+
type: z.literal("assistant-delta"),
|
|
276
|
+
runtimeEpoch: z.string().min(1),
|
|
277
|
+
sequence: z.int().nonnegative(),
|
|
278
|
+
textDelta: z.string()
|
|
360
279
|
});
|
|
361
|
-
var
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
280
|
+
var AgentTransientReasoningIdentitySchema = EventIdentitySchema.extend({
|
|
281
|
+
runtimeEpoch: z.string().min(1),
|
|
282
|
+
sequence: z.int().nonnegative(),
|
|
283
|
+
provider: AgentProviderEnvelopeSchema.optional()
|
|
365
284
|
});
|
|
366
|
-
var
|
|
367
|
-
|
|
368
|
-
outputTokens: AgentUsageValueSchema,
|
|
369
|
-
reasoningTokens: AgentUsageValueSchema.optional(),
|
|
370
|
-
cacheReadTokens: AgentUsageValueSchema.optional(),
|
|
371
|
-
cacheWriteTokens: AgentUsageValueSchema.optional(),
|
|
372
|
-
cost: AgentCostValueSchema.optional()
|
|
285
|
+
var AgentReasoningStartEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
286
|
+
type: z.literal("reasoning-start")
|
|
373
287
|
});
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
conversationId: AgentRecordIdSchema,
|
|
378
|
-
runId: AgentRecordIdSchema,
|
|
379
|
-
emittedAt: AgentTimestampSchema
|
|
288
|
+
var AgentReasoningDeltaEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
289
|
+
type: z.literal("reasoning-delta"),
|
|
290
|
+
textDelta: z.string()
|
|
380
291
|
});
|
|
381
|
-
var
|
|
382
|
-
type:
|
|
383
|
-
runtimeEpoch: z2.string().min(1),
|
|
384
|
-
sequence: z2.int().nonnegative(),
|
|
385
|
-
textDelta: z2.string()
|
|
292
|
+
var AgentReasoningEndEventSchema = AgentTransientReasoningIdentitySchema.extend({
|
|
293
|
+
type: z.literal("reasoning-end")
|
|
386
294
|
});
|
|
387
295
|
var AgentCheckpointEventSchema = EventIdentitySchema.extend({
|
|
388
|
-
type:
|
|
296
|
+
type: z.literal("assistant-checkpoint"),
|
|
389
297
|
eventId: AgentRecordIdSchema,
|
|
390
298
|
snapshotVersion: AgentRecordVersionSchema,
|
|
391
|
-
message: AgentMessageSchema
|
|
299
|
+
message: AgentMessageSchema,
|
|
300
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
392
301
|
});
|
|
393
302
|
var AgentRunStateEventSchema = EventIdentitySchema.extend({
|
|
394
|
-
type:
|
|
303
|
+
type: z.literal("run-state"),
|
|
395
304
|
eventId: AgentRecordIdSchema,
|
|
396
305
|
snapshotVersion: AgentRecordVersionSchema,
|
|
397
306
|
state: AgentRunStateSchema
|
|
398
307
|
});
|
|
399
308
|
var AgentToolStatusEventSchema = EventIdentitySchema.extend({
|
|
400
|
-
type:
|
|
401
|
-
runtimeEpoch:
|
|
402
|
-
sequence:
|
|
309
|
+
type: z.literal("tool-status"),
|
|
310
|
+
runtimeEpoch: z.string().min(1),
|
|
311
|
+
sequence: z.int().nonnegative(),
|
|
403
312
|
callId: AgentRecordIdSchema,
|
|
404
|
-
toolName:
|
|
405
|
-
status:
|
|
406
|
-
input:
|
|
407
|
-
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()
|
|
408
317
|
});
|
|
409
318
|
var AgentTerminalEventSchema = EventIdentitySchema.extend({
|
|
410
|
-
type:
|
|
319
|
+
type: z.literal("terminal"),
|
|
411
320
|
eventId: AgentRecordIdSchema,
|
|
412
321
|
snapshotVersion: AgentRecordVersionSchema,
|
|
413
322
|
reason: AgentTerminalReasonSchema,
|
|
414
|
-
policyName:
|
|
415
|
-
message: AgentMessageSchema
|
|
323
|
+
policyName: z.string().min(1).optional(),
|
|
324
|
+
message: AgentMessageSchema,
|
|
325
|
+
metrics: AgentRunMetricsSchema.optional()
|
|
416
326
|
});
|
|
417
|
-
var AgentRuntimeEventSchema =
|
|
327
|
+
var AgentRuntimeEventSchema = z.discriminatedUnion("type", [
|
|
328
|
+
AgentAdmissionEventSchema,
|
|
418
329
|
AgentTransientDeltaEventSchema,
|
|
330
|
+
AgentReasoningStartEventSchema,
|
|
331
|
+
AgentReasoningDeltaEventSchema,
|
|
332
|
+
AgentReasoningEndEventSchema,
|
|
419
333
|
AgentCheckpointEventSchema,
|
|
420
334
|
AgentRunStateEventSchema,
|
|
421
335
|
AgentToolStatusEventSchema,
|
|
@@ -547,15 +461,15 @@ function createAgentToolFenceLifecycle(config) {
|
|
|
547
461
|
};
|
|
548
462
|
}
|
|
549
463
|
// src/agent-runtime/models.ts
|
|
550
|
-
import { z as
|
|
551
|
-
var AgentModelCapabilitySchema =
|
|
552
|
-
var AgentModelDescriptorSchema =
|
|
553
|
-
provider:
|
|
554
|
-
modelId:
|
|
555
|
-
contextWindow:
|
|
556
|
-
capabilities:
|
|
557
|
-
observedAt:
|
|
558
|
-
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()
|
|
559
473
|
});
|
|
560
474
|
function defineModelRegistry(config) {
|
|
561
475
|
const descriptors = new Map;
|
|
@@ -595,25 +509,25 @@ function defineModelRegistry(config) {
|
|
|
595
509
|
};
|
|
596
510
|
}
|
|
597
511
|
// src/agent-runtime/observability.ts
|
|
598
|
-
import { z as
|
|
599
|
-
var AgentRunEventSchema =
|
|
600
|
-
schemaVersion:
|
|
512
|
+
import { z as z3 } from "zod";
|
|
513
|
+
var AgentRunEventSchema = z3.object({
|
|
514
|
+
schemaVersion: z3.literal(1),
|
|
601
515
|
eventId: AgentRecordIdSchema,
|
|
602
|
-
type:
|
|
516
|
+
type: z3.enum(["run-started", "step-finished", "run-terminal"]),
|
|
603
517
|
conversationId: AgentRecordIdSchema,
|
|
604
518
|
runId: AgentRecordIdSchema,
|
|
605
|
-
traceId:
|
|
606
|
-
spanId:
|
|
607
|
-
parentSpanId:
|
|
519
|
+
traceId: z3.string().min(1),
|
|
520
|
+
spanId: z3.string().min(1),
|
|
521
|
+
parentSpanId: z3.string().min(1).optional(),
|
|
608
522
|
state: AgentRunStateSchema,
|
|
609
523
|
terminalReason: AgentTerminalReasonSchema.optional(),
|
|
610
|
-
modelId:
|
|
611
|
-
step:
|
|
612
|
-
queueWaitMs:
|
|
613
|
-
durationMs:
|
|
614
|
-
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(),
|
|
615
529
|
usage: AgentUsageSchema.optional(),
|
|
616
|
-
internalCause:
|
|
530
|
+
internalCause: z3.unknown().optional(),
|
|
617
531
|
emittedAt: AgentTimestampSchema
|
|
618
532
|
});
|
|
619
533
|
function createAgentObservability(config) {
|
|
@@ -638,10 +552,10 @@ function createAgentObservability(config) {
|
|
|
638
552
|
};
|
|
639
553
|
}
|
|
640
554
|
// src/agent-runtime/prompt.ts
|
|
641
|
-
import { z as
|
|
642
|
-
var AgentTokenCountSchema =
|
|
643
|
-
value:
|
|
644
|
-
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"])
|
|
645
559
|
});
|
|
646
560
|
function knownValue(value) {
|
|
647
561
|
return value.provenance === "unavailable" ? undefined : value.value;
|
|
@@ -720,7 +634,7 @@ import {
|
|
|
720
634
|
stepCountIs,
|
|
721
635
|
streamText
|
|
722
636
|
} from "ai";
|
|
723
|
-
import { z as
|
|
637
|
+
import { z as z5 } from "zod";
|
|
724
638
|
class AgentRuntimeConflictError extends Error {
|
|
725
639
|
constructor(operation) {
|
|
726
640
|
super(`Agent runtime store conflict during ${operation}`);
|
|
@@ -739,7 +653,7 @@ function findRun(runs, runId) {
|
|
|
739
653
|
return run;
|
|
740
654
|
}
|
|
741
655
|
function jsonValue(value) {
|
|
742
|
-
const parsed =
|
|
656
|
+
const parsed = z5.json().safeParse(value);
|
|
743
657
|
return parsed.success ? parsed.data : { message: "Non-JSON tool output omitted" };
|
|
744
658
|
}
|
|
745
659
|
function providerEnvelope(value) {
|
|
@@ -984,6 +898,12 @@ function createAgentRuntime(config) {
|
|
|
984
898
|
assistant
|
|
985
899
|
}), "assistant checkpoint");
|
|
986
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
|
+
};
|
|
987
907
|
await publish({
|
|
988
908
|
type: "assistant-checkpoint",
|
|
989
909
|
eventId: generateId(),
|
|
@@ -991,6 +911,7 @@ function createAgentRuntime(config) {
|
|
|
991
911
|
runId: run.id,
|
|
992
912
|
snapshotVersion: snapshot.version,
|
|
993
913
|
message: assistant,
|
|
914
|
+
metrics: checkpointMetrics,
|
|
994
915
|
emittedAt: now().toISOString()
|
|
995
916
|
});
|
|
996
917
|
};
|
|
@@ -1102,11 +1023,42 @@ function createAgentRuntime(config) {
|
|
|
1102
1023
|
} else if (part.type === "reasoning-start") {
|
|
1103
1024
|
reasoningPartIndex = undefined;
|
|
1104
1025
|
updateReasoning("", part.providerMetadata);
|
|
1026
|
+
const provider = providerEnvelope(part.providerMetadata);
|
|
1027
|
+
await publish({
|
|
1028
|
+
type: "reasoning-start",
|
|
1029
|
+
conversationId: run.conversationId,
|
|
1030
|
+
runId: run.id,
|
|
1031
|
+
runtimeEpoch,
|
|
1032
|
+
sequence,
|
|
1033
|
+
...provider && { provider },
|
|
1034
|
+
emittedAt: now().toISOString()
|
|
1035
|
+
});
|
|
1105
1036
|
} else if (part.type === "reasoning-delta") {
|
|
1106
1037
|
updateReasoning(part.text, part.providerMetadata);
|
|
1038
|
+
const provider = providerEnvelope(part.providerMetadata);
|
|
1039
|
+
await publish({
|
|
1040
|
+
type: "reasoning-delta",
|
|
1041
|
+
conversationId: run.conversationId,
|
|
1042
|
+
runId: run.id,
|
|
1043
|
+
runtimeEpoch,
|
|
1044
|
+
sequence,
|
|
1045
|
+
textDelta: part.text,
|
|
1046
|
+
...provider && { provider },
|
|
1047
|
+
emittedAt: now().toISOString()
|
|
1048
|
+
});
|
|
1107
1049
|
} else if (part.type === "reasoning-end") {
|
|
1108
1050
|
updateReasoning("", part.providerMetadata);
|
|
1109
1051
|
reasoningPartIndex = undefined;
|
|
1052
|
+
const provider = providerEnvelope(part.providerMetadata);
|
|
1053
|
+
await publish({
|
|
1054
|
+
type: "reasoning-end",
|
|
1055
|
+
conversationId: run.conversationId,
|
|
1056
|
+
runId: run.id,
|
|
1057
|
+
runtimeEpoch,
|
|
1058
|
+
sequence,
|
|
1059
|
+
...provider && { provider },
|
|
1060
|
+
emittedAt: now().toISOString()
|
|
1061
|
+
});
|
|
1110
1062
|
} else if (part.type === "tool-call") {
|
|
1111
1063
|
const provider = providerEnvelope(part.providerMetadata);
|
|
1112
1064
|
parts.push(AgentMessagePartSchema.parse({
|
|
@@ -1329,6 +1281,12 @@ function createAgentRuntime(config) {
|
|
|
1329
1281
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1330
1282
|
}), "terminal commit");
|
|
1331
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
|
+
};
|
|
1332
1290
|
config.observe?.emit({
|
|
1333
1291
|
schemaVersion: 1,
|
|
1334
1292
|
eventId: generateId(),
|
|
@@ -1341,7 +1299,7 @@ function createAgentRuntime(config) {
|
|
|
1341
1299
|
state: run.state,
|
|
1342
1300
|
terminalReason,
|
|
1343
1301
|
...selectedModel && { modelId: selectedModel.descriptor.modelId },
|
|
1344
|
-
durationMs:
|
|
1302
|
+
durationMs: terminalMetrics.durationMs,
|
|
1345
1303
|
...usage && { usage },
|
|
1346
1304
|
...internalCause !== undefined && { internalCause },
|
|
1347
1305
|
...firstOutputAt !== undefined && { ttftMs: firstOutputAt - runStartedAt },
|
|
@@ -1356,6 +1314,7 @@ function createAgentRuntime(config) {
|
|
|
1356
1314
|
reason: terminalReason,
|
|
1357
1315
|
...terminalPolicyName && { policyName: terminalPolicyName },
|
|
1358
1316
|
message: assistant,
|
|
1317
|
+
metrics: terminalMetrics,
|
|
1359
1318
|
emittedAt: now().toISOString()
|
|
1360
1319
|
});
|
|
1361
1320
|
return {
|
|
@@ -1363,9 +1322,38 @@ function createAgentRuntime(config) {
|
|
|
1363
1322
|
message: assistant,
|
|
1364
1323
|
reason: terminalReason,
|
|
1365
1324
|
snapshotVersion: snapshot.version,
|
|
1325
|
+
metrics: terminalMetrics,
|
|
1366
1326
|
...terminalPolicyName && { policyName: terminalPolicyName }
|
|
1367
1327
|
};
|
|
1368
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
|
+
};
|
|
1369
1357
|
return {
|
|
1370
1358
|
submit(rawInput) {
|
|
1371
1359
|
const metadata = rawInput.metadata === undefined ? undefined : config.protocol.parseInputMetadata(rawInput.metadata);
|
|
@@ -1456,10 +1444,41 @@ function createAgentRuntime(config) {
|
|
|
1456
1444
|
const acceptedSnapshot = appliedSnapshot(acceptance, "input acceptance");
|
|
1457
1445
|
const assignedRunId = acceptance.outcome === "duplicate" ? acceptance.runId : reservation?.admission.runId ?? runId;
|
|
1458
1446
|
const acceptedRun = findRun(acceptedSnapshot.runs, assignedRunId);
|
|
1459
|
-
|
|
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,
|
|
1460
1464
|
runId: acceptedRun.id,
|
|
1461
|
-
assistantMessageId:
|
|
1465
|
+
assistantMessageId: assistantPlaceholder.id,
|
|
1466
|
+
input: acceptedInput,
|
|
1467
|
+
run: acceptedRun,
|
|
1468
|
+
assistant: acceptedAssistant,
|
|
1462
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()
|
|
1463
1482
|
});
|
|
1464
1483
|
await publish({
|
|
1465
1484
|
type: "run-state",
|
|
@@ -1548,34 +1567,7 @@ function createAgentRuntime(config) {
|
|
|
1548
1567
|
})();
|
|
1549
1568
|
return publicTicket;
|
|
1550
1569
|
},
|
|
1551
|
-
resume
|
|
1552
|
-
const context = config.protocol.parseContext(rawInput.context);
|
|
1553
|
-
const accepted = Promise.withResolvers();
|
|
1554
|
-
const result = Promise.withResolvers();
|
|
1555
|
-
(async () => {
|
|
1556
|
-
try {
|
|
1557
|
-
const snapshot = await config.store.loadSnapshot(rawInput.conversationId);
|
|
1558
|
-
const recoveredRun = findRun(snapshot.runs, rawInput.runId);
|
|
1559
|
-
if (recoveredRun.state !== "queued") {
|
|
1560
|
-
throw new Error("Only a queued recovered agent run can be resumed");
|
|
1561
|
-
}
|
|
1562
|
-
accepted.resolve();
|
|
1563
|
-
const ticket = coordinator.submit({
|
|
1564
|
-
key: rawInput.conversationKey ?? rawInput.conversationId,
|
|
1565
|
-
policy: "queue",
|
|
1566
|
-
create: (signal) => ({
|
|
1567
|
-
runId: recoveredRun.id,
|
|
1568
|
-
execute: () => executeRun({ acceptedRun: recoveredRun, context, signal })
|
|
1569
|
-
})
|
|
1570
|
-
});
|
|
1571
|
-
ticket.result.then(result.resolve, result.reject);
|
|
1572
|
-
} catch (error) {
|
|
1573
|
-
accepted.reject(error);
|
|
1574
|
-
result.reject(error);
|
|
1575
|
-
}
|
|
1576
|
-
})();
|
|
1577
|
-
return { accepted: accepted.promise, result: result.promise };
|
|
1578
|
-
},
|
|
1570
|
+
resume,
|
|
1579
1571
|
async interrupt(input) {
|
|
1580
1572
|
const snapshot = await config.store.loadSnapshot(input.conversationId);
|
|
1581
1573
|
const run = findRun(snapshot.runs, input.runId);
|
|
@@ -1599,101 +1591,298 @@ function createAgentRuntime(config) {
|
|
|
1599
1591
|
}
|
|
1600
1592
|
return requested;
|
|
1601
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
|
+
},
|
|
1602
1697
|
stop: (conversationKey, reason) => coordinator.stop(conversationKey, reason),
|
|
1603
1698
|
close: (options) => coordinator.close(options)
|
|
1604
1699
|
};
|
|
1605
1700
|
}
|
|
1606
1701
|
// src/agent-runtime/store.ts
|
|
1607
|
-
import { z as
|
|
1608
|
-
var AgentStoreConflictSchema =
|
|
1609
|
-
outcome:
|
|
1702
|
+
import { z as z6 } from "zod";
|
|
1703
|
+
var AgentStoreConflictSchema = z6.object({
|
|
1704
|
+
outcome: z6.literal("conflict"),
|
|
1610
1705
|
actualVersion: AgentRecordVersionSchema
|
|
1611
1706
|
});
|
|
1612
|
-
var AgentStoreNotFoundSchema =
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
var AgentStoreAppliedSchema = z7.object({
|
|
1616
|
-
outcome: z7.literal("applied"),
|
|
1707
|
+
var AgentStoreNotFoundSchema = z6.object({ outcome: z6.literal("not_found") });
|
|
1708
|
+
var AgentStoreAppliedSchema = z6.object({
|
|
1709
|
+
outcome: z6.literal("applied"),
|
|
1617
1710
|
snapshot: AgentSnapshotSchema
|
|
1618
1711
|
});
|
|
1619
|
-
var AgentStoreDuplicateSchema =
|
|
1620
|
-
outcome:
|
|
1712
|
+
var AgentStoreDuplicateSchema = z6.object({
|
|
1713
|
+
outcome: z6.literal("duplicate"),
|
|
1714
|
+
input: AgentMessageSchema,
|
|
1715
|
+
inputMessageId: AgentRecordIdSchema,
|
|
1621
1716
|
runId: AgentRecordIdSchema,
|
|
1717
|
+
assistantMessageId: AgentRecordIdSchema,
|
|
1622
1718
|
snapshot: AgentSnapshotSchema
|
|
1623
1719
|
});
|
|
1624
|
-
var AgentStoreMutationResultSchema =
|
|
1720
|
+
var AgentStoreMutationResultSchema = z6.discriminatedUnion("outcome", [
|
|
1625
1721
|
AgentStoreAppliedSchema,
|
|
1626
1722
|
AgentStoreDuplicateSchema,
|
|
1627
1723
|
AgentStoreConflictSchema,
|
|
1628
1724
|
AgentStoreNotFoundSchema
|
|
1629
1725
|
]);
|
|
1630
|
-
var AcceptInputAndAssignRunSchema =
|
|
1631
|
-
idempotencyKey:
|
|
1726
|
+
var AcceptInputAndAssignRunSchema = z6.object({
|
|
1727
|
+
idempotencyKey: z6.string().min(1),
|
|
1632
1728
|
expectedVersion: AgentRecordVersionSchema.optional(),
|
|
1633
1729
|
input: AgentMessageSchema,
|
|
1634
1730
|
run: AgentRunSchema,
|
|
1635
1731
|
coalesceIntoRunId: AgentRecordIdSchema.optional()
|
|
1636
1732
|
});
|
|
1637
|
-
var AcquireAgentRunSchema =
|
|
1733
|
+
var AcquireAgentRunSchema = z6.object({
|
|
1638
1734
|
conversationId: AgentRecordIdSchema,
|
|
1639
1735
|
runId: AgentRecordIdSchema,
|
|
1640
1736
|
expectedRevision: AgentRecordVersionSchema,
|
|
1641
|
-
ownerId:
|
|
1737
|
+
ownerId: z6.string().min(1)
|
|
1642
1738
|
});
|
|
1643
|
-
var CheckpointRunAssistantSchema =
|
|
1739
|
+
var CheckpointRunAssistantSchema = z6.object({
|
|
1644
1740
|
conversationId: AgentRecordIdSchema,
|
|
1645
1741
|
runId: AgentRecordIdSchema,
|
|
1646
1742
|
expectedRevision: AgentRecordVersionSchema,
|
|
1647
|
-
ownerId:
|
|
1743
|
+
ownerId: z6.string().min(1),
|
|
1648
1744
|
assistant: AgentMessageSchema
|
|
1649
1745
|
});
|
|
1650
|
-
var CommitRunTerminalSchema =
|
|
1746
|
+
var CommitRunTerminalSchema = z6.object({
|
|
1651
1747
|
conversationId: AgentRecordIdSchema,
|
|
1652
1748
|
runId: AgentRecordIdSchema,
|
|
1653
1749
|
expectedRevision: AgentRecordVersionSchema,
|
|
1654
|
-
ownerId:
|
|
1750
|
+
ownerId: z6.string().min(1),
|
|
1655
1751
|
assistant: AgentMessageSchema,
|
|
1656
1752
|
reason: AgentTerminalReasonSchema,
|
|
1657
|
-
policyName:
|
|
1753
|
+
policyName: z6.string().min(1).optional()
|
|
1658
1754
|
});
|
|
1659
|
-
var RequestRunInterruptSchema =
|
|
1755
|
+
var RequestRunInterruptSchema = z6.object({
|
|
1660
1756
|
conversationId: AgentRecordIdSchema,
|
|
1661
1757
|
runId: AgentRecordIdSchema,
|
|
1662
1758
|
expectedRevision: AgentRecordVersionSchema
|
|
1663
1759
|
});
|
|
1664
|
-
var RecoverAgentRunSchema =
|
|
1760
|
+
var RecoverAgentRunSchema = z6.object({
|
|
1665
1761
|
conversationId: AgentRecordIdSchema,
|
|
1666
1762
|
runId: AgentRecordIdSchema,
|
|
1667
1763
|
expectedRevision: AgentRecordVersionSchema,
|
|
1668
|
-
action:
|
|
1669
|
-
replaySafe:
|
|
1764
|
+
action: z6.enum(["requeue", "abandon"]),
|
|
1765
|
+
replaySafe: z6.boolean().optional()
|
|
1670
1766
|
});
|
|
1671
|
-
var ReplaceCompactedRangeSchema =
|
|
1767
|
+
var ReplaceCompactedRangeSchema = z6.object({
|
|
1672
1768
|
conversationId: AgentRecordIdSchema,
|
|
1673
1769
|
expectedVersion: AgentRecordVersionSchema,
|
|
1674
|
-
replacedMessageIds:
|
|
1770
|
+
replacedMessageIds: z6.array(AgentRecordIdSchema).min(1),
|
|
1675
1771
|
summary: AgentMessageSchema
|
|
1676
1772
|
});
|
|
1677
|
-
|
|
1678
|
-
|
|
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({
|
|
1679
1814
|
schemaVersion: 1,
|
|
1680
1815
|
conversationId,
|
|
1681
1816
|
version: 0,
|
|
1682
|
-
|
|
1683
|
-
|
|
1817
|
+
runs: [],
|
|
1818
|
+
admissions: []
|
|
1684
1819
|
});
|
|
1685
1820
|
}
|
|
1686
|
-
function
|
|
1687
|
-
|
|
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));
|
|
1688
1877
|
}
|
|
1689
1878
|
function replaceRun(runs, next) {
|
|
1690
1879
|
return runs.map((run) => run.id === next.id ? next : run);
|
|
1691
1880
|
}
|
|
1692
1881
|
function replaceMessage(messages, next) {
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
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 };
|
|
1697
1886
|
}
|
|
1698
1887
|
function terminalState(reason) {
|
|
1699
1888
|
if (reason === "success" || reason === "policy_stop")
|
|
@@ -1715,217 +1904,426 @@ function terminalMessageStatus(reason) {
|
|
|
1715
1904
|
}
|
|
1716
1905
|
return "failed";
|
|
1717
1906
|
}
|
|
1718
|
-
function
|
|
1719
|
-
const conversations = new Map;
|
|
1720
|
-
const get = (conversationId) => {
|
|
1721
|
-
const existing = conversations.get(conversationId);
|
|
1722
|
-
if (existing)
|
|
1723
|
-
return existing;
|
|
1724
|
-
const created = { snapshot: emptySnapshot(conversationId), idempotency: new Map };
|
|
1725
|
-
conversations.set(conversationId, created);
|
|
1726
|
-
return created;
|
|
1727
|
-
};
|
|
1728
|
-
const conflict = (actualVersion) => ({
|
|
1729
|
-
outcome: "conflict",
|
|
1730
|
-
actualVersion
|
|
1731
|
-
});
|
|
1732
|
-
const apply = (entry, snapshot) => {
|
|
1733
|
-
entry.snapshot = AgentSnapshotSchema.parse(snapshot);
|
|
1734
|
-
return { outcome: "applied", snapshot: cloneSnapshot(entry.snapshot) };
|
|
1735
|
-
};
|
|
1907
|
+
function applied(current, admissions, input, historyMutation) {
|
|
1736
1908
|
return {
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
if (input.coalesceIntoRunId !== undefined && (!coalescedRun || coalescedRun.conversationId !== input.input.conversationId || coalescedRun.state !== "queued" || coalescedRun.ownerId !== undefined || coalescedRun.terminalReason !== undefined)) {
|
|
1756
|
-
return coalescedRun ? conflict(coalescedRun.revision) : { outcome: "not_found" };
|
|
1757
|
-
}
|
|
1758
|
-
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))) {
|
|
1759
|
-
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");
|
|
1760
1927
|
}
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
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",
|
|
1765
2047
|
updatedAt: new Date().toISOString()
|
|
1766
|
-
}) : input.run;
|
|
1767
|
-
const next = AgentSnapshotSchema.parse({
|
|
1768
|
-
...entry.snapshot,
|
|
1769
|
-
version: entry.snapshot.version + 1,
|
|
1770
|
-
messages: [...entry.snapshot.messages, input.input],
|
|
1771
|
-
runs: coalescedRun ? replaceRun(entry.snapshot.runs, assignedRun) : [...entry.snapshot.runs, assignedRun]
|
|
1772
2048
|
});
|
|
1773
|
-
|
|
1774
|
-
|
|
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;
|
|
1775
2193
|
},
|
|
1776
|
-
async
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
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;
|
|
1791
2226
|
});
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
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();
|
|
1806
2246
|
}
|
|
1807
|
-
const nextRun = AgentRunSchema.parse({
|
|
1808
|
-
...run,
|
|
1809
|
-
revision: run.revision + 1,
|
|
1810
|
-
updatedAt: new Date().toISOString()
|
|
1811
|
-
});
|
|
1812
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1813
|
-
...entry.snapshot,
|
|
1814
|
-
version: entry.snapshot.version + 1,
|
|
1815
|
-
messages: replaceMessage(entry.snapshot.messages, input.assistant),
|
|
1816
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1817
|
-
}));
|
|
1818
2247
|
},
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
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" };
|
|
1827
2261
|
}
|
|
1828
|
-
const nextRun = AgentRunSchema.parse({
|
|
1829
|
-
...run,
|
|
1830
|
-
state: "interrupt_requested",
|
|
1831
|
-
revision: run.revision + 1,
|
|
1832
|
-
updatedAt: new Date().toISOString()
|
|
1833
|
-
});
|
|
1834
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1835
|
-
...entry.snapshot,
|
|
1836
|
-
version: entry.snapshot.version + 1,
|
|
1837
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1838
|
-
}));
|
|
1839
2262
|
},
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
return
|
|
1848
|
-
}
|
|
1849
|
-
|
|
1850
|
-
|
|
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
|
+
]);
|
|
1851
2299
|
}
|
|
1852
|
-
const nextRun = AgentRunSchema.parse({
|
|
1853
|
-
schemaVersion: 1,
|
|
1854
|
-
id: run.id,
|
|
1855
|
-
conversationId: run.conversationId,
|
|
1856
|
-
inputMessageIds: run.inputMessageIds,
|
|
1857
|
-
assistantMessageId: run.assistantMessageId,
|
|
1858
|
-
state: input.action === "requeue" ? "queued" : "abandoned",
|
|
1859
|
-
revision: run.revision + 1,
|
|
1860
|
-
...input.action === "abandon" && { terminalReason: "abandoned" },
|
|
1861
|
-
createdAt: run.createdAt,
|
|
1862
|
-
updatedAt: new Date().toISOString()
|
|
1863
|
-
});
|
|
1864
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1865
|
-
...entry.snapshot,
|
|
1866
|
-
version: entry.snapshot.version + 1,
|
|
1867
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1868
|
-
}));
|
|
1869
2300
|
},
|
|
1870
|
-
async
|
|
1871
|
-
const
|
|
1872
|
-
const
|
|
1873
|
-
const
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
...run,
|
|
1881
|
-
state: terminalState(input.reason),
|
|
1882
|
-
terminalReason: input.reason,
|
|
1883
|
-
...input.policyName && { terminalPolicyName: input.policyName },
|
|
1884
|
-
revision: run.revision + 1,
|
|
1885
|
-
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) }
|
|
1886
2311
|
});
|
|
1887
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1888
|
-
...entry.snapshot,
|
|
1889
|
-
version: entry.snapshot.version + 1,
|
|
1890
|
-
messages: replaceMessage(entry.snapshot.messages, input.assistant),
|
|
1891
|
-
runs: replaceRun(entry.snapshot.runs, nextRun)
|
|
1892
|
-
}));
|
|
1893
|
-
},
|
|
1894
|
-
async replaceCompactedRange(rawInput) {
|
|
1895
|
-
const input = ReplaceCompactedRangeSchema.parse(rawInput);
|
|
1896
|
-
const entry = get(input.conversationId);
|
|
1897
|
-
if (entry.snapshot.version !== input.expectedVersion) {
|
|
1898
|
-
return conflict(entry.snapshot.version);
|
|
1899
|
-
}
|
|
1900
|
-
const replaced = new Set(input.replacedMessageIds);
|
|
1901
|
-
if (!input.replacedMessageIds.every((id) => entry.snapshot.messages.some((m) => m.id === id))) {
|
|
1902
|
-
return { outcome: "not_found" };
|
|
1903
|
-
}
|
|
1904
|
-
const positions = entry.snapshot.messages.map((message, index) => replaced.has(message.id) ? index : undefined).filter((index) => index !== undefined);
|
|
1905
|
-
const first = positions[0];
|
|
1906
|
-
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") {
|
|
1907
|
-
throw new TypeError("Compaction replacement must be one valid contiguous history range");
|
|
1908
|
-
}
|
|
1909
|
-
const before = entry.snapshot.messages.slice(0, first);
|
|
1910
|
-
const after = entry.snapshot.messages.slice(first + positions.length);
|
|
1911
|
-
return apply(entry, AgentSnapshotSchema.parse({
|
|
1912
|
-
...entry.snapshot,
|
|
1913
|
-
version: entry.snapshot.version + 1,
|
|
1914
|
-
messages: [...before, input.summary, ...after]
|
|
1915
|
-
}));
|
|
1916
|
-
},
|
|
1917
|
-
async scanRecoverable() {
|
|
1918
|
-
return [...conversations.values()].filter((entry) => entry.snapshot.runs.some((run) => ["queued", "running", "interrupt_requested"].includes(run.state))).map((entry) => cloneSnapshot(entry.snapshot));
|
|
1919
2312
|
}
|
|
1920
2313
|
};
|
|
2314
|
+
return createAgentRuntimeStore(driver);
|
|
1921
2315
|
}
|
|
1922
2316
|
export {
|
|
1923
2317
|
AcceptInputAndAssignRunSchema,
|
|
1924
2318
|
AcquireAgentRunSchema,
|
|
2319
|
+
AgentAdmissionEventSchema,
|
|
2320
|
+
AgentAdmissionIdentitySchema,
|
|
2321
|
+
AgentAssistantPlaceholderSchema,
|
|
1925
2322
|
AgentCheckpointEventSchema,
|
|
1926
2323
|
AgentControlPartSchema,
|
|
1927
2324
|
AgentCostValueSchema,
|
|
1928
2325
|
AgentFilePartSchema,
|
|
2326
|
+
AgentHistoryMutationSchema,
|
|
1929
2327
|
AgentJsonObjectSchema,
|
|
1930
2328
|
AgentMessagePartSchema,
|
|
1931
2329
|
AgentMessageRoleSchema,
|
|
@@ -1935,10 +2333,16 @@ export {
|
|
|
1935
2333
|
AgentModelDescriptorSchema,
|
|
1936
2334
|
AgentOpaquePartSchema,
|
|
1937
2335
|
AgentProviderEnvelopeSchema,
|
|
2336
|
+
AgentReasoningDeltaEventSchema,
|
|
2337
|
+
AgentReasoningEndEventSchema,
|
|
1938
2338
|
AgentReasoningPartSchema,
|
|
2339
|
+
AgentReasoningStartEventSchema,
|
|
1939
2340
|
AgentRecordIdSchema,
|
|
1940
2341
|
AgentRecordVersionSchema,
|
|
2342
|
+
AgentRecoverableDescriptorSchema,
|
|
2343
|
+
AgentRecoverablePageSchema,
|
|
1941
2344
|
AgentRunEventSchema,
|
|
2345
|
+
AgentRunMetricsSchema,
|
|
1942
2346
|
AgentRunSchema,
|
|
1943
2347
|
AgentRunStateEventSchema,
|
|
1944
2348
|
AgentRunStateSchema,
|
|
@@ -1950,6 +2354,7 @@ export {
|
|
|
1950
2354
|
AgentStoreDuplicateSchema,
|
|
1951
2355
|
AgentStoreMutationResultSchema,
|
|
1952
2356
|
AgentStoreNotFoundSchema,
|
|
2357
|
+
AgentStoredStateSchema,
|
|
1953
2358
|
AgentTerminalEventSchema,
|
|
1954
2359
|
AgentTerminalReasonSchema,
|
|
1955
2360
|
AgentTextPartSchema,
|
|
@@ -1969,6 +2374,7 @@ export {
|
|
|
1969
2374
|
composeAgentPrompt,
|
|
1970
2375
|
createAgentObservability,
|
|
1971
2376
|
createAgentRuntime,
|
|
2377
|
+
createAgentRuntimeStore,
|
|
1972
2378
|
createAgentSessionCoordinator,
|
|
1973
2379
|
createAgentToolFenceLifecycle,
|
|
1974
2380
|
createMemoryAgentRuntimeStore,
|