@sentry/junior 0.144.0 → 0.145.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-hooks-GCUTGKY3.js → agent-hooks-L7D2BDVV.js} +10 -10
- package/dist/api/conversations/detail.d.ts +1 -0
- package/dist/api/schema/conversation.d.ts +2 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +40 -22
- package/dist/app.js +335 -409
- package/dist/{catalog-runtime-DWVYKDC6.js → catalog-runtime-QLAC4Q3W.js} +5 -5
- package/dist/chat/agent/request.d.ts +1 -1
- package/dist/chat/agent/resume.d.ts +6 -15
- package/dist/chat/agent/tools.d.ts +1 -1
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/app/conversation-work.d.ts +24 -0
- package/dist/chat/conversations/projection.d.ts +11 -3
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/runtime/agent-run-outcome.d.ts +1 -1
- package/dist/chat/runtime/reply-executor.d.ts +5 -5
- package/dist/chat/runtime/slack-resume.d.ts +12 -4
- package/dist/chat/state/locks.d.ts +28 -0
- package/dist/chat/task-execution/checkpoint.d.ts +86 -0
- package/dist/chat/task-execution/paused-turn.d.ts +29 -0
- package/dist/chat/task-execution/queue-signing.d.ts +1 -0
- package/dist/chat/task-execution/queue.d.ts +7 -1
- package/dist/chat/task-execution/slack-work.d.ts +1 -1
- package/dist/chat/task-execution/state.d.ts +4 -4
- package/dist/chat/task-execution/store.d.ts +2 -2
- package/dist/chat/task-execution/turn-cursor-keys.d.ts +6 -0
- package/dist/chat/{state/turn-session.d.ts → task-execution/turn-cursor.d.ts} +52 -42
- package/dist/chat/task-execution/turn-wake.d.ts +29 -0
- package/dist/chat/tools/types.d.ts +1 -1
- package/dist/{chunk-HNO2E5WB.js → chunk-27TEJTZO.js} +1 -1
- package/dist/{chunk-RIFJUJSI.js → chunk-2LWPFSB4.js} +1 -1
- package/dist/{chunk-SBZP2J5Q.js → chunk-2UH3JI64.js} +10 -9
- package/dist/{chunk-QUZJDAQQ.js → chunk-2XET6PKP.js} +93 -89
- package/dist/{chunk-SUK7NC5A.js → chunk-5PNPQYTO.js} +2 -2
- package/dist/{chunk-SSDTQDYQ.js → chunk-64SBYW2O.js} +132 -127
- package/dist/{chunk-DVO5VKYQ.js → chunk-B46ZMVOV.js} +5 -5
- package/dist/{chunk-A67GZN73.js → chunk-CD2XFGYQ.js} +1 -1
- package/dist/{chunk-WTX2RFJG.js → chunk-DS3J6WGY.js} +1 -1
- package/dist/{chunk-PKH6762I.js → chunk-DXEQAIUV.js} +1 -1
- package/dist/{chunk-WSOLZJJS.js → chunk-EIX2LQ6L.js} +3 -3
- package/dist/{chunk-KQBXT5B7.js → chunk-FUBRJA5F.js} +6 -6
- package/dist/{chunk-I44JB7HQ.js → chunk-GDV5LXRA.js} +1 -1
- package/dist/{chunk-7KAW7UF4.js → chunk-GUWLDMGF.js} +4 -4
- package/dist/{chunk-PELZ2ZSY.js → chunk-HISR3DTV.js} +33 -61
- package/dist/{chunk-HD6BHR25.js → chunk-HLOUZC5C.js} +32 -34
- package/dist/{chunk-BBCWYXHB.js → chunk-I47UGQPJ.js} +7 -7
- package/dist/{chunk-IUFM5JWV.js → chunk-JAL3CH4G.js} +3 -0
- package/dist/{chunk-E7HCWOHG.js → chunk-JLMVCNXL.js} +2 -1
- package/dist/{chunk-STFCL3K4.js → chunk-JTGS3ZZE.js} +1 -1
- package/dist/{chunk-GKA74DRA.js → chunk-LVVSIMBB.js} +1 -1
- package/dist/{chunk-SHEEZSLY.js → chunk-PJ4NMVQB.js} +2 -2
- package/dist/{chunk-W7JFV5SB.js → chunk-QJBDABQA.js} +14 -17
- package/dist/{chunk-UDQMY6RS.js → chunk-QUG4JSZK.js} +43 -1
- package/dist/{chunk-4X5KM7Z6.js → chunk-RUSS62EN.js} +14 -14
- package/dist/{chunk-ZF3N3JD6.js → chunk-SIN3MXH6.js} +1 -1
- package/dist/{chunk-QE3TICD4.js → chunk-TZRXYWRE.js} +2 -2
- package/dist/{chunk-CNTWB2SM.js → chunk-VTDMB7XX.js} +1 -1
- package/dist/{chunk-LDWNYCZ6.js → chunk-WF5HVH3M.js} +1 -1
- package/dist/{chunk-KCBZSJRU.js → chunk-WR76IVA5.js} +5 -2
- package/dist/{chunk-3GU4PWA3.js → chunk-XFKIRN3Q.js} +29 -29
- package/dist/{chunk-ZZCTGAIT.js → chunk-YD54Q45G.js} +4 -4
- package/dist/{chunk-BEGGFVFR.js → chunk-ZIAMN2UW.js} +407 -523
- package/dist/{chunk-44RJWAND.js → chunk-ZIMRGFYL.js} +6 -6
- package/dist/{chunk-54JMYQYS.js → chunk-ZYAJMESY.js} +2 -2
- package/dist/cli/chat.js +32 -32
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +13 -13
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/{db-EFLHTBU6.js → db-62PW6LM3.js} +7 -7
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -4
- package/dist/{oauth-callback-server-RO3IQKWM.js → oauth-callback-server-6NZNHJAM.js} +25 -25
- package/dist/{runner-LOWW7JKA.js → runner-GCRPG3GQ.js} +19 -18
- package/dist/{skills-ZLMUG7AE.js → skills-2S3MDFIQ.js} +6 -6
- package/dist/spawn-7WK37BTD.js +39 -0
- package/dist/{validation-MAKEZNTS.js → validation-HAOLCJ5A.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-5OAGC3I4.js → work-R4XYKMUW.js} +17 -16
- package/dist/worker-U2UCVR7M.js +24 -0
- package/package.json +4 -4
- package/dist/chat/agent/session.d.ts +0 -19
- package/dist/chat/runtime/agent-continue-runner.d.ts +0 -37
- package/dist/chat/services/agent-continue.d.ts +0 -29
- package/dist/chat/services/turn-session-record.d.ts +0 -164
- package/dist/chat/state/turn-session-keys.d.ts +0 -6
- package/dist/spawn-R3LUV6RF.js +0 -38
- package/dist/worker-KTGGRTTP.js +0 -24
|
@@ -21,18 +21,21 @@ import {
|
|
|
21
21
|
instructionProvenanceFor,
|
|
22
22
|
sanitizePostgresJson,
|
|
23
23
|
withConversationEventLock
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-2UH3JI64.js";
|
|
25
25
|
import {
|
|
26
26
|
SANDBOX_WORKSPACE_ROOT,
|
|
27
27
|
sleep
|
|
28
28
|
} from "./chunk-EM225ITJ.js";
|
|
29
29
|
import {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
MUTATION_LOCK_TTL_MS,
|
|
31
|
+
fenceLock,
|
|
32
|
+
getStateAdapter,
|
|
33
|
+
withLock
|
|
34
|
+
} from "./chunk-QUG4JSZK.js";
|
|
32
35
|
import {
|
|
33
36
|
botConfig,
|
|
34
37
|
unwrapCurrentInstruction
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-XFKIRN3Q.js";
|
|
36
39
|
import {
|
|
37
40
|
toStoredSlackActor
|
|
38
41
|
} from "./chunk-HZF5P5UN.js";
|
|
@@ -42,7 +45,7 @@ import {
|
|
|
42
45
|
logException,
|
|
43
46
|
toOptionalNumber,
|
|
44
47
|
toOptionalString
|
|
45
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-2LWPFSB4.js";
|
|
46
49
|
|
|
47
50
|
// src/chat/turn-context-tag.ts
|
|
48
51
|
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
@@ -517,7 +520,52 @@ function connectedMcpProvidersFromEvents(events) {
|
|
|
517
520
|
function isUserMessage(message) {
|
|
518
521
|
return message.role === "user";
|
|
519
522
|
}
|
|
520
|
-
|
|
523
|
+
var AgentHistoryBranchError = class extends Error {
|
|
524
|
+
constructor(conversationId) {
|
|
525
|
+
super(
|
|
526
|
+
`Agent history for ${conversationId} changed before its committed boundary`
|
|
527
|
+
);
|
|
528
|
+
this.name = "AgentHistoryBranchError";
|
|
529
|
+
}
|
|
530
|
+
};
|
|
531
|
+
function normalizeDurableMessage(message) {
|
|
532
|
+
return piMessageSchema.parse(
|
|
533
|
+
JSON.parse(JSON.stringify(sanitizePostgresJson(message)))
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
function durableMessageIdentity(message) {
|
|
537
|
+
const record = message;
|
|
538
|
+
if (record.role === "toolResult") {
|
|
539
|
+
return {
|
|
540
|
+
role: "toolResult",
|
|
541
|
+
timestamp: record.timestamp,
|
|
542
|
+
toolCallId: record.toolCallId,
|
|
543
|
+
toolName: record.toolName,
|
|
544
|
+
isError: record.isError,
|
|
545
|
+
content: record.content
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
role: record.role,
|
|
550
|
+
timestamp: record.timestamp,
|
|
551
|
+
content: record.content
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function countDurablePrefix(current, next) {
|
|
555
|
+
if (next.length < current.length) {
|
|
556
|
+
return countMatchingDeepPrefix(current, next);
|
|
557
|
+
}
|
|
558
|
+
for (let index = 0; index < current.length; index += 1) {
|
|
559
|
+
if (!isDeepStrictEqual3(
|
|
560
|
+
durableMessageIdentity(current[index]),
|
|
561
|
+
durableMessageIdentity(next[index])
|
|
562
|
+
)) {
|
|
563
|
+
return index;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return current.length;
|
|
567
|
+
}
|
|
568
|
+
function countMatchingDeepPrefix(left, right) {
|
|
521
569
|
const limit = Math.min(left.length, right.length);
|
|
522
570
|
for (let index = 0; index < limit; index += 1) {
|
|
523
571
|
if (!isDeepStrictEqual3(left[index], right[index])) {
|
|
@@ -526,11 +574,6 @@ function countMatchingPrefix(left, right) {
|
|
|
526
574
|
}
|
|
527
575
|
return limit;
|
|
528
576
|
}
|
|
529
|
-
function normalizeDurableMessage(message) {
|
|
530
|
-
return piMessageSchema.parse(
|
|
531
|
-
JSON.parse(JSON.stringify(sanitizePostgresJson(message)))
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
577
|
function resolveCommitProvenance(args) {
|
|
535
578
|
if (args.explicitProvenance) {
|
|
536
579
|
if (args.explicitProvenance.length !== args.nextMessages.length) {
|
|
@@ -672,7 +715,7 @@ async function commitMessagesLocked(args, executor) {
|
|
|
672
715
|
const nextLocalMessages = stripRuntimeTurnContext(args.messages).map(
|
|
673
716
|
normalizeDurableMessage
|
|
674
717
|
);
|
|
675
|
-
const matchingPrefix =
|
|
718
|
+
const matchingPrefix = countDurablePrefix(
|
|
676
719
|
current.messages,
|
|
677
720
|
nextLocalMessages
|
|
678
721
|
);
|
|
@@ -710,9 +753,7 @@ async function commitMessagesLocked(args, executor) {
|
|
|
710
753
|
...turnContextEvents
|
|
711
754
|
]);
|
|
712
755
|
} else {
|
|
713
|
-
throw new
|
|
714
|
-
`Agent history for ${args.conversationId} changed before its committed boundary`
|
|
715
|
-
);
|
|
756
|
+
throw new AgentHistoryBranchError(args.conversationId);
|
|
716
757
|
}
|
|
717
758
|
const committedEvents = await eventStore.loadCurrentHistory(
|
|
718
759
|
args.conversationId
|
|
@@ -1056,7 +1097,25 @@ function getAssistantReplyText(message) {
|
|
|
1056
1097
|
return decision.kind === "deliver" ? decision.text : void 0;
|
|
1057
1098
|
}
|
|
1058
1099
|
|
|
1059
|
-
// src/chat/
|
|
1100
|
+
// src/chat/services/persist-retry.ts
|
|
1101
|
+
var PERSIST_ATTEMPTS = 3;
|
|
1102
|
+
async function persistWithRetry(persist) {
|
|
1103
|
+
let lastError;
|
|
1104
|
+
for (let attempt = 1; attempt <= PERSIST_ATTEMPTS; attempt += 1) {
|
|
1105
|
+
try {
|
|
1106
|
+
await persist();
|
|
1107
|
+
return;
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
lastError = error;
|
|
1110
|
+
if (attempt < PERSIST_ATTEMPTS) {
|
|
1111
|
+
await sleep(attempt * 100);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
throw lastError;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
// src/chat/task-execution/turn-cursor.ts
|
|
1060
1119
|
import { z as z2 } from "zod";
|
|
1061
1120
|
|
|
1062
1121
|
// src/chat/repository-instructions.ts
|
|
@@ -1407,40 +1466,63 @@ function isAgentsInstructionsMessage(message) {
|
|
|
1407
1466
|
return Boolean(findVisibleAgentsInstructions([message]));
|
|
1408
1467
|
}
|
|
1409
1468
|
|
|
1410
|
-
// src/chat/
|
|
1411
|
-
var
|
|
1412
|
-
function
|
|
1413
|
-
return `${
|
|
1469
|
+
// src/chat/task-execution/turn-cursor-keys.ts
|
|
1470
|
+
var TURN_CURSOR_PREFIX = "junior:turn_cursor:v2";
|
|
1471
|
+
function turnCursorKey(conversationId, turnId) {
|
|
1472
|
+
return `${TURN_CURSOR_PREFIX}:${conversationId}:${turnId}`;
|
|
1414
1473
|
}
|
|
1415
|
-
function
|
|
1416
|
-
return `${
|
|
1474
|
+
function turnCursorIndexKey(conversationId) {
|
|
1475
|
+
return `${TURN_CURSOR_PREFIX}:conversation:${conversationId}:index`;
|
|
1476
|
+
}
|
|
1477
|
+
function turnCursorMutationKey(conversationId, turnId) {
|
|
1478
|
+
return `${TURN_CURSOR_PREFIX}:mutation:${conversationId}:${turnId}`;
|
|
1417
1479
|
}
|
|
1418
1480
|
|
|
1419
|
-
// src/chat/
|
|
1420
|
-
var
|
|
1421
|
-
var
|
|
1422
|
-
|
|
1423
|
-
|
|
1481
|
+
// src/chat/task-execution/turn-cursor.ts
|
|
1482
|
+
var TURN_CURSOR_SCHEMA_VERSION = 2;
|
|
1483
|
+
var TURN_CURSOR_RESUME_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
1484
|
+
var TURN_CURSOR_TERMINAL_TTL_MS = 60 * 60 * 1e3;
|
|
1485
|
+
var TURN_CURSOR_MUTATION_LOCK_WAIT_MS = 1e4;
|
|
1486
|
+
async function withTurnCursorMutation(conversationId, turnId, run) {
|
|
1487
|
+
const state = getStateAdapter();
|
|
1488
|
+
await state.connect();
|
|
1489
|
+
const result = await withLock(
|
|
1490
|
+
state,
|
|
1491
|
+
turnCursorMutationKey(conversationId, turnId),
|
|
1492
|
+
async (lock) => await run(async () => await fenceLock(state, lock)),
|
|
1493
|
+
{
|
|
1494
|
+
keepAlive: true,
|
|
1495
|
+
ttlMs: MUTATION_LOCK_TTL_MS,
|
|
1496
|
+
waitMs: TURN_CURSOR_MUTATION_LOCK_WAIT_MS
|
|
1497
|
+
}
|
|
1498
|
+
);
|
|
1499
|
+
if (!result.acquired) {
|
|
1500
|
+
throw new Error(`Could not acquire turn cursor lock for ${turnId}`);
|
|
1501
|
+
}
|
|
1502
|
+
return result.value;
|
|
1503
|
+
}
|
|
1504
|
+
function executionMetricsForTurn(conversation, turnId) {
|
|
1505
|
+
return conversation?.executionMetrics?.runId === turnId ? conversation.executionMetrics : void 0;
|
|
1424
1506
|
}
|
|
1425
1507
|
function definedProps(values) {
|
|
1426
1508
|
return Object.fromEntries(
|
|
1427
1509
|
Object.entries(values).filter(([, value]) => value !== void 0)
|
|
1428
1510
|
);
|
|
1429
1511
|
}
|
|
1430
|
-
var
|
|
1512
|
+
var turnStatusSchema = z2.enum([
|
|
1431
1513
|
"running",
|
|
1432
|
-
"
|
|
1514
|
+
"paused",
|
|
1433
1515
|
"completed",
|
|
1434
1516
|
"failed",
|
|
1435
1517
|
"abandoned"
|
|
1436
1518
|
]);
|
|
1437
|
-
var
|
|
1519
|
+
var turnSurfaceSchema = z2.enum([
|
|
1438
1520
|
"slack",
|
|
1439
1521
|
"api",
|
|
1440
1522
|
"scheduler",
|
|
1441
1523
|
"internal"
|
|
1442
1524
|
]);
|
|
1443
|
-
var
|
|
1525
|
+
var turnPauseReasonSchema = z2.enum([
|
|
1444
1526
|
"timeout",
|
|
1445
1527
|
"auth",
|
|
1446
1528
|
"yield",
|
|
@@ -1448,33 +1530,35 @@ var agentTurnResumeReasonSchema = z2.enum([
|
|
|
1448
1530
|
]);
|
|
1449
1531
|
var nonNegativeNumberSchema = z2.number().finite().nonnegative();
|
|
1450
1532
|
var seqCursorSchema = z2.number().int().min(-1);
|
|
1451
|
-
var
|
|
1533
|
+
var storedTurnSummarySchema = z2.object({
|
|
1534
|
+
schemaVersion: z2.literal(TURN_CURSOR_SCHEMA_VERSION),
|
|
1452
1535
|
version: z2.number().int().nonnegative(),
|
|
1453
1536
|
conversationId: z2.string().min(1),
|
|
1454
1537
|
dispatchId: z2.string().min(1).optional(),
|
|
1455
1538
|
dispatchOutcome: z2.enum(["blocked", "completed", "failed"]).optional(),
|
|
1456
1539
|
resultMessageId: z2.string().min(1).optional(),
|
|
1457
|
-
resumeReason:
|
|
1458
|
-
|
|
1540
|
+
resumeReason: turnPauseReasonSchema.optional(),
|
|
1541
|
+
turnId: z2.string().min(1),
|
|
1459
1542
|
sliceId: z2.number().int().nonnegative(),
|
|
1460
|
-
state:
|
|
1461
|
-
surface:
|
|
1543
|
+
state: turnStatusSchema,
|
|
1544
|
+
surface: turnSurfaceSchema.optional(),
|
|
1462
1545
|
updatedAtMs: nonNegativeNumberSchema
|
|
1463
1546
|
}).strip();
|
|
1464
|
-
var
|
|
1547
|
+
var storedTurnRecordSchema = z2.object({
|
|
1548
|
+
schemaVersion: z2.literal(TURN_CURSOR_SCHEMA_VERSION),
|
|
1465
1549
|
version: z2.number().int().nonnegative(),
|
|
1466
1550
|
conversationId: z2.string().min(1),
|
|
1467
1551
|
dispatchId: z2.string().min(1).optional(),
|
|
1468
1552
|
dispatchOutcome: z2.enum(["blocked", "completed", "failed"]).optional(),
|
|
1469
1553
|
resultMessageId: z2.string().min(1).optional(),
|
|
1470
1554
|
lastProgressAtMs: nonNegativeNumberSchema,
|
|
1471
|
-
resumeReason:
|
|
1555
|
+
resumeReason: turnPauseReasonSchema.optional(),
|
|
1472
1556
|
resumedFromSliceId: z2.number().int().nonnegative().optional(),
|
|
1473
|
-
|
|
1557
|
+
turnId: z2.string().min(1),
|
|
1474
1558
|
sliceId: z2.number().int().nonnegative(),
|
|
1475
1559
|
startedAtMs: nonNegativeNumberSchema,
|
|
1476
|
-
state:
|
|
1477
|
-
surface:
|
|
1560
|
+
state: turnStatusSchema,
|
|
1561
|
+
surface: turnSurfaceSchema.optional(),
|
|
1478
1562
|
traceId: z2.string().optional(),
|
|
1479
1563
|
updatedAtMs: nonNegativeNumberSchema,
|
|
1480
1564
|
committedSeq: seqCursorSchema,
|
|
@@ -1487,36 +1571,39 @@ function conversationExecutionFromSummary(summary) {
|
|
|
1487
1571
|
const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
|
|
1488
1572
|
return {
|
|
1489
1573
|
status,
|
|
1490
|
-
runId: summary.
|
|
1574
|
+
runId: summary.turnId,
|
|
1491
1575
|
updatedAtMs: summary.updatedAtMs
|
|
1492
1576
|
};
|
|
1493
1577
|
}
|
|
1494
1578
|
function sessionLogActor(actor) {
|
|
1495
1579
|
return actor?.platform === "slack" ? toStoredSlackActor(actor) : void 0;
|
|
1496
1580
|
}
|
|
1497
|
-
function
|
|
1498
|
-
return
|
|
1581
|
+
function parseTurnRecord(value) {
|
|
1582
|
+
return storedTurnRecordSchema.parse(value);
|
|
1499
1583
|
}
|
|
1500
|
-
function
|
|
1501
|
-
return
|
|
1584
|
+
function parseTurnSummary(value) {
|
|
1585
|
+
return storedTurnSummarySchema.parse(value);
|
|
1502
1586
|
}
|
|
1503
|
-
function
|
|
1587
|
+
function storeTurnSummary(summary) {
|
|
1588
|
+
return storedTurnSummarySchema.parse(summary);
|
|
1589
|
+
}
|
|
1590
|
+
function turnCursorTtlMs(state, ttlMs) {
|
|
1504
1591
|
if (ttlMs !== void 0) {
|
|
1505
1592
|
return Math.max(1, ttlMs);
|
|
1506
1593
|
}
|
|
1507
|
-
return state === "running" || state === "
|
|
1594
|
+
return state === "running" || state === "paused" ? TURN_CURSOR_RESUME_TTL_MS : TURN_CURSOR_TERMINAL_TTL_MS;
|
|
1508
1595
|
}
|
|
1509
|
-
function
|
|
1510
|
-
return Math.max(1, ttlMs ??
|
|
1596
|
+
function turnCursorIndexTtlMs(ttlMs) {
|
|
1597
|
+
return Math.max(1, ttlMs ?? TURN_CURSOR_RESUME_TTL_MS);
|
|
1511
1598
|
}
|
|
1512
|
-
function
|
|
1513
|
-
return
|
|
1599
|
+
function projectTurnSummary(record) {
|
|
1600
|
+
return parseTurnSummary(record);
|
|
1514
1601
|
}
|
|
1515
|
-
async function
|
|
1602
|
+
async function appendTurnSummary(summary, ttlMs) {
|
|
1516
1603
|
const stateAdapter = getStateAdapter();
|
|
1517
1604
|
await stateAdapter.appendToList(
|
|
1518
|
-
|
|
1519
|
-
|
|
1605
|
+
turnCursorIndexKey(summary.conversationId),
|
|
1606
|
+
storeTurnSummary(summary),
|
|
1520
1607
|
{ ttlMs }
|
|
1521
1608
|
);
|
|
1522
1609
|
}
|
|
@@ -1556,12 +1643,13 @@ async function recordConversationActivityMetadata(args) {
|
|
|
1556
1643
|
visibility: isChild ? void 0 : args.destinationVisibility
|
|
1557
1644
|
});
|
|
1558
1645
|
}
|
|
1559
|
-
function
|
|
1560
|
-
const restoredProjection = restoreVolatileContext && (stored.state === "running" || stored.state === "
|
|
1646
|
+
function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true, runtimeMetadata) {
|
|
1647
|
+
const restoredProjection = restoreVolatileContext && (stored.state === "running" || stored.state === "paused") ? restoreRuntimeContext(piProjection, stored.runtimeContext) : piProjection;
|
|
1561
1648
|
return {
|
|
1649
|
+
schemaVersion: stored.schemaVersion,
|
|
1562
1650
|
version: stored.version,
|
|
1563
1651
|
conversationId: stored.conversationId,
|
|
1564
|
-
|
|
1652
|
+
turnId: stored.turnId,
|
|
1565
1653
|
sliceId: stored.sliceId,
|
|
1566
1654
|
state: stored.state,
|
|
1567
1655
|
startedAtMs: stored.startedAtMs,
|
|
@@ -1625,19 +1713,14 @@ function restoreRuntimeContext(projection, runtimeContext) {
|
|
|
1625
1713
|
]
|
|
1626
1714
|
};
|
|
1627
1715
|
}
|
|
1628
|
-
async function
|
|
1716
|
+
async function getStoredTurnRecord(conversationId, turnId) {
|
|
1629
1717
|
const stateAdapter = getStateAdapter();
|
|
1630
1718
|
await stateAdapter.connect();
|
|
1631
|
-
const value = await stateAdapter.get(
|
|
1632
|
-
|
|
1633
|
-
);
|
|
1634
|
-
return value == null ? void 0 : parseAgentTurnSessionRecord(value);
|
|
1719
|
+
const value = await stateAdapter.get(turnCursorKey(conversationId, turnId));
|
|
1720
|
+
return value == null ? void 0 : parseTurnRecord(value);
|
|
1635
1721
|
}
|
|
1636
|
-
async function
|
|
1637
|
-
const parsed = await
|
|
1638
|
-
conversationId,
|
|
1639
|
-
sessionId
|
|
1640
|
-
);
|
|
1722
|
+
async function materializeStoredTurnRecord(conversationId, turnId, followCurrentReplacement) {
|
|
1723
|
+
const parsed = await getStoredTurnRecord(conversationId, turnId);
|
|
1641
1724
|
if (!parsed) {
|
|
1642
1725
|
return void 0;
|
|
1643
1726
|
}
|
|
@@ -1646,19 +1729,19 @@ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId
|
|
|
1646
1729
|
committedSeq: parsed.committedSeq,
|
|
1647
1730
|
// Unfinished records include the current-epoch tail so parked input
|
|
1648
1731
|
// appended after the last safe boundary stays model-visible on resume.
|
|
1649
|
-
includeTail: parsed.state === "running" || parsed.state === "
|
|
1732
|
+
includeTail: parsed.state === "running" || parsed.state === "paused"
|
|
1650
1733
|
});
|
|
1651
1734
|
if (!pinnedProjection) {
|
|
1652
1735
|
return void 0;
|
|
1653
1736
|
}
|
|
1654
1737
|
const currentHistory = await getConversationEventStore().loadCurrentHistory(conversationId);
|
|
1655
1738
|
const currentHistoryVersion = currentHistory.at(-1)?.historyVersion ?? parsed.historyVersion ?? 0;
|
|
1656
|
-
const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "
|
|
1739
|
+
const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "paused") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
|
|
1657
1740
|
const piProjection = followsReplacement ? projectConversationEvents(currentHistory) : pinnedProjection;
|
|
1658
1741
|
const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
|
|
1659
1742
|
const conversation = await getConversationStore().get({ conversationId });
|
|
1660
|
-
const executionMetrics =
|
|
1661
|
-
return
|
|
1743
|
+
const executionMetrics = executionMetricsForTurn(conversation, turnId);
|
|
1744
|
+
return materializeTurnRecord(
|
|
1662
1745
|
parsed,
|
|
1663
1746
|
piProjection,
|
|
1664
1747
|
turnStartMessageIndex,
|
|
@@ -1670,26 +1753,19 @@ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId
|
|
|
1670
1753
|
}
|
|
1671
1754
|
);
|
|
1672
1755
|
}
|
|
1673
|
-
async function
|
|
1674
|
-
return await
|
|
1675
|
-
conversationId,
|
|
1676
|
-
sessionId,
|
|
1677
|
-
false
|
|
1678
|
-
);
|
|
1756
|
+
async function getTurnRecord(conversationId, turnId) {
|
|
1757
|
+
return await materializeStoredTurnRecord(conversationId, turnId, false);
|
|
1679
1758
|
}
|
|
1680
|
-
async function
|
|
1681
|
-
return await
|
|
1682
|
-
conversationId,
|
|
1683
|
-
sessionId,
|
|
1684
|
-
true
|
|
1685
|
-
);
|
|
1759
|
+
async function getTurnRecordForResume(conversationId, turnId) {
|
|
1760
|
+
return await materializeStoredTurnRecord(conversationId, turnId, true);
|
|
1686
1761
|
}
|
|
1687
1762
|
function buildStoredRecord(args) {
|
|
1688
1763
|
const nowMs = Date.now();
|
|
1689
1764
|
return {
|
|
1765
|
+
schemaVersion: TURN_CURSOR_SCHEMA_VERSION,
|
|
1690
1766
|
version: (args.previousVersion ?? 0) + 1,
|
|
1691
1767
|
conversationId: args.conversationId,
|
|
1692
|
-
|
|
1768
|
+
turnId: args.turnId,
|
|
1693
1769
|
sliceId: args.sliceId,
|
|
1694
1770
|
state: args.state,
|
|
1695
1771
|
startedAtMs: args.startedAtMs ?? nowMs,
|
|
@@ -1714,7 +1790,8 @@ function buildStoredRecord(args) {
|
|
|
1714
1790
|
async function setStoredRecord(args) {
|
|
1715
1791
|
const stateAdapter = getStateAdapter();
|
|
1716
1792
|
await stateAdapter.connect();
|
|
1717
|
-
const storedRecord =
|
|
1793
|
+
const storedRecord = storedTurnRecordSchema.parse(args.record);
|
|
1794
|
+
await args.fence();
|
|
1718
1795
|
await recordConversationActivityMetadata({
|
|
1719
1796
|
actor: args.actor,
|
|
1720
1797
|
conversationStore: args.conversationStore,
|
|
@@ -1722,18 +1799,24 @@ async function setStoredRecord(args) {
|
|
|
1722
1799
|
destinationVisibility: args.destinationVisibility,
|
|
1723
1800
|
nowMs: Date.now(),
|
|
1724
1801
|
source: args.source,
|
|
1725
|
-
summary: {
|
|
1802
|
+
summary: {
|
|
1803
|
+
...projectTurnSummary(storedRecord),
|
|
1804
|
+
startedAtMs: storedRecord.startedAtMs,
|
|
1805
|
+
...args.runtimeMetadata
|
|
1806
|
+
}
|
|
1726
1807
|
});
|
|
1808
|
+
await args.fence();
|
|
1727
1809
|
await stateAdapter.set(
|
|
1728
|
-
|
|
1810
|
+
turnCursorKey(storedRecord.conversationId, storedRecord.turnId),
|
|
1729
1811
|
storedRecord,
|
|
1730
1812
|
args.ttlMs
|
|
1731
1813
|
);
|
|
1732
|
-
await
|
|
1733
|
-
|
|
1734
|
-
|
|
1814
|
+
await args.fence();
|
|
1815
|
+
await appendTurnSummary(
|
|
1816
|
+
projectTurnSummary(storedRecord),
|
|
1817
|
+
turnCursorIndexTtlMs(args.indexTtlMs)
|
|
1735
1818
|
);
|
|
1736
|
-
return
|
|
1819
|
+
return materializeTurnRecord(
|
|
1737
1820
|
storedRecord,
|
|
1738
1821
|
{
|
|
1739
1822
|
messages: [...args.piMessages],
|
|
@@ -1744,18 +1827,19 @@ async function setStoredRecord(args) {
|
|
|
1744
1827
|
args.runtimeMetadata
|
|
1745
1828
|
);
|
|
1746
1829
|
}
|
|
1747
|
-
async function
|
|
1748
|
-
const parsed = await
|
|
1830
|
+
async function updateTurnState(args) {
|
|
1831
|
+
const parsed = await getStoredTurnRecord(
|
|
1749
1832
|
args.existing.conversationId,
|
|
1750
|
-
args.existing.
|
|
1833
|
+
args.existing.turnId
|
|
1751
1834
|
);
|
|
1752
1835
|
if (!parsed || parsed.version !== args.existing.version) {
|
|
1753
1836
|
return void 0;
|
|
1754
1837
|
}
|
|
1755
1838
|
return await setStoredRecord({
|
|
1839
|
+
fence: args.fence,
|
|
1756
1840
|
piMessages: args.existing.piMessages,
|
|
1757
1841
|
piMessageProvenance: args.existing.piMessageProvenance,
|
|
1758
|
-
ttlMs:
|
|
1842
|
+
ttlMs: turnCursorTtlMs(args.state),
|
|
1759
1843
|
runtimeMetadata: {
|
|
1760
1844
|
cumulativeDurationMs: args.existing.cumulativeDurationMs,
|
|
1761
1845
|
...definedProps({
|
|
@@ -1771,7 +1855,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1771
1855
|
}),
|
|
1772
1856
|
record: buildStoredRecord({
|
|
1773
1857
|
conversationId: args.existing.conversationId,
|
|
1774
|
-
|
|
1858
|
+
turnId: args.existing.turnId,
|
|
1775
1859
|
sliceId: args.existing.sliceId,
|
|
1776
1860
|
state: args.state,
|
|
1777
1861
|
committedSeq: parsed.committedSeq,
|
|
@@ -1784,7 +1868,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1784
1868
|
errorMessage: args.errorMessage ?? args.existing.errorMessage,
|
|
1785
1869
|
historyVersion: parsed.historyVersion,
|
|
1786
1870
|
resumeReason: args.existing.resumeReason,
|
|
1787
|
-
resultMessageId: args.existing.resultMessageId,
|
|
1871
|
+
resultMessageId: args.resultMessageId ?? args.existing.resultMessageId,
|
|
1788
1872
|
resumedFromSliceId: args.existing.resumedFromSliceId,
|
|
1789
1873
|
surface: args.existing.surface,
|
|
1790
1874
|
traceId: args.existing.traceId,
|
|
@@ -1793,25 +1877,29 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1793
1877
|
})
|
|
1794
1878
|
});
|
|
1795
1879
|
}
|
|
1796
|
-
async function
|
|
1797
|
-
|
|
1880
|
+
async function upsertTurnRecord(args) {
|
|
1881
|
+
return await withTurnCursorMutation(
|
|
1798
1882
|
args.conversationId,
|
|
1799
|
-
args.
|
|
1883
|
+
args.turnId,
|
|
1884
|
+
async (fence) => await upsertTurnRecordLocked(args, fence)
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
async function upsertTurnRecordLocked(args, fence) {
|
|
1888
|
+
const existingRecord = await getStoredTurnRecord(
|
|
1889
|
+
args.conversationId,
|
|
1890
|
+
args.turnId
|
|
1800
1891
|
);
|
|
1801
1892
|
const conversation = await (args.conversationStore ?? getConversationStore()).get({
|
|
1802
1893
|
conversationId: args.conversationId
|
|
1803
1894
|
});
|
|
1804
|
-
const executionMetrics =
|
|
1805
|
-
|
|
1806
|
-
args.
|
|
1807
|
-
);
|
|
1808
|
-
const existingDispatchId = existingRecord?.dispatchId ?? (await listAgentTurnSessionSummariesForConversation(args.conversationId)).find((summary) => summary.sessionId === args.sessionId)?.dispatchId;
|
|
1895
|
+
const executionMetrics = executionMetricsForTurn(conversation, args.turnId);
|
|
1896
|
+
const existingDispatchId = existingRecord?.dispatchId ?? (await listTurnSummaries(args.conversationId)).find(
|
|
1897
|
+
(summary) => summary.turnId === args.turnId
|
|
1898
|
+
)?.dispatchId;
|
|
1809
1899
|
if (existingDispatchId && args.dispatchId && existingDispatchId !== args.dispatchId) {
|
|
1810
|
-
throw new Error(
|
|
1811
|
-
`Turn session ${args.sessionId} dispatchId cannot be changed`
|
|
1812
|
-
);
|
|
1900
|
+
throw new Error(`Turn ${args.turnId} dispatchId cannot be changed`);
|
|
1813
1901
|
}
|
|
1814
|
-
const ttlMs =
|
|
1902
|
+
const ttlMs = turnCursorTtlMs(args.state, args.ttlMs);
|
|
1815
1903
|
const instructionActor = args.actor;
|
|
1816
1904
|
const commit = await commitMessages({
|
|
1817
1905
|
conversationId: args.conversationId,
|
|
@@ -1821,7 +1909,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1821
1909
|
...args.turnContexts && args.turnContexts.length > 0 ? {
|
|
1822
1910
|
turnContext: {
|
|
1823
1911
|
contexts: args.turnContexts,
|
|
1824
|
-
turnId: args.
|
|
1912
|
+
turnId: args.turnId
|
|
1825
1913
|
}
|
|
1826
1914
|
} : {}
|
|
1827
1915
|
});
|
|
@@ -1834,6 +1922,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1834
1922
|
const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
|
|
1835
1923
|
return await setStoredRecord({
|
|
1836
1924
|
actor: args.actor,
|
|
1925
|
+
fence,
|
|
1837
1926
|
conversationStore: args.conversationStore,
|
|
1838
1927
|
destination: args.destination,
|
|
1839
1928
|
destinationVisibility: args.destinationVisibility,
|
|
@@ -1842,7 +1931,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1842
1931
|
piMessageProvenance: commit.provenance,
|
|
1843
1932
|
ttlMs,
|
|
1844
1933
|
// TODO(#1267): remove the temporary caller-to-SQL metadata write once the
|
|
1845
|
-
// SQL-only turn
|
|
1934
|
+
// SQL-only turn readers have completed rollout.
|
|
1846
1935
|
runtimeMetadata: {
|
|
1847
1936
|
channelName: args.channelName ?? conversation?.channelName,
|
|
1848
1937
|
cumulativeDurationMs: args.cumulativeDurationMs ?? executionMetrics?.durationMs ?? 0,
|
|
@@ -1857,7 +1946,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1857
1946
|
turnStartMessageIndex,
|
|
1858
1947
|
record: buildStoredRecord({
|
|
1859
1948
|
conversationId: args.conversationId,
|
|
1860
|
-
|
|
1949
|
+
turnId: args.turnId,
|
|
1861
1950
|
sliceId: args.sliceId,
|
|
1862
1951
|
state: args.state,
|
|
1863
1952
|
committedSeq: commit.committedSeq,
|
|
@@ -1871,7 +1960,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1871
1960
|
resumeReason: args.resumeReason,
|
|
1872
1961
|
resultMessageId: args.resultMessageId ?? existingRecord?.resultMessageId,
|
|
1873
1962
|
resumedFromSliceId: args.resumedFromSliceId,
|
|
1874
|
-
runtimeContext: args.state === "running" || args.state === "
|
|
1963
|
+
runtimeContext: args.state === "running" || args.state === "paused" ? retainedRuntimeContext : void 0,
|
|
1875
1964
|
startedAtMs: existingRecord?.startedAtMs,
|
|
1876
1965
|
surface: args.surface ?? existingRecord?.surface,
|
|
1877
1966
|
traceId: args.traceId ?? existingRecord?.traceId,
|
|
@@ -1880,35 +1969,42 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1880
1969
|
})
|
|
1881
1970
|
});
|
|
1882
1971
|
}
|
|
1883
|
-
async function
|
|
1884
|
-
|
|
1972
|
+
async function recordTurnSummary(args) {
|
|
1973
|
+
await withTurnCursorMutation(
|
|
1885
1974
|
args.conversationId,
|
|
1886
|
-
args.
|
|
1975
|
+
args.turnId,
|
|
1976
|
+
async (fence) => await recordTurnSummaryOwned(args, fence)
|
|
1887
1977
|
);
|
|
1978
|
+
}
|
|
1979
|
+
async function recordTurnSummaryOwned(args, fence) {
|
|
1980
|
+
const stored = await getStoredTurnRecord(args.conversationId, args.turnId);
|
|
1888
1981
|
const conversationStore = args.conversationStore ?? getConversationStore();
|
|
1889
1982
|
const conversation = await conversationStore.get({
|
|
1890
1983
|
conversationId: args.conversationId
|
|
1891
1984
|
});
|
|
1892
|
-
const executionMetrics =
|
|
1893
|
-
|
|
1894
|
-
args.
|
|
1985
|
+
const executionMetrics = executionMetricsForTurn(conversation, args.turnId);
|
|
1986
|
+
const priorSummary = (await listTurnSummaries(args.conversationId)).find(
|
|
1987
|
+
(summary2) => summary2.turnId === args.turnId
|
|
1895
1988
|
);
|
|
1896
|
-
const
|
|
1897
|
-
const
|
|
1989
|
+
const terminalSummary = priorSummary && (priorSummary.state === "completed" || priorSummary.state === "failed" || priorSummary.state === "abandoned") ? priorSummary : void 0;
|
|
1990
|
+
const terminalRecord = stored && (stored.state === "completed" || stored.state === "failed" || stored.state === "abandoned") ? stored : void 0;
|
|
1991
|
+
const existing = terminalSummary ?? terminalRecord ?? stored ?? priorSummary;
|
|
1992
|
+
if ((terminalSummary || terminalRecord) && (args.state === "running" || args.state === "paused")) {
|
|
1993
|
+
return;
|
|
1994
|
+
}
|
|
1898
1995
|
const existingDispatchId = existing?.dispatchId;
|
|
1899
1996
|
const existingDispatchOutcome = priorSummary?.dispatchOutcome ?? stored?.dispatchOutcome;
|
|
1900
1997
|
const existingResultMessageId = priorSummary?.resultMessageId ?? stored?.resultMessageId;
|
|
1901
1998
|
if (existingDispatchId && args.dispatchId && existingDispatchId !== args.dispatchId) {
|
|
1902
|
-
throw new Error(
|
|
1903
|
-
`Turn session ${args.sessionId} dispatchId cannot be changed`
|
|
1904
|
-
);
|
|
1999
|
+
throw new Error(`Turn ${args.turnId} dispatchId cannot be changed`);
|
|
1905
2000
|
}
|
|
1906
2001
|
const nowMs = Date.now();
|
|
1907
|
-
const ttlMs =
|
|
2002
|
+
const ttlMs = turnCursorIndexTtlMs(args.ttlMs);
|
|
1908
2003
|
const summary = {
|
|
2004
|
+
schemaVersion: TURN_CURSOR_SCHEMA_VERSION,
|
|
1909
2005
|
version: existing?.version ?? 0,
|
|
1910
2006
|
conversationId: args.conversationId,
|
|
1911
|
-
|
|
2007
|
+
turnId: args.turnId,
|
|
1912
2008
|
sliceId: args.sliceId,
|
|
1913
2009
|
state: args.state,
|
|
1914
2010
|
updatedAtMs: nowMs,
|
|
@@ -1920,6 +2016,7 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
1920
2016
|
surface: args.surface ?? existing?.surface
|
|
1921
2017
|
})
|
|
1922
2018
|
};
|
|
2019
|
+
await fence();
|
|
1923
2020
|
await recordConversationActivityMetadata({
|
|
1924
2021
|
actor: args.actor,
|
|
1925
2022
|
conversationStore,
|
|
@@ -1935,9 +2032,10 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
1935
2032
|
startedAtMs: stored?.startedAtMs ?? args.startedAtMs ?? nowMs
|
|
1936
2033
|
}
|
|
1937
2034
|
});
|
|
1938
|
-
await
|
|
2035
|
+
await fence();
|
|
2036
|
+
await appendTurnSummary(summary, ttlMs);
|
|
1939
2037
|
}
|
|
1940
|
-
async function
|
|
2038
|
+
async function readTurnSummaries(key) {
|
|
1941
2039
|
const stateAdapter = getStateAdapter();
|
|
1942
2040
|
await stateAdapter.connect();
|
|
1943
2041
|
const values = await stateAdapter.getList(key);
|
|
@@ -1945,11 +2043,11 @@ async function readAgentTurnSessionSummariesFromIndex(key) {
|
|
|
1945
2043
|
for (const value of [...values].reverse()) {
|
|
1946
2044
|
let summary;
|
|
1947
2045
|
try {
|
|
1948
|
-
summary =
|
|
2046
|
+
summary = parseTurnSummary(value);
|
|
1949
2047
|
} catch {
|
|
1950
2048
|
continue;
|
|
1951
2049
|
}
|
|
1952
|
-
const summaryKey = `${summary.conversationId}:${summary.
|
|
2050
|
+
const summaryKey = `${summary.conversationId}:${summary.turnId}`;
|
|
1953
2051
|
if (!summaries.has(summaryKey)) {
|
|
1954
2052
|
summaries.set(summaryKey, summary);
|
|
1955
2053
|
}
|
|
@@ -1958,56 +2056,62 @@ async function readAgentTurnSessionSummariesFromIndex(key) {
|
|
|
1958
2056
|
(left, right) => right.updatedAtMs - left.updatedAtMs
|
|
1959
2057
|
);
|
|
1960
2058
|
}
|
|
1961
|
-
async function
|
|
1962
|
-
return
|
|
1963
|
-
agentTurnSessionConversationIndexKey(conversationId)
|
|
1964
|
-
);
|
|
2059
|
+
async function listTurnSummaries(conversationId) {
|
|
2060
|
+
return readTurnSummaries(turnCursorIndexKey(conversationId));
|
|
1965
2061
|
}
|
|
1966
|
-
async function
|
|
1967
|
-
|
|
2062
|
+
async function abandonTurnRecord(args) {
|
|
2063
|
+
return await withTurnCursorMutation(
|
|
1968
2064
|
args.conversationId,
|
|
1969
|
-
args.
|
|
2065
|
+
args.turnId,
|
|
2066
|
+
async (fence) => {
|
|
2067
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2068
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned") {
|
|
2069
|
+
return void 0;
|
|
2070
|
+
}
|
|
2071
|
+
return await updateTurnState({
|
|
2072
|
+
existing,
|
|
2073
|
+
fence,
|
|
2074
|
+
state: "abandoned",
|
|
2075
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
1970
2078
|
);
|
|
1971
|
-
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned") {
|
|
1972
|
-
return void 0;
|
|
1973
|
-
}
|
|
1974
|
-
return await updateAgentTurnSessionState({
|
|
1975
|
-
existing,
|
|
1976
|
-
state: "abandoned",
|
|
1977
|
-
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
1978
|
-
});
|
|
1979
2079
|
}
|
|
1980
|
-
async function
|
|
1981
|
-
|
|
2080
|
+
async function completeTurnRecord(args) {
|
|
2081
|
+
return await withTurnCursorMutation(
|
|
1982
2082
|
args.conversationId,
|
|
1983
|
-
args.
|
|
2083
|
+
args.turnId,
|
|
2084
|
+
async (fence) => {
|
|
2085
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2086
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
2087
|
+
return void 0;
|
|
2088
|
+
}
|
|
2089
|
+
return await updateTurnState({
|
|
2090
|
+
existing,
|
|
2091
|
+
fence,
|
|
2092
|
+
resultMessageId: args.resultMessageId,
|
|
2093
|
+
state: "completed"
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
1984
2096
|
);
|
|
1985
|
-
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
1986
|
-
return void 0;
|
|
1987
|
-
}
|
|
1988
|
-
return await updateAgentTurnSessionState({
|
|
1989
|
-
existing,
|
|
1990
|
-
state: "failed",
|
|
1991
|
-
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
1992
|
-
});
|
|
1993
2097
|
}
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
return;
|
|
2003
|
-
} catch (error) {
|
|
2004
|
-
lastError = error;
|
|
2005
|
-
if (attempt < PERSIST_ATTEMPTS) {
|
|
2006
|
-
await sleep(attempt * 100);
|
|
2098
|
+
async function failTurnRecord(args) {
|
|
2099
|
+
return await withTurnCursorMutation(
|
|
2100
|
+
args.conversationId,
|
|
2101
|
+
args.turnId,
|
|
2102
|
+
async (fence) => {
|
|
2103
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2104
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
2105
|
+
return void 0;
|
|
2007
2106
|
}
|
|
2107
|
+
return await updateTurnState({
|
|
2108
|
+
existing,
|
|
2109
|
+
fence,
|
|
2110
|
+
state: "failed",
|
|
2111
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
2112
|
+
});
|
|
2008
2113
|
}
|
|
2009
|
-
|
|
2010
|
-
throw lastError;
|
|
2114
|
+
);
|
|
2011
2115
|
}
|
|
2012
2116
|
|
|
2013
2117
|
// src/chat/services/turn-limit.ts
|
|
@@ -2021,25 +2125,56 @@ function buildTurnLimitResponse(eventId) {
|
|
|
2021
2125
|
return `I couldn't finish this request because this turn reached its execution limit. Please try again with a smaller or more specific request. Reference: \`event_id=${eventId}\`.`;
|
|
2022
2126
|
}
|
|
2023
2127
|
|
|
2024
|
-
// src/chat/
|
|
2128
|
+
// src/chat/task-execution/checkpoint.ts
|
|
2025
2129
|
function definedProps2(values) {
|
|
2026
2130
|
return Object.fromEntries(
|
|
2027
2131
|
Object.entries(values).filter(([, value]) => value !== void 0)
|
|
2028
2132
|
);
|
|
2029
2133
|
}
|
|
2030
|
-
function
|
|
2134
|
+
function addDurationMs(prior, current) {
|
|
2135
|
+
const total = [prior, current].reduce((sum, value) => {
|
|
2136
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2137
|
+
return sum;
|
|
2138
|
+
}
|
|
2139
|
+
return (sum ?? 0) + Math.max(0, Math.floor(value));
|
|
2140
|
+
}, void 0);
|
|
2141
|
+
return total;
|
|
2142
|
+
}
|
|
2143
|
+
function continuableMessages(messages, fallback) {
|
|
2144
|
+
const current = trimTrailingAssistantMessages(messages);
|
|
2145
|
+
if (current.length > 0 && isContinuablePiBoundary(current)) {
|
|
2146
|
+
return current;
|
|
2147
|
+
}
|
|
2148
|
+
return trimTrailingAssistantMessages(fallback ?? []);
|
|
2149
|
+
}
|
|
2150
|
+
async function loadTurnCheckpoint(args) {
|
|
2151
|
+
const record = await getTurnRecordForResume(args.conversationId, args.turnId);
|
|
2152
|
+
if (record?.state === "paused") {
|
|
2153
|
+
return { resumed: true, sliceId: record.sliceId, record };
|
|
2154
|
+
}
|
|
2155
|
+
return { resumed: false, sliceId: 1, record };
|
|
2156
|
+
}
|
|
2157
|
+
async function saveTurnCheckpoint(args) {
|
|
2158
|
+
if (args.mode === "running") {
|
|
2159
|
+
return await saveRunning(args);
|
|
2160
|
+
}
|
|
2161
|
+
if (args.mode === "paused") {
|
|
2162
|
+
return await savePaused(args);
|
|
2163
|
+
}
|
|
2164
|
+
await saveDone(args);
|
|
2165
|
+
return void 0;
|
|
2166
|
+
}
|
|
2167
|
+
function sharedWrite(args, latest) {
|
|
2031
2168
|
return {
|
|
2032
2169
|
conversationId: args.conversationId,
|
|
2033
2170
|
modelId: args.modelId,
|
|
2034
|
-
|
|
2171
|
+
turnId: args.turnId,
|
|
2035
2172
|
...definedProps2({
|
|
2036
|
-
// Live only: Redis no longer stores execution actor across resumes.
|
|
2037
2173
|
actor: args.actor,
|
|
2038
2174
|
channelName: args.channelName ?? latest?.channelName,
|
|
2039
2175
|
destination: args.destination,
|
|
2040
2176
|
destinationVisibility: args.destinationVisibility,
|
|
2041
2177
|
dispatchId: args.dispatchId ?? latest?.dispatchId,
|
|
2042
|
-
// Caller-resolved: some paths inherit these, others stay caller-only.
|
|
2043
2178
|
loadedSkillNames: args.loadedSkillNames,
|
|
2044
2179
|
reasoningLevel: args.reasoningLevel,
|
|
2045
2180
|
source: args.source,
|
|
@@ -2049,372 +2184,124 @@ function sessionWriteContext(args, latest) {
|
|
|
2049
2184
|
})
|
|
2050
2185
|
};
|
|
2051
2186
|
}
|
|
2052
|
-
function
|
|
2053
|
-
logException(error, eventName, {
|
|
2054
|
-
"app.ai.resume_conversation_id": args.conversationId,
|
|
2055
|
-
"app.ai.resume_session_id": args.sessionId,
|
|
2056
|
-
...attributes
|
|
2057
|
-
});
|
|
2058
|
-
}
|
|
2059
|
-
function addDurationMs(prior, current) {
|
|
2060
|
-
const total = [prior, current].reduce((sum, value) => {
|
|
2061
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
2062
|
-
return sum;
|
|
2063
|
-
}
|
|
2064
|
-
return (sum ?? 0) + Math.max(0, Math.floor(value));
|
|
2065
|
-
}, void 0);
|
|
2066
|
-
return total;
|
|
2067
|
-
}
|
|
2068
|
-
function resumableBoundary(messages, fallbackMessages) {
|
|
2069
|
-
const current = trimTrailingAssistantMessages(messages);
|
|
2070
|
-
if (current.length > 0 && isContinuablePiBoundary(current)) {
|
|
2071
|
-
return current;
|
|
2072
|
-
}
|
|
2073
|
-
return trimTrailingAssistantMessages(fallbackMessages ?? []);
|
|
2074
|
-
}
|
|
2075
|
-
async function loadTurnSessionRecord(ctx) {
|
|
2076
|
-
const existingSessionRecord = await getAgentTurnSessionRecordForResume(
|
|
2077
|
-
ctx.conversationId,
|
|
2078
|
-
ctx.sessionId
|
|
2079
|
-
);
|
|
2080
|
-
const hasAwaitingResumeRecord = Boolean(
|
|
2081
|
-
existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
|
|
2082
|
-
);
|
|
2083
|
-
return {
|
|
2084
|
-
resumedFromSessionRecord: hasAwaitingResumeRecord,
|
|
2085
|
-
currentSliceId: hasAwaitingResumeRecord ? existingSessionRecord.sliceId : 1,
|
|
2086
|
-
existingSessionRecord
|
|
2087
|
-
};
|
|
2088
|
-
}
|
|
2089
|
-
async function persistRunningSessionRecord(args) {
|
|
2187
|
+
async function saveRunning(args) {
|
|
2090
2188
|
if (args.messages.length === 0 || !isContinuablePiBoundary(args.messages)) {
|
|
2091
|
-
return
|
|
2189
|
+
return void 0;
|
|
2092
2190
|
}
|
|
2093
2191
|
try {
|
|
2094
|
-
const
|
|
2095
|
-
|
|
2096
|
-
args
|
|
2097
|
-
);
|
|
2098
|
-
await upsertAgentTurnSessionRecord({
|
|
2099
|
-
...sessionWriteContext(
|
|
2100
|
-
{
|
|
2101
|
-
actor: args.actor,
|
|
2102
|
-
channelName: args.channelName,
|
|
2103
|
-
conversationId: args.conversationId,
|
|
2104
|
-
destination: args.destination,
|
|
2105
|
-
destinationVisibility: args.destinationVisibility,
|
|
2106
|
-
dispatchId: args.dispatchId,
|
|
2107
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2108
|
-
modelId: args.modelId,
|
|
2109
|
-
reasoningLevel: args.reasoningLevel,
|
|
2110
|
-
sessionId: args.sessionId,
|
|
2111
|
-
source: args.source,
|
|
2112
|
-
surface: args.surface,
|
|
2113
|
-
turnStartMessageIndex: args.turnStartMessageIndex
|
|
2114
|
-
},
|
|
2115
|
-
latestSessionRecord
|
|
2116
|
-
),
|
|
2192
|
+
const latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2193
|
+
return await upsertTurnRecord({
|
|
2194
|
+
...sharedWrite(args, latest),
|
|
2117
2195
|
...definedProps2({
|
|
2118
2196
|
trailingMessageProvenance: args.trailingMessageProvenance,
|
|
2119
2197
|
turnContexts: args.turnContexts
|
|
2120
2198
|
}),
|
|
2121
|
-
cumulativeDurationMs:
|
|
2122
|
-
cumulativeUsage:
|
|
2199
|
+
cumulativeDurationMs: latest?.cumulativeDurationMs,
|
|
2200
|
+
cumulativeUsage: latest?.cumulativeUsage,
|
|
2123
2201
|
piMessages: args.messages,
|
|
2124
2202
|
sliceId: args.sliceId,
|
|
2125
2203
|
state: "running"
|
|
2126
2204
|
});
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
args,
|
|
2133
|
-
{
|
|
2205
|
+
} catch (error) {
|
|
2206
|
+
if (!(error instanceof AgentHistoryBranchError)) {
|
|
2207
|
+
logException(error, "agent.turn.checkpoint.running.failed", {
|
|
2208
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
2209
|
+
"app.ai.resume_session_id": args.turnId,
|
|
2134
2210
|
"app.ai.resume_slice_id": args.sliceId
|
|
2135
|
-
}
|
|
2136
|
-
|
|
2137
|
-
return
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
async function persistCompletedSessionRecord(args) {
|
|
2141
|
-
let latestSessionRecord;
|
|
2142
|
-
await persistWithRetry(async () => {
|
|
2143
|
-
latestSessionRecord = await getAgentTurnSessionRecord(
|
|
2144
|
-
args.conversationId,
|
|
2145
|
-
args.sessionId
|
|
2146
|
-
);
|
|
2147
|
-
});
|
|
2148
|
-
const sliceId = args.sliceId ?? latestSessionRecord?.sliceId;
|
|
2149
|
-
if (sliceId === void 0) {
|
|
2150
|
-
throw new Error(
|
|
2151
|
-
"Completed session record requires a slice id from the caller or the latest stored record"
|
|
2152
|
-
);
|
|
2211
|
+
});
|
|
2212
|
+
}
|
|
2213
|
+
return void 0;
|
|
2153
2214
|
}
|
|
2154
|
-
const target = {
|
|
2155
|
-
...sessionWriteContext(
|
|
2156
|
-
{
|
|
2157
|
-
actor: args.actor,
|
|
2158
|
-
channelName: args.channelName,
|
|
2159
|
-
conversationId: args.conversationId,
|
|
2160
|
-
destination: args.destination,
|
|
2161
|
-
destinationVisibility: args.destinationVisibility,
|
|
2162
|
-
dispatchId: args.dispatchId,
|
|
2163
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2164
|
-
modelId: args.modelId,
|
|
2165
|
-
reasoningLevel: args.reasoningLevel,
|
|
2166
|
-
sessionId: args.sessionId,
|
|
2167
|
-
source: args.source,
|
|
2168
|
-
surface: args.surface,
|
|
2169
|
-
turnStartMessageIndex: args.turnStartMessageIndex
|
|
2170
|
-
},
|
|
2171
|
-
latestSessionRecord
|
|
2172
|
-
),
|
|
2173
|
-
...definedProps2({
|
|
2174
|
-
cumulativeDurationMs: addDurationMs(
|
|
2175
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
2176
|
-
args.currentDurationMs
|
|
2177
|
-
),
|
|
2178
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
2179
|
-
latestSessionRecord?.cumulativeUsage,
|
|
2180
|
-
args.currentUsage
|
|
2181
|
-
),
|
|
2182
|
-
dispatchOutcome: args.dispatchOutcome ?? latestSessionRecord?.dispatchOutcome,
|
|
2183
|
-
errorMessage: args.errorMessage,
|
|
2184
|
-
resultMessageId: args.resultMessageId ?? latestSessionRecord?.resultMessageId
|
|
2185
|
-
}),
|
|
2186
|
-
piMessages: args.allMessages,
|
|
2187
|
-
sliceId,
|
|
2188
|
-
state: "completed"
|
|
2189
|
-
};
|
|
2190
|
-
await persistWithRetry(async () => {
|
|
2191
|
-
await upsertAgentTurnSessionRecord(target);
|
|
2192
|
-
});
|
|
2193
2215
|
}
|
|
2194
|
-
async function
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
conversationId: args.conversationId,
|
|
2198
|
-
currentDurationMs: args.durationMs,
|
|
2199
|
-
currentUsage: args.usage,
|
|
2200
|
-
destination: args.destination,
|
|
2201
|
-
destinationVisibility: args.destinationVisibility,
|
|
2202
|
-
dispatchId: args.dispatchId,
|
|
2203
|
-
dispatchOutcome: args.dispatchOutcome,
|
|
2204
|
-
errorMessage: args.errorMessage,
|
|
2205
|
-
resultMessageId: args.resultMessageId,
|
|
2206
|
-
source: args.source,
|
|
2207
|
-
sessionId: args.sessionId,
|
|
2208
|
-
sliceId: args.sliceId,
|
|
2209
|
-
allMessages: args.messages,
|
|
2210
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2211
|
-
modelId: args.modelId,
|
|
2212
|
-
actor: args.actor,
|
|
2213
|
-
reasoningLevel: args.reasoningLevel,
|
|
2214
|
-
surface: args.surface,
|
|
2215
|
-
turnStartMessageIndex: args.turnStartMessageIndex
|
|
2216
|
-
});
|
|
2217
|
-
}
|
|
2218
|
-
async function persistAuthPauseSessionRecord(args) {
|
|
2219
|
-
const nextSliceId = args.currentSliceId + 1;
|
|
2216
|
+
async function savePaused(args) {
|
|
2217
|
+
const keepSlice = args.reason === "yield";
|
|
2218
|
+
const nextSliceId = keepSlice ? args.sliceId : args.sliceId + 1;
|
|
2220
2219
|
try {
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
);
|
|
2229
|
-
if (piMessages.length > 0 && !isContinuablePiBoundary(piMessages)) {
|
|
2220
|
+
const latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2221
|
+
const messages = args.reason === "yield" ? [...args.messages] : continuableMessages(args.messages, latest?.piMessages);
|
|
2222
|
+
if (args.reason === "auth") {
|
|
2223
|
+
if (messages.length > 0 && !isContinuablePiBoundary(messages)) {
|
|
2224
|
+
return void 0;
|
|
2225
|
+
}
|
|
2226
|
+
} else if (messages.length === 0 || !isContinuablePiBoundary(messages)) {
|
|
2230
2227
|
return void 0;
|
|
2231
2228
|
}
|
|
2232
|
-
|
|
2233
|
-
...
|
|
2234
|
-
{
|
|
2235
|
-
actor: args.actor,
|
|
2236
|
-
channelName: args.channelName,
|
|
2237
|
-
conversationId: args.conversationId,
|
|
2238
|
-
destination: args.destination,
|
|
2239
|
-
destinationVisibility: args.destinationVisibility,
|
|
2240
|
-
dispatchId: args.dispatchId,
|
|
2241
|
-
// Auth-pause keeps caller-owned skill names only.
|
|
2242
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2243
|
-
modelId: args.modelId,
|
|
2244
|
-
reasoningLevel: args.reasoningLevel,
|
|
2245
|
-
sessionId: args.sessionId,
|
|
2246
|
-
source: args.source,
|
|
2247
|
-
surface: args.surface
|
|
2248
|
-
},
|
|
2249
|
-
latestSessionRecord
|
|
2250
|
-
),
|
|
2229
|
+
const base = {
|
|
2230
|
+
...sharedWrite(args, latest),
|
|
2251
2231
|
...definedProps2({
|
|
2252
2232
|
cumulativeDurationMs: addDurationMs(
|
|
2253
|
-
|
|
2254
|
-
args.
|
|
2233
|
+
latest?.cumulativeDurationMs,
|
|
2234
|
+
args.durationMs
|
|
2255
2235
|
),
|
|
2256
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
2257
|
-
|
|
2258
|
-
args.currentUsage
|
|
2259
|
-
)
|
|
2260
|
-
}),
|
|
2261
|
-
errorMessage: args.errorMessage,
|
|
2262
|
-
piMessages,
|
|
2263
|
-
resumeReason: "auth",
|
|
2264
|
-
resumedFromSliceId: args.currentSliceId,
|
|
2265
|
-
sliceId: nextSliceId,
|
|
2266
|
-
state: "awaiting_resume"
|
|
2267
|
-
});
|
|
2268
|
-
} catch (recordError) {
|
|
2269
|
-
logSessionRecordError(
|
|
2270
|
-
recordError,
|
|
2271
|
-
"agent.turn.auth_resume_session_record.failed",
|
|
2272
|
-
args,
|
|
2273
|
-
{
|
|
2274
|
-
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
2275
|
-
"app.ai.resume_next_slice_id": nextSliceId
|
|
2276
|
-
}
|
|
2277
|
-
);
|
|
2278
|
-
}
|
|
2279
|
-
return void 0;
|
|
2280
|
-
}
|
|
2281
|
-
async function persistContinuationSessionRecord(args) {
|
|
2282
|
-
const nextSliceId = args.currentSliceId + 1;
|
|
2283
|
-
try {
|
|
2284
|
-
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
2285
|
-
args.conversationId,
|
|
2286
|
-
args.sessionId
|
|
2287
|
-
);
|
|
2288
|
-
const piMessages = resumableBoundary(
|
|
2289
|
-
args.messages,
|
|
2290
|
-
latestSessionRecord?.piMessages
|
|
2291
|
-
);
|
|
2292
|
-
if (piMessages.length === 0 || !isContinuablePiBoundary(piMessages)) {
|
|
2293
|
-
return void 0;
|
|
2294
|
-
}
|
|
2295
|
-
const cumulativeDurationMs = addDurationMs(
|
|
2296
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
2297
|
-
args.currentDurationMs
|
|
2298
|
-
);
|
|
2299
|
-
const cumulativeUsage = addAgentTurnUsage(
|
|
2300
|
-
latestSessionRecord?.cumulativeUsage,
|
|
2301
|
-
args.currentUsage
|
|
2302
|
-
);
|
|
2303
|
-
const shared = {
|
|
2304
|
-
...sessionWriteContext(
|
|
2305
|
-
{
|
|
2306
|
-
actor: args.actor,
|
|
2307
|
-
channelName: args.channelName,
|
|
2308
|
-
conversationId: args.conversationId,
|
|
2309
|
-
destination: args.destination,
|
|
2310
|
-
destinationVisibility: args.destinationVisibility,
|
|
2311
|
-
dispatchId: args.dispatchId,
|
|
2312
|
-
// Continuation keeps caller-owned skill names only.
|
|
2313
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2314
|
-
modelId: args.modelId,
|
|
2315
|
-
reasoningLevel: args.reasoningLevel,
|
|
2316
|
-
sessionId: args.sessionId,
|
|
2317
|
-
source: args.source,
|
|
2318
|
-
surface: args.surface
|
|
2319
|
-
},
|
|
2320
|
-
latestSessionRecord
|
|
2321
|
-
),
|
|
2322
|
-
...definedProps2({
|
|
2323
|
-
cumulativeDurationMs,
|
|
2324
|
-
cumulativeUsage
|
|
2236
|
+
cumulativeUsage: addAgentTurnUsage(latest?.cumulativeUsage, args.usage),
|
|
2237
|
+
errorMessage: args.errorMessage
|
|
2325
2238
|
}),
|
|
2326
|
-
piMessages,
|
|
2327
|
-
resumeReason: args.
|
|
2239
|
+
piMessages: messages,
|
|
2240
|
+
resumeReason: args.reason
|
|
2328
2241
|
};
|
|
2329
|
-
if (nextSliceId > botConfig.maxSlicesPerTurn) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
...
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
botConfig.maxSlicesPerTurn
|
|
2337
|
-
).message,
|
|
2338
|
-
sliceId: args.currentSliceId,
|
|
2242
|
+
if (!keepSlice && nextSliceId > botConfig.maxSlicesPerTurn) {
|
|
2243
|
+
const error = new TurnSliceLimitExceededError(botConfig.maxSlicesPerTurn);
|
|
2244
|
+
await upsertTurnRecord({
|
|
2245
|
+
...base,
|
|
2246
|
+
...definedProps2({ resumedFromSliceId: latest?.resumedFromSliceId }),
|
|
2247
|
+
errorMessage: error.message,
|
|
2248
|
+
sliceId: args.sliceId,
|
|
2339
2249
|
state: "failed"
|
|
2340
2250
|
});
|
|
2251
|
+
throw error;
|
|
2341
2252
|
}
|
|
2342
|
-
return await
|
|
2343
|
-
...
|
|
2344
|
-
|
|
2345
|
-
|
|
2253
|
+
return await upsertTurnRecord({
|
|
2254
|
+
...base,
|
|
2255
|
+
...definedProps2({
|
|
2256
|
+
resumedFromSliceId: keepSlice ? latest?.resumedFromSliceId : args.sliceId
|
|
2257
|
+
}),
|
|
2346
2258
|
sliceId: nextSliceId,
|
|
2347
|
-
|
|
2259
|
+
// Stored name is historical; means "paused, may continue".
|
|
2260
|
+
state: "paused"
|
|
2261
|
+
});
|
|
2262
|
+
} catch (error) {
|
|
2263
|
+
if (error instanceof TurnSliceLimitExceededError) {
|
|
2264
|
+
throw error;
|
|
2265
|
+
}
|
|
2266
|
+
logException(error, "agent.turn.checkpoint.paused.failed", {
|
|
2267
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
2268
|
+
"app.ai.resume_session_id": args.turnId,
|
|
2269
|
+
"app.ai.resume_from_slice_id": args.sliceId,
|
|
2270
|
+
"app.ai.resume_next_slice_id": nextSliceId,
|
|
2271
|
+
"app.ai.resume_reason": args.reason
|
|
2348
2272
|
});
|
|
2349
|
-
} catch (recordError) {
|
|
2350
|
-
logSessionRecordError(
|
|
2351
|
-
recordError,
|
|
2352
|
-
"agent.continue.session_record.failed",
|
|
2353
|
-
args,
|
|
2354
|
-
{
|
|
2355
|
-
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
2356
|
-
"app.ai.resume_next_slice_id": nextSliceId
|
|
2357
|
-
}
|
|
2358
|
-
);
|
|
2359
2273
|
return void 0;
|
|
2360
2274
|
}
|
|
2361
2275
|
}
|
|
2362
|
-
async function
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2276
|
+
async function saveDone(args) {
|
|
2277
|
+
let latest;
|
|
2278
|
+
await persistWithRetry(async () => {
|
|
2279
|
+
latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2280
|
+
});
|
|
2281
|
+
const sliceId = args.sliceId ?? latest?.sliceId;
|
|
2282
|
+
if (sliceId === void 0) {
|
|
2283
|
+
throw new Error(
|
|
2284
|
+
"Completed turn checkpoint requires a slice id from the caller or the latest stored record"
|
|
2371
2285
|
);
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
channelName: args.channelName,
|
|
2377
|
-
conversationId: args.conversationId,
|
|
2378
|
-
destination: args.destination,
|
|
2379
|
-
dispatchId: args.dispatchId,
|
|
2380
|
-
// Yield keeps caller-owned skill names only.
|
|
2381
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2382
|
-
modelId: args.modelId,
|
|
2383
|
-
reasoningLevel: args.reasoningLevel,
|
|
2384
|
-
sessionId: args.sessionId,
|
|
2385
|
-
source: args.source,
|
|
2386
|
-
surface: args.surface
|
|
2387
|
-
},
|
|
2388
|
-
latestSessionRecord
|
|
2389
|
-
),
|
|
2286
|
+
}
|
|
2287
|
+
await persistWithRetry(async () => {
|
|
2288
|
+
await upsertTurnRecord({
|
|
2289
|
+
...sharedWrite(args, latest),
|
|
2390
2290
|
...definedProps2({
|
|
2391
2291
|
cumulativeDurationMs: addDurationMs(
|
|
2392
|
-
|
|
2393
|
-
args.
|
|
2292
|
+
latest?.cumulativeDurationMs,
|
|
2293
|
+
args.durationMs
|
|
2394
2294
|
),
|
|
2395
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId
|
|
2295
|
+
cumulativeUsage: addAgentTurnUsage(latest?.cumulativeUsage, args.usage),
|
|
2296
|
+
dispatchOutcome: args.dispatchOutcome ?? latest?.dispatchOutcome,
|
|
2297
|
+
errorMessage: args.errorMessage,
|
|
2298
|
+
resultMessageId: args.resultMessageId ?? latest?.resultMessageId
|
|
2400
2299
|
}),
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
sliceId: args.currentSliceId,
|
|
2405
|
-
state: "awaiting_resume"
|
|
2300
|
+
piMessages: args.messages,
|
|
2301
|
+
sliceId,
|
|
2302
|
+
state: args.mode
|
|
2406
2303
|
});
|
|
2407
|
-
}
|
|
2408
|
-
logSessionRecordError(
|
|
2409
|
-
recordError,
|
|
2410
|
-
"agent.turn.yield_session_record.failed",
|
|
2411
|
-
args,
|
|
2412
|
-
{
|
|
2413
|
-
"app.ai.resume_slice_id": args.currentSliceId
|
|
2414
|
-
}
|
|
2415
|
-
);
|
|
2416
|
-
return void 0;
|
|
2417
|
-
}
|
|
2304
|
+
});
|
|
2418
2305
|
}
|
|
2419
2306
|
|
|
2420
2307
|
// src/chat/state/conversation.ts
|
|
@@ -2923,9 +2810,9 @@ function clearPendingAuth(conversation, sessionId) {
|
|
|
2923
2810
|
}
|
|
2924
2811
|
async function abandonReplacedPendingAuth(args) {
|
|
2925
2812
|
if (args.previousPendingAuth && args.previousPendingAuth.sessionId !== args.nextPendingAuth.sessionId && args.conversationId) {
|
|
2926
|
-
await
|
|
2813
|
+
await abandonTurnRecord({
|
|
2927
2814
|
conversationId: args.conversationId,
|
|
2928
|
-
|
|
2815
|
+
turnId: args.previousPendingAuth.sessionId,
|
|
2929
2816
|
errorMessage: "Abandoned by a newer auth-blocked request in the same conversation."
|
|
2930
2817
|
});
|
|
2931
2818
|
}
|
|
@@ -3021,22 +2908,19 @@ export {
|
|
|
3021
2908
|
sanitizeAssistantText,
|
|
3022
2909
|
decideReply,
|
|
3023
2910
|
getAssistantReplyText,
|
|
3024
|
-
getAgentTurnSessionRecord,
|
|
3025
|
-
getAgentTurnSessionRecordForResume,
|
|
3026
|
-
recordAgentTurnSessionSummary,
|
|
3027
|
-
listAgentTurnSessionSummariesForConversation,
|
|
3028
|
-
abandonAgentTurnSessionRecord,
|
|
3029
|
-
failAgentTurnSessionRecord,
|
|
3030
|
-
persistWithRetry,
|
|
3031
2911
|
TurnSliceLimitExceededError,
|
|
3032
2912
|
buildTurnLimitResponse,
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
2913
|
+
persistWithRetry,
|
|
2914
|
+
getTurnRecord,
|
|
2915
|
+
getTurnRecordForResume,
|
|
2916
|
+
recordTurnSummary,
|
|
2917
|
+
listTurnSummaries,
|
|
2918
|
+
abandonTurnRecord,
|
|
2919
|
+
completeTurnRecord,
|
|
2920
|
+
failTurnRecord,
|
|
2921
|
+
continuableMessages,
|
|
2922
|
+
loadTurnCheckpoint,
|
|
2923
|
+
saveTurnCheckpoint,
|
|
3040
2924
|
canReusePendingAuthLink,
|
|
3041
2925
|
getConversationPendingAuth,
|
|
3042
2926
|
clearPendingAuth,
|