@sentry/junior 0.144.0 → 0.146.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-SLJADYE5.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 +400 -461
- package/dist/{catalog-runtime-DWVYKDC6.js → catalog-runtime-RQIMSDPR.js} +5 -5
- package/dist/chat/agent/request.d.ts +1 -1
- package/dist/chat/agent/resume.d.ts +6 -18
- package/dist/chat/agent/skills.d.ts +0 -1
- package/dist/chat/agent/tools.d.ts +1 -2
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/app/conversation-work.d.ts +24 -0
- package/dist/chat/conversations/message-search.d.ts +32 -0
- package/dist/chat/conversations/projection.d.ts +11 -3
- package/dist/chat/conversations/sql/message-search.d.ts +4 -0
- package/dist/chat/conversations/store.d.ts +1 -1
- package/dist/chat/conversations/tool-source.d.ts +5 -0
- package/dist/chat/db.d.ts +2 -2
- 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/slack/message/attachments.d.ts +2 -0
- package/dist/chat/slack/message/blocks.d.ts +2 -0
- package/dist/chat/slack/message/content.d.ts +18 -0
- package/dist/chat/slack/message/identity.d.ts +7 -0
- package/dist/chat/slack/tools/{conversation-search.d.ts → conversation-message-search.d.ts} +21 -14
- package/dist/chat/slack/tools/user-lookup.d.ts +2 -1
- package/dist/chat/state/locks.d.ts +28 -0
- package/dist/chat/task-execution/checkpoint.d.ts +84 -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 -51
- package/dist/chat/task-execution/turn-wake.d.ts +29 -0
- package/dist/chat/tools/{query-conversation-events.d.ts → search-conversation-events.d.ts} +3 -3
- package/dist/chat/tools/types.d.ts +1 -1
- package/dist/{chunk-SBZP2J5Q.js → chunk-37EVZP66.js} +48 -35
- package/dist/{chunk-A67GZN73.js → chunk-37JJDS4H.js} +1 -1
- package/dist/{chunk-BBCWYXHB.js → chunk-47OSH2ZO.js} +7 -7
- package/dist/{chunk-4X5KM7Z6.js → chunk-AISNSNDV.js} +14 -14
- package/dist/{chunk-RIFJUJSI.js → chunk-B63BF443.js} +1 -1
- package/dist/{chunk-SSDTQDYQ.js → chunk-BLETMLRX.js} +566 -268
- package/dist/{chunk-I44JB7HQ.js → chunk-F7REGDKO.js} +1 -1
- package/dist/{chunk-DVO5VKYQ.js → chunk-FEKQHPVA.js} +5 -5
- package/dist/{chunk-SUK7NC5A.js → chunk-FGHADGIC.js} +2 -2
- package/dist/{chunk-7KAW7UF4.js → chunk-FXDDDSPL.js} +4 -4
- package/dist/{chunk-BEGGFVFR.js → chunk-GDQEU4I7.js} +414 -542
- package/dist/{chunk-UDQMY6RS.js → chunk-GYEKOX7N.js} +43 -1
- package/dist/{chunk-W7JFV5SB.js → chunk-H6U2U3EP.js} +14 -17
- package/dist/{chunk-WTX2RFJG.js → chunk-HQE6EJS5.js} +1 -1
- package/dist/{chunk-IUFM5JWV.js → chunk-JAL3CH4G.js} +3 -0
- package/dist/{chunk-E7HCWOHG.js → chunk-JLMVCNXL.js} +2 -1
- package/dist/{chunk-PELZ2ZSY.js → chunk-JUA6VU2Z.js} +33 -63
- package/dist/{chunk-SHEEZSLY.js → chunk-LVW7BLLT.js} +2 -2
- package/dist/{chunk-GKA74DRA.js → chunk-M6ZWP4RI.js} +1 -1
- package/dist/{chunk-HNO2E5WB.js → chunk-MCFIXAV5.js} +1 -1
- package/dist/{chunk-3GU4PWA3.js → chunk-MKRIZ4VG.js} +29 -30
- package/dist/{chunk-QUZJDAQQ.js → chunk-OFPYOC3M.js} +93 -90
- package/dist/{chunk-ZF3N3JD6.js → chunk-PB222GIA.js} +1 -1
- package/dist/{chunk-PKH6762I.js → chunk-PH4Q3A6W.js} +1 -1
- package/dist/{chunk-STFCL3K4.js → chunk-QG7OOKIN.js} +1 -1
- package/dist/{chunk-WSOLZJJS.js → chunk-QSXZ42KX.js} +3 -3
- package/dist/{chunk-44RJWAND.js → chunk-SCGVWUZX.js} +6 -6
- package/dist/{chunk-CNTWB2SM.js → chunk-T5GXFFLA.js} +1 -1
- package/dist/{chunk-LDWNYCZ6.js → chunk-TCEJ7XXM.js} +1 -1
- package/dist/{chunk-QE3TICD4.js → chunk-V6SRLKUB.js} +2 -2
- package/dist/{chunk-54JMYQYS.js → chunk-W65V465O.js} +2 -2
- package/dist/{chunk-KCBZSJRU.js → chunk-WR76IVA5.js} +5 -2
- package/dist/{chunk-ZZCTGAIT.js → chunk-X22KCSCO.js} +4 -4
- package/dist/{chunk-HD6BHR25.js → chunk-X2WMBTS3.js} +32 -34
- package/dist/{chunk-KQBXT5B7.js → chunk-ZDGHGY74.js} +6 -6
- 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-WWOMXWTT.js} +9 -9
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -4
- package/dist/{oauth-callback-server-RO3IQKWM.js → oauth-callback-server-A6ZZX74U.js} +25 -25
- package/dist/{runner-LOWW7JKA.js → runner-KSGB2UAV.js} +19 -20
- package/dist/{skills-ZLMUG7AE.js → skills-26QPWKVS.js} +6 -6
- package/dist/spawn-5LAJXZ2P.js +39 -0
- package/dist/{validation-MAKEZNTS.js → validation-OPSRGFGH.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-5OAGC3I4.js → work-VMSW7STJ.js} +17 -16
- package/dist/worker-2JNFB7C7.js +24 -0
- package/package.json +5 -5
- package/dist/chat/agent/session.d.ts +0 -19
- package/dist/chat/conversations/search.d.ts +0 -24
- package/dist/chat/conversations/sql/search.d.ts +0 -4
- 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/slack/legacy-attachments.d.ts +0 -4
- package/dist/chat/slack/message.d.ts +0 -12
- 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-37EVZP66.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-GYEKOX7N.js";
|
|
32
35
|
import {
|
|
33
36
|
botConfig,
|
|
34
37
|
unwrapCurrentInstruction
|
|
35
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-MKRIZ4VG.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-B63BF443.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}`;
|
|
1473
|
+
}
|
|
1474
|
+
function turnCursorIndexKey(conversationId) {
|
|
1475
|
+
return `${TURN_CURSOR_PREFIX}:conversation:${conversationId}:index`;
|
|
1414
1476
|
}
|
|
1415
|
-
function
|
|
1416
|
-
return `${
|
|
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);
|
|
1583
|
+
}
|
|
1584
|
+
function parseTurnSummary(value) {
|
|
1585
|
+
return storedTurnSummarySchema.parse(value);
|
|
1499
1586
|
}
|
|
1500
|
-
function
|
|
1501
|
-
return
|
|
1587
|
+
function storeTurnSummary(summary) {
|
|
1588
|
+
return storedTurnSummarySchema.parse(summary);
|
|
1502
1589
|
}
|
|
1503
|
-
function
|
|
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, runtimeMetrics) {
|
|
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,
|
|
@@ -1570,13 +1658,10 @@ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessag
|
|
|
1570
1658
|
piMessages: restoredProjection.messages,
|
|
1571
1659
|
piMessageProvenance: restoredProjection.provenance,
|
|
1572
1660
|
actors: instructionActors(piProjection.provenance),
|
|
1573
|
-
cumulativeDurationMs:
|
|
1661
|
+
cumulativeDurationMs: runtimeMetrics?.cumulativeDurationMs ?? 0,
|
|
1574
1662
|
...definedProps({
|
|
1575
|
-
channelName:
|
|
1576
|
-
cumulativeUsage:
|
|
1577
|
-
loadedSkillNames: runtimeMetadata?.loadedSkillNames,
|
|
1578
|
-
modelId: runtimeMetadata?.modelId,
|
|
1579
|
-
reasoningLevel: runtimeMetadata?.reasoningLevel,
|
|
1663
|
+
channelName: runtimeMetrics?.channelName,
|
|
1664
|
+
cumulativeUsage: runtimeMetrics?.cumulativeUsage,
|
|
1580
1665
|
dispatchId: stored.dispatchId,
|
|
1581
1666
|
dispatchOutcome: stored.dispatchOutcome,
|
|
1582
1667
|
errorMessage: stored.errorMessage,
|
|
@@ -1625,19 +1710,14 @@ function restoreRuntimeContext(projection, runtimeContext) {
|
|
|
1625
1710
|
]
|
|
1626
1711
|
};
|
|
1627
1712
|
}
|
|
1628
|
-
async function
|
|
1713
|
+
async function getStoredTurnRecord(conversationId, turnId) {
|
|
1629
1714
|
const stateAdapter = getStateAdapter();
|
|
1630
1715
|
await stateAdapter.connect();
|
|
1631
|
-
const value = await stateAdapter.get(
|
|
1632
|
-
|
|
1633
|
-
);
|
|
1634
|
-
return value == null ? void 0 : parseAgentTurnSessionRecord(value);
|
|
1716
|
+
const value = await stateAdapter.get(turnCursorKey(conversationId, turnId));
|
|
1717
|
+
return value == null ? void 0 : parseTurnRecord(value);
|
|
1635
1718
|
}
|
|
1636
|
-
async function
|
|
1637
|
-
const parsed = await
|
|
1638
|
-
conversationId,
|
|
1639
|
-
sessionId
|
|
1640
|
-
);
|
|
1719
|
+
async function materializeStoredTurnRecord(conversationId, turnId, followCurrentReplacement) {
|
|
1720
|
+
const parsed = await getStoredTurnRecord(conversationId, turnId);
|
|
1641
1721
|
if (!parsed) {
|
|
1642
1722
|
return void 0;
|
|
1643
1723
|
}
|
|
@@ -1646,19 +1726,19 @@ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId
|
|
|
1646
1726
|
committedSeq: parsed.committedSeq,
|
|
1647
1727
|
// Unfinished records include the current-epoch tail so parked input
|
|
1648
1728
|
// appended after the last safe boundary stays model-visible on resume.
|
|
1649
|
-
includeTail: parsed.state === "running" || parsed.state === "
|
|
1729
|
+
includeTail: parsed.state === "running" || parsed.state === "paused"
|
|
1650
1730
|
});
|
|
1651
1731
|
if (!pinnedProjection) {
|
|
1652
1732
|
return void 0;
|
|
1653
1733
|
}
|
|
1654
1734
|
const currentHistory = await getConversationEventStore().loadCurrentHistory(conversationId);
|
|
1655
1735
|
const currentHistoryVersion = currentHistory.at(-1)?.historyVersion ?? parsed.historyVersion ?? 0;
|
|
1656
|
-
const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "
|
|
1736
|
+
const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "paused") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
|
|
1657
1737
|
const piProjection = followsReplacement ? projectConversationEvents(currentHistory) : pinnedProjection;
|
|
1658
1738
|
const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
|
|
1659
1739
|
const conversation = await getConversationStore().get({ conversationId });
|
|
1660
|
-
const executionMetrics =
|
|
1661
|
-
return
|
|
1740
|
+
const executionMetrics = executionMetricsForTurn(conversation, turnId);
|
|
1741
|
+
return materializeTurnRecord(
|
|
1662
1742
|
parsed,
|
|
1663
1743
|
piProjection,
|
|
1664
1744
|
turnStartMessageIndex,
|
|
@@ -1670,26 +1750,19 @@ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId
|
|
|
1670
1750
|
}
|
|
1671
1751
|
);
|
|
1672
1752
|
}
|
|
1673
|
-
async function
|
|
1674
|
-
return await
|
|
1675
|
-
conversationId,
|
|
1676
|
-
sessionId,
|
|
1677
|
-
false
|
|
1678
|
-
);
|
|
1753
|
+
async function getTurnRecord(conversationId, turnId) {
|
|
1754
|
+
return await materializeStoredTurnRecord(conversationId, turnId, false);
|
|
1679
1755
|
}
|
|
1680
|
-
async function
|
|
1681
|
-
return await
|
|
1682
|
-
conversationId,
|
|
1683
|
-
sessionId,
|
|
1684
|
-
true
|
|
1685
|
-
);
|
|
1756
|
+
async function getTurnRecordForResume(conversationId, turnId) {
|
|
1757
|
+
return await materializeStoredTurnRecord(conversationId, turnId, true);
|
|
1686
1758
|
}
|
|
1687
1759
|
function buildStoredRecord(args) {
|
|
1688
1760
|
const nowMs = Date.now();
|
|
1689
1761
|
return {
|
|
1762
|
+
schemaVersion: TURN_CURSOR_SCHEMA_VERSION,
|
|
1690
1763
|
version: (args.previousVersion ?? 0) + 1,
|
|
1691
1764
|
conversationId: args.conversationId,
|
|
1692
|
-
|
|
1765
|
+
turnId: args.turnId,
|
|
1693
1766
|
sliceId: args.sliceId,
|
|
1694
1767
|
state: args.state,
|
|
1695
1768
|
startedAtMs: args.startedAtMs ?? nowMs,
|
|
@@ -1714,7 +1787,8 @@ function buildStoredRecord(args) {
|
|
|
1714
1787
|
async function setStoredRecord(args) {
|
|
1715
1788
|
const stateAdapter = getStateAdapter();
|
|
1716
1789
|
await stateAdapter.connect();
|
|
1717
|
-
const storedRecord =
|
|
1790
|
+
const storedRecord = storedTurnRecordSchema.parse(args.record);
|
|
1791
|
+
await args.fence();
|
|
1718
1792
|
await recordConversationActivityMetadata({
|
|
1719
1793
|
actor: args.actor,
|
|
1720
1794
|
conversationStore: args.conversationStore,
|
|
@@ -1722,18 +1796,24 @@ async function setStoredRecord(args) {
|
|
|
1722
1796
|
destinationVisibility: args.destinationVisibility,
|
|
1723
1797
|
nowMs: Date.now(),
|
|
1724
1798
|
source: args.source,
|
|
1725
|
-
summary: {
|
|
1799
|
+
summary: {
|
|
1800
|
+
...projectTurnSummary(storedRecord),
|
|
1801
|
+
startedAtMs: storedRecord.startedAtMs,
|
|
1802
|
+
...args.runtimeMetrics
|
|
1803
|
+
}
|
|
1726
1804
|
});
|
|
1805
|
+
await args.fence();
|
|
1727
1806
|
await stateAdapter.set(
|
|
1728
|
-
|
|
1807
|
+
turnCursorKey(storedRecord.conversationId, storedRecord.turnId),
|
|
1729
1808
|
storedRecord,
|
|
1730
1809
|
args.ttlMs
|
|
1731
1810
|
);
|
|
1732
|
-
await
|
|
1733
|
-
|
|
1734
|
-
|
|
1811
|
+
await args.fence();
|
|
1812
|
+
await appendTurnSummary(
|
|
1813
|
+
projectTurnSummary(storedRecord),
|
|
1814
|
+
turnCursorIndexTtlMs(args.indexTtlMs)
|
|
1735
1815
|
);
|
|
1736
|
-
return
|
|
1816
|
+
return materializeTurnRecord(
|
|
1737
1817
|
storedRecord,
|
|
1738
1818
|
{
|
|
1739
1819
|
messages: [...args.piMessages],
|
|
@@ -1741,29 +1821,27 @@ async function setStoredRecord(args) {
|
|
|
1741
1821
|
},
|
|
1742
1822
|
args.turnStartMessageIndex,
|
|
1743
1823
|
true,
|
|
1744
|
-
args.
|
|
1824
|
+
args.runtimeMetrics
|
|
1745
1825
|
);
|
|
1746
1826
|
}
|
|
1747
|
-
async function
|
|
1748
|
-
const parsed = await
|
|
1827
|
+
async function updateTurnState(args) {
|
|
1828
|
+
const parsed = await getStoredTurnRecord(
|
|
1749
1829
|
args.existing.conversationId,
|
|
1750
|
-
args.existing.
|
|
1830
|
+
args.existing.turnId
|
|
1751
1831
|
);
|
|
1752
1832
|
if (!parsed || parsed.version !== args.existing.version) {
|
|
1753
1833
|
return void 0;
|
|
1754
1834
|
}
|
|
1755
1835
|
return await setStoredRecord({
|
|
1836
|
+
fence: args.fence,
|
|
1756
1837
|
piMessages: args.existing.piMessages,
|
|
1757
1838
|
piMessageProvenance: args.existing.piMessageProvenance,
|
|
1758
|
-
ttlMs:
|
|
1759
|
-
|
|
1839
|
+
ttlMs: turnCursorTtlMs(args.state),
|
|
1840
|
+
runtimeMetrics: {
|
|
1760
1841
|
cumulativeDurationMs: args.existing.cumulativeDurationMs,
|
|
1761
1842
|
...definedProps({
|
|
1762
1843
|
channelName: args.existing.channelName,
|
|
1763
|
-
cumulativeUsage: args.existing.cumulativeUsage
|
|
1764
|
-
loadedSkillNames: args.existing.loadedSkillNames,
|
|
1765
|
-
modelId: args.existing.modelId,
|
|
1766
|
-
reasoningLevel: args.existing.reasoningLevel
|
|
1844
|
+
cumulativeUsage: args.existing.cumulativeUsage
|
|
1767
1845
|
})
|
|
1768
1846
|
},
|
|
1769
1847
|
...definedProps({
|
|
@@ -1771,7 +1849,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1771
1849
|
}),
|
|
1772
1850
|
record: buildStoredRecord({
|
|
1773
1851
|
conversationId: args.existing.conversationId,
|
|
1774
|
-
|
|
1852
|
+
turnId: args.existing.turnId,
|
|
1775
1853
|
sliceId: args.existing.sliceId,
|
|
1776
1854
|
state: args.state,
|
|
1777
1855
|
committedSeq: parsed.committedSeq,
|
|
@@ -1784,7 +1862,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1784
1862
|
errorMessage: args.errorMessage ?? args.existing.errorMessage,
|
|
1785
1863
|
historyVersion: parsed.historyVersion,
|
|
1786
1864
|
resumeReason: args.existing.resumeReason,
|
|
1787
|
-
resultMessageId: args.existing.resultMessageId,
|
|
1865
|
+
resultMessageId: args.resultMessageId ?? args.existing.resultMessageId,
|
|
1788
1866
|
resumedFromSliceId: args.existing.resumedFromSliceId,
|
|
1789
1867
|
surface: args.existing.surface,
|
|
1790
1868
|
traceId: args.existing.traceId,
|
|
@@ -1793,25 +1871,29 @@ async function updateAgentTurnSessionState(args) {
|
|
|
1793
1871
|
})
|
|
1794
1872
|
});
|
|
1795
1873
|
}
|
|
1796
|
-
async function
|
|
1797
|
-
|
|
1874
|
+
async function upsertTurnRecord(args) {
|
|
1875
|
+
return await withTurnCursorMutation(
|
|
1876
|
+
args.conversationId,
|
|
1877
|
+
args.turnId,
|
|
1878
|
+
async (fence) => await upsertTurnRecordLocked(args, fence)
|
|
1879
|
+
);
|
|
1880
|
+
}
|
|
1881
|
+
async function upsertTurnRecordLocked(args, fence) {
|
|
1882
|
+
const existingRecord = await getStoredTurnRecord(
|
|
1798
1883
|
args.conversationId,
|
|
1799
|
-
args.
|
|
1884
|
+
args.turnId
|
|
1800
1885
|
);
|
|
1801
1886
|
const conversation = await (args.conversationStore ?? getConversationStore()).get({
|
|
1802
1887
|
conversationId: args.conversationId
|
|
1803
1888
|
});
|
|
1804
|
-
const executionMetrics =
|
|
1805
|
-
|
|
1806
|
-
args.
|
|
1807
|
-
);
|
|
1808
|
-
const existingDispatchId = existingRecord?.dispatchId ?? (await listAgentTurnSessionSummariesForConversation(args.conversationId)).find((summary) => summary.sessionId === args.sessionId)?.dispatchId;
|
|
1889
|
+
const executionMetrics = executionMetricsForTurn(conversation, args.turnId);
|
|
1890
|
+
const existingDispatchId = existingRecord?.dispatchId ?? (await listTurnSummaries(args.conversationId)).find(
|
|
1891
|
+
(summary) => summary.turnId === args.turnId
|
|
1892
|
+
)?.dispatchId;
|
|
1809
1893
|
if (existingDispatchId && args.dispatchId && existingDispatchId !== args.dispatchId) {
|
|
1810
|
-
throw new Error(
|
|
1811
|
-
`Turn session ${args.sessionId} dispatchId cannot be changed`
|
|
1812
|
-
);
|
|
1894
|
+
throw new Error(`Turn ${args.turnId} dispatchId cannot be changed`);
|
|
1813
1895
|
}
|
|
1814
|
-
const ttlMs =
|
|
1896
|
+
const ttlMs = turnCursorTtlMs(args.state, args.ttlMs);
|
|
1815
1897
|
const instructionActor = args.actor;
|
|
1816
1898
|
const commit = await commitMessages({
|
|
1817
1899
|
conversationId: args.conversationId,
|
|
@@ -1821,7 +1903,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1821
1903
|
...args.turnContexts && args.turnContexts.length > 0 ? {
|
|
1822
1904
|
turnContext: {
|
|
1823
1905
|
contexts: args.turnContexts,
|
|
1824
|
-
turnId: args.
|
|
1906
|
+
turnId: args.turnId
|
|
1825
1907
|
}
|
|
1826
1908
|
} : {}
|
|
1827
1909
|
});
|
|
@@ -1834,6 +1916,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1834
1916
|
const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
|
|
1835
1917
|
return await setStoredRecord({
|
|
1836
1918
|
actor: args.actor,
|
|
1919
|
+
fence,
|
|
1837
1920
|
conversationStore: args.conversationStore,
|
|
1838
1921
|
destination: args.destination,
|
|
1839
1922
|
destinationVisibility: args.destinationVisibility,
|
|
@@ -1841,15 +1924,12 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1841
1924
|
piMessages: commit.messages,
|
|
1842
1925
|
piMessageProvenance: commit.provenance,
|
|
1843
1926
|
ttlMs,
|
|
1844
|
-
//
|
|
1845
|
-
//
|
|
1846
|
-
|
|
1927
|
+
// Checkpoint commit also updates conversation execution metrics in SQL.
|
|
1928
|
+
// Channel/duration/usage are not Redis resume-cursor fields.
|
|
1929
|
+
runtimeMetrics: {
|
|
1847
1930
|
channelName: args.channelName ?? conversation?.channelName,
|
|
1848
1931
|
cumulativeDurationMs: args.cumulativeDurationMs ?? executionMetrics?.durationMs ?? 0,
|
|
1849
|
-
cumulativeUsage: args.cumulativeUsage ?? executionMetrics?.usage
|
|
1850
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
1851
|
-
modelId: args.modelId,
|
|
1852
|
-
reasoningLevel: args.reasoningLevel
|
|
1932
|
+
cumulativeUsage: args.cumulativeUsage ?? executionMetrics?.usage
|
|
1853
1933
|
},
|
|
1854
1934
|
// Pass through only an explicit override so indexes stay on the resume window
|
|
1855
1935
|
// by default even when the record takes a short terminal TTL.
|
|
@@ -1857,7 +1937,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1857
1937
|
turnStartMessageIndex,
|
|
1858
1938
|
record: buildStoredRecord({
|
|
1859
1939
|
conversationId: args.conversationId,
|
|
1860
|
-
|
|
1940
|
+
turnId: args.turnId,
|
|
1861
1941
|
sliceId: args.sliceId,
|
|
1862
1942
|
state: args.state,
|
|
1863
1943
|
committedSeq: commit.committedSeq,
|
|
@@ -1871,7 +1951,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1871
1951
|
resumeReason: args.resumeReason,
|
|
1872
1952
|
resultMessageId: args.resultMessageId ?? existingRecord?.resultMessageId,
|
|
1873
1953
|
resumedFromSliceId: args.resumedFromSliceId,
|
|
1874
|
-
runtimeContext: args.state === "running" || args.state === "
|
|
1954
|
+
runtimeContext: args.state === "running" || args.state === "paused" ? retainedRuntimeContext : void 0,
|
|
1875
1955
|
startedAtMs: existingRecord?.startedAtMs,
|
|
1876
1956
|
surface: args.surface ?? existingRecord?.surface,
|
|
1877
1957
|
traceId: args.traceId ?? existingRecord?.traceId,
|
|
@@ -1880,35 +1960,42 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
1880
1960
|
})
|
|
1881
1961
|
});
|
|
1882
1962
|
}
|
|
1883
|
-
async function
|
|
1884
|
-
|
|
1963
|
+
async function recordTurnSummary(args) {
|
|
1964
|
+
await withTurnCursorMutation(
|
|
1885
1965
|
args.conversationId,
|
|
1886
|
-
args.
|
|
1966
|
+
args.turnId,
|
|
1967
|
+
async (fence) => await recordTurnSummaryOwned(args, fence)
|
|
1887
1968
|
);
|
|
1969
|
+
}
|
|
1970
|
+
async function recordTurnSummaryOwned(args, fence) {
|
|
1971
|
+
const stored = await getStoredTurnRecord(args.conversationId, args.turnId);
|
|
1888
1972
|
const conversationStore = args.conversationStore ?? getConversationStore();
|
|
1889
1973
|
const conversation = await conversationStore.get({
|
|
1890
1974
|
conversationId: args.conversationId
|
|
1891
1975
|
});
|
|
1892
|
-
const executionMetrics =
|
|
1893
|
-
|
|
1894
|
-
args.
|
|
1976
|
+
const executionMetrics = executionMetricsForTurn(conversation, args.turnId);
|
|
1977
|
+
const priorSummary = (await listTurnSummaries(args.conversationId)).find(
|
|
1978
|
+
(summary2) => summary2.turnId === args.turnId
|
|
1895
1979
|
);
|
|
1896
|
-
const
|
|
1897
|
-
const
|
|
1980
|
+
const terminalSummary = priorSummary && (priorSummary.state === "completed" || priorSummary.state === "failed" || priorSummary.state === "abandoned") ? priorSummary : void 0;
|
|
1981
|
+
const terminalRecord = stored && (stored.state === "completed" || stored.state === "failed" || stored.state === "abandoned") ? stored : void 0;
|
|
1982
|
+
const existing = terminalSummary ?? terminalRecord ?? stored ?? priorSummary;
|
|
1983
|
+
if ((terminalSummary || terminalRecord) && (args.state === "running" || args.state === "paused")) {
|
|
1984
|
+
return;
|
|
1985
|
+
}
|
|
1898
1986
|
const existingDispatchId = existing?.dispatchId;
|
|
1899
1987
|
const existingDispatchOutcome = priorSummary?.dispatchOutcome ?? stored?.dispatchOutcome;
|
|
1900
1988
|
const existingResultMessageId = priorSummary?.resultMessageId ?? stored?.resultMessageId;
|
|
1901
1989
|
if (existingDispatchId && args.dispatchId && existingDispatchId !== args.dispatchId) {
|
|
1902
|
-
throw new Error(
|
|
1903
|
-
`Turn session ${args.sessionId} dispatchId cannot be changed`
|
|
1904
|
-
);
|
|
1990
|
+
throw new Error(`Turn ${args.turnId} dispatchId cannot be changed`);
|
|
1905
1991
|
}
|
|
1906
1992
|
const nowMs = Date.now();
|
|
1907
|
-
const ttlMs =
|
|
1993
|
+
const ttlMs = turnCursorIndexTtlMs(args.ttlMs);
|
|
1908
1994
|
const summary = {
|
|
1995
|
+
schemaVersion: TURN_CURSOR_SCHEMA_VERSION,
|
|
1909
1996
|
version: existing?.version ?? 0,
|
|
1910
1997
|
conversationId: args.conversationId,
|
|
1911
|
-
|
|
1998
|
+
turnId: args.turnId,
|
|
1912
1999
|
sliceId: args.sliceId,
|
|
1913
2000
|
state: args.state,
|
|
1914
2001
|
updatedAtMs: nowMs,
|
|
@@ -1920,6 +2007,7 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
1920
2007
|
surface: args.surface ?? existing?.surface
|
|
1921
2008
|
})
|
|
1922
2009
|
};
|
|
2010
|
+
await fence();
|
|
1923
2011
|
await recordConversationActivityMetadata({
|
|
1924
2012
|
actor: args.actor,
|
|
1925
2013
|
conversationStore,
|
|
@@ -1935,9 +2023,10 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
1935
2023
|
startedAtMs: stored?.startedAtMs ?? args.startedAtMs ?? nowMs
|
|
1936
2024
|
}
|
|
1937
2025
|
});
|
|
1938
|
-
await
|
|
2026
|
+
await fence();
|
|
2027
|
+
await appendTurnSummary(summary, ttlMs);
|
|
1939
2028
|
}
|
|
1940
|
-
async function
|
|
2029
|
+
async function readTurnSummaries(key) {
|
|
1941
2030
|
const stateAdapter = getStateAdapter();
|
|
1942
2031
|
await stateAdapter.connect();
|
|
1943
2032
|
const values = await stateAdapter.getList(key);
|
|
@@ -1945,11 +2034,11 @@ async function readAgentTurnSessionSummariesFromIndex(key) {
|
|
|
1945
2034
|
for (const value of [...values].reverse()) {
|
|
1946
2035
|
let summary;
|
|
1947
2036
|
try {
|
|
1948
|
-
summary =
|
|
2037
|
+
summary = parseTurnSummary(value);
|
|
1949
2038
|
} catch {
|
|
1950
2039
|
continue;
|
|
1951
2040
|
}
|
|
1952
|
-
const summaryKey = `${summary.conversationId}:${summary.
|
|
2041
|
+
const summaryKey = `${summary.conversationId}:${summary.turnId}`;
|
|
1953
2042
|
if (!summaries.has(summaryKey)) {
|
|
1954
2043
|
summaries.set(summaryKey, summary);
|
|
1955
2044
|
}
|
|
@@ -1958,56 +2047,62 @@ async function readAgentTurnSessionSummariesFromIndex(key) {
|
|
|
1958
2047
|
(left, right) => right.updatedAtMs - left.updatedAtMs
|
|
1959
2048
|
);
|
|
1960
2049
|
}
|
|
1961
|
-
async function
|
|
1962
|
-
return
|
|
1963
|
-
agentTurnSessionConversationIndexKey(conversationId)
|
|
1964
|
-
);
|
|
2050
|
+
async function listTurnSummaries(conversationId) {
|
|
2051
|
+
return readTurnSummaries(turnCursorIndexKey(conversationId));
|
|
1965
2052
|
}
|
|
1966
|
-
async function
|
|
1967
|
-
|
|
2053
|
+
async function abandonTurnRecord(args) {
|
|
2054
|
+
return await withTurnCursorMutation(
|
|
1968
2055
|
args.conversationId,
|
|
1969
|
-
args.
|
|
2056
|
+
args.turnId,
|
|
2057
|
+
async (fence) => {
|
|
2058
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2059
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned") {
|
|
2060
|
+
return void 0;
|
|
2061
|
+
}
|
|
2062
|
+
return await updateTurnState({
|
|
2063
|
+
existing,
|
|
2064
|
+
fence,
|
|
2065
|
+
state: "abandoned",
|
|
2066
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
1970
2069
|
);
|
|
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
2070
|
}
|
|
1980
|
-
async function
|
|
1981
|
-
|
|
2071
|
+
async function completeTurnRecord(args) {
|
|
2072
|
+
return await withTurnCursorMutation(
|
|
1982
2073
|
args.conversationId,
|
|
1983
|
-
args.
|
|
2074
|
+
args.turnId,
|
|
2075
|
+
async (fence) => {
|
|
2076
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2077
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
2078
|
+
return void 0;
|
|
2079
|
+
}
|
|
2080
|
+
return await updateTurnState({
|
|
2081
|
+
existing,
|
|
2082
|
+
fence,
|
|
2083
|
+
resultMessageId: args.resultMessageId,
|
|
2084
|
+
state: "completed"
|
|
2085
|
+
});
|
|
2086
|
+
}
|
|
1984
2087
|
);
|
|
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
2088
|
}
|
|
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);
|
|
2089
|
+
async function failTurnRecord(args) {
|
|
2090
|
+
return await withTurnCursorMutation(
|
|
2091
|
+
args.conversationId,
|
|
2092
|
+
args.turnId,
|
|
2093
|
+
async (fence) => {
|
|
2094
|
+
const existing = await getTurnRecord(args.conversationId, args.turnId);
|
|
2095
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
2096
|
+
return void 0;
|
|
2007
2097
|
}
|
|
2098
|
+
return await updateTurnState({
|
|
2099
|
+
existing,
|
|
2100
|
+
fence,
|
|
2101
|
+
state: "failed",
|
|
2102
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
2103
|
+
});
|
|
2008
2104
|
}
|
|
2009
|
-
|
|
2010
|
-
throw lastError;
|
|
2105
|
+
);
|
|
2011
2106
|
}
|
|
2012
2107
|
|
|
2013
2108
|
// src/chat/services/turn-limit.ts
|
|
@@ -2021,41 +2116,12 @@ function buildTurnLimitResponse(eventId) {
|
|
|
2021
2116
|
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
2117
|
}
|
|
2023
2118
|
|
|
2024
|
-
// src/chat/
|
|
2119
|
+
// src/chat/task-execution/checkpoint.ts
|
|
2025
2120
|
function definedProps2(values) {
|
|
2026
2121
|
return Object.fromEntries(
|
|
2027
2122
|
Object.entries(values).filter(([, value]) => value !== void 0)
|
|
2028
2123
|
);
|
|
2029
2124
|
}
|
|
2030
|
-
function sessionWriteContext(args, latest) {
|
|
2031
|
-
return {
|
|
2032
|
-
conversationId: args.conversationId,
|
|
2033
|
-
modelId: args.modelId,
|
|
2034
|
-
sessionId: args.sessionId,
|
|
2035
|
-
...definedProps2({
|
|
2036
|
-
// Live only: Redis no longer stores execution actor across resumes.
|
|
2037
|
-
actor: args.actor,
|
|
2038
|
-
channelName: args.channelName ?? latest?.channelName,
|
|
2039
|
-
destination: args.destination,
|
|
2040
|
-
destinationVisibility: args.destinationVisibility,
|
|
2041
|
-
dispatchId: args.dispatchId ?? latest?.dispatchId,
|
|
2042
|
-
// Caller-resolved: some paths inherit these, others stay caller-only.
|
|
2043
|
-
loadedSkillNames: args.loadedSkillNames,
|
|
2044
|
-
reasoningLevel: args.reasoningLevel,
|
|
2045
|
-
source: args.source,
|
|
2046
|
-
surface: args.surface ?? latest?.surface,
|
|
2047
|
-
traceId: getActiveTraceId() ?? latest?.traceId,
|
|
2048
|
-
turnStartMessageIndex: args.turnStartMessageIndex ?? latest?.turnStartMessageIndex
|
|
2049
|
-
})
|
|
2050
|
-
};
|
|
2051
|
-
}
|
|
2052
|
-
function logSessionRecordError(error, eventName, args, attributes) {
|
|
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
2125
|
function addDurationMs(prior, current) {
|
|
2060
2126
|
const total = [prior, current].reduce((sum, value) => {
|
|
2061
2127
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
@@ -2065,356 +2131,165 @@ function addDurationMs(prior, current) {
|
|
|
2065
2131
|
}, void 0);
|
|
2066
2132
|
return total;
|
|
2067
2133
|
}
|
|
2068
|
-
function
|
|
2134
|
+
function continuableMessages(messages, fallback) {
|
|
2069
2135
|
const current = trimTrailingAssistantMessages(messages);
|
|
2070
2136
|
if (current.length > 0 && isContinuablePiBoundary(current)) {
|
|
2071
2137
|
return current;
|
|
2072
2138
|
}
|
|
2073
|
-
return trimTrailingAssistantMessages(
|
|
2139
|
+
return trimTrailingAssistantMessages(fallback ?? []);
|
|
2074
2140
|
}
|
|
2075
|
-
async function
|
|
2076
|
-
const
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2141
|
+
async function loadTurnCheckpoint(args) {
|
|
2142
|
+
const record = await getTurnRecordForResume(args.conversationId, args.turnId);
|
|
2143
|
+
if (record?.state === "paused") {
|
|
2144
|
+
return { resumed: true, sliceId: record.sliceId, record };
|
|
2145
|
+
}
|
|
2146
|
+
return { resumed: false, sliceId: 1, record };
|
|
2147
|
+
}
|
|
2148
|
+
async function saveTurnCheckpoint(args) {
|
|
2149
|
+
if (args.mode === "running") {
|
|
2150
|
+
return await saveRunning(args);
|
|
2151
|
+
}
|
|
2152
|
+
if (args.mode === "paused") {
|
|
2153
|
+
return await savePaused(args);
|
|
2154
|
+
}
|
|
2155
|
+
await saveDone(args);
|
|
2156
|
+
return void 0;
|
|
2157
|
+
}
|
|
2158
|
+
function sharedWrite(args, latest) {
|
|
2083
2159
|
return {
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2160
|
+
conversationId: args.conversationId,
|
|
2161
|
+
turnId: args.turnId,
|
|
2162
|
+
...definedProps2({
|
|
2163
|
+
actor: args.actor,
|
|
2164
|
+
channelName: args.channelName ?? latest?.channelName,
|
|
2165
|
+
destination: args.destination,
|
|
2166
|
+
destinationVisibility: args.destinationVisibility,
|
|
2167
|
+
dispatchId: args.dispatchId ?? latest?.dispatchId,
|
|
2168
|
+
source: args.source,
|
|
2169
|
+
surface: args.surface ?? latest?.surface,
|
|
2170
|
+
traceId: getActiveTraceId() ?? latest?.traceId,
|
|
2171
|
+
turnStartMessageIndex: args.turnStartMessageIndex ?? latest?.turnStartMessageIndex
|
|
2172
|
+
})
|
|
2087
2173
|
};
|
|
2088
2174
|
}
|
|
2089
|
-
async function
|
|
2175
|
+
async function saveRunning(args) {
|
|
2090
2176
|
if (args.messages.length === 0 || !isContinuablePiBoundary(args.messages)) {
|
|
2091
|
-
return
|
|
2177
|
+
return void 0;
|
|
2092
2178
|
}
|
|
2093
2179
|
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
|
-
),
|
|
2180
|
+
const latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2181
|
+
return await upsertTurnRecord({
|
|
2182
|
+
...sharedWrite(args, latest),
|
|
2117
2183
|
...definedProps2({
|
|
2118
2184
|
trailingMessageProvenance: args.trailingMessageProvenance,
|
|
2119
2185
|
turnContexts: args.turnContexts
|
|
2120
2186
|
}),
|
|
2121
|
-
cumulativeDurationMs:
|
|
2122
|
-
cumulativeUsage:
|
|
2187
|
+
cumulativeDurationMs: latest?.cumulativeDurationMs,
|
|
2188
|
+
cumulativeUsage: latest?.cumulativeUsage,
|
|
2123
2189
|
piMessages: args.messages,
|
|
2124
2190
|
sliceId: args.sliceId,
|
|
2125
2191
|
state: "running"
|
|
2126
2192
|
});
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
args,
|
|
2133
|
-
{
|
|
2193
|
+
} catch (error) {
|
|
2194
|
+
if (!(error instanceof AgentHistoryBranchError)) {
|
|
2195
|
+
logException(error, "agent.turn.checkpoint.running.failed", {
|
|
2196
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
2197
|
+
"app.ai.resume_session_id": args.turnId,
|
|
2134
2198
|
"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
|
-
);
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
return void 0;
|
|
2153
2202
|
}
|
|
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
|
-
}
|
|
2194
|
-
async function completeDeliveredTurn(args) {
|
|
2195
|
-
await persistCompletedSessionRecord({
|
|
2196
|
-
channelName: args.channelName,
|
|
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
2203
|
}
|
|
2218
|
-
async function
|
|
2219
|
-
const
|
|
2204
|
+
async function savePaused(args) {
|
|
2205
|
+
const keepSlice = args.reason === "yield";
|
|
2206
|
+
const nextSliceId = keepSlice ? args.sliceId : args.sliceId + 1;
|
|
2220
2207
|
try {
|
|
2221
|
-
const
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
);
|
|
2229
|
-
if (piMessages.length > 0 && !isContinuablePiBoundary(piMessages)) {
|
|
2208
|
+
const latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2209
|
+
const messages = args.reason === "yield" ? [...args.messages] : continuableMessages(args.messages, latest?.piMessages);
|
|
2210
|
+
if (args.reason === "auth") {
|
|
2211
|
+
if (messages.length > 0 && !isContinuablePiBoundary(messages)) {
|
|
2212
|
+
return void 0;
|
|
2213
|
+
}
|
|
2214
|
+
} else if (messages.length === 0 || !isContinuablePiBoundary(messages)) {
|
|
2230
2215
|
return void 0;
|
|
2231
2216
|
}
|
|
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
|
-
),
|
|
2217
|
+
const base = {
|
|
2218
|
+
...sharedWrite(args, latest),
|
|
2251
2219
|
...definedProps2({
|
|
2252
2220
|
cumulativeDurationMs: addDurationMs(
|
|
2253
|
-
|
|
2254
|
-
args.
|
|
2221
|
+
latest?.cumulativeDurationMs,
|
|
2222
|
+
args.durationMs
|
|
2255
2223
|
),
|
|
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
|
|
2224
|
+
cumulativeUsage: addAgentTurnUsage(latest?.cumulativeUsage, args.usage),
|
|
2225
|
+
errorMessage: args.errorMessage
|
|
2325
2226
|
}),
|
|
2326
|
-
piMessages,
|
|
2327
|
-
resumeReason: args.
|
|
2227
|
+
piMessages: messages,
|
|
2228
|
+
resumeReason: args.reason
|
|
2328
2229
|
};
|
|
2329
|
-
if (nextSliceId > botConfig.maxSlicesPerTurn) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
...
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
botConfig.maxSlicesPerTurn
|
|
2337
|
-
).message,
|
|
2338
|
-
sliceId: args.currentSliceId,
|
|
2230
|
+
if (!keepSlice && nextSliceId > botConfig.maxSlicesPerTurn) {
|
|
2231
|
+
const error = new TurnSliceLimitExceededError(botConfig.maxSlicesPerTurn);
|
|
2232
|
+
await upsertTurnRecord({
|
|
2233
|
+
...base,
|
|
2234
|
+
...definedProps2({ resumedFromSliceId: latest?.resumedFromSliceId }),
|
|
2235
|
+
errorMessage: error.message,
|
|
2236
|
+
sliceId: args.sliceId,
|
|
2339
2237
|
state: "failed"
|
|
2340
2238
|
});
|
|
2239
|
+
throw error;
|
|
2341
2240
|
}
|
|
2342
|
-
return await
|
|
2343
|
-
...
|
|
2344
|
-
|
|
2345
|
-
|
|
2241
|
+
return await upsertTurnRecord({
|
|
2242
|
+
...base,
|
|
2243
|
+
...definedProps2({
|
|
2244
|
+
resumedFromSliceId: keepSlice ? latest?.resumedFromSliceId : args.sliceId
|
|
2245
|
+
}),
|
|
2346
2246
|
sliceId: nextSliceId,
|
|
2347
|
-
|
|
2247
|
+
// Stored name is historical; means "paused, may continue".
|
|
2248
|
+
state: "paused"
|
|
2249
|
+
});
|
|
2250
|
+
} catch (error) {
|
|
2251
|
+
if (error instanceof TurnSliceLimitExceededError) {
|
|
2252
|
+
throw error;
|
|
2253
|
+
}
|
|
2254
|
+
logException(error, "agent.turn.checkpoint.paused.failed", {
|
|
2255
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
2256
|
+
"app.ai.resume_session_id": args.turnId,
|
|
2257
|
+
"app.ai.resume_from_slice_id": args.sliceId,
|
|
2258
|
+
"app.ai.resume_next_slice_id": nextSliceId,
|
|
2259
|
+
"app.ai.resume_reason": args.reason
|
|
2348
2260
|
});
|
|
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
2261
|
return void 0;
|
|
2360
2262
|
}
|
|
2361
2263
|
}
|
|
2362
|
-
async function
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2264
|
+
async function saveDone(args) {
|
|
2265
|
+
let latest;
|
|
2266
|
+
await persistWithRetry(async () => {
|
|
2267
|
+
latest = await getTurnRecord(args.conversationId, args.turnId);
|
|
2268
|
+
});
|
|
2269
|
+
const sliceId = args.sliceId ?? latest?.sliceId;
|
|
2270
|
+
if (sliceId === void 0) {
|
|
2271
|
+
throw new Error(
|
|
2272
|
+
"Completed turn checkpoint requires a slice id from the caller or the latest stored record"
|
|
2371
2273
|
);
|
|
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
|
-
),
|
|
2274
|
+
}
|
|
2275
|
+
await persistWithRetry(async () => {
|
|
2276
|
+
await upsertTurnRecord({
|
|
2277
|
+
...sharedWrite(args, latest),
|
|
2390
2278
|
...definedProps2({
|
|
2391
2279
|
cumulativeDurationMs: addDurationMs(
|
|
2392
|
-
|
|
2393
|
-
args.
|
|
2394
|
-
),
|
|
2395
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
2396
|
-
latestSessionRecord?.cumulativeUsage,
|
|
2397
|
-
args.currentUsage
|
|
2280
|
+
latest?.cumulativeDurationMs,
|
|
2281
|
+
args.durationMs
|
|
2398
2282
|
),
|
|
2399
|
-
|
|
2283
|
+
cumulativeUsage: addAgentTurnUsage(latest?.cumulativeUsage, args.usage),
|
|
2284
|
+
dispatchOutcome: args.dispatchOutcome ?? latest?.dispatchOutcome,
|
|
2285
|
+
errorMessage: args.errorMessage,
|
|
2286
|
+
resultMessageId: args.resultMessageId ?? latest?.resultMessageId
|
|
2400
2287
|
}),
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
sliceId: args.currentSliceId,
|
|
2405
|
-
state: "awaiting_resume"
|
|
2288
|
+
piMessages: args.messages,
|
|
2289
|
+
sliceId,
|
|
2290
|
+
state: args.mode
|
|
2406
2291
|
});
|
|
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
|
-
}
|
|
2292
|
+
});
|
|
2418
2293
|
}
|
|
2419
2294
|
|
|
2420
2295
|
// src/chat/state/conversation.ts
|
|
@@ -2923,9 +2798,9 @@ function clearPendingAuth(conversation, sessionId) {
|
|
|
2923
2798
|
}
|
|
2924
2799
|
async function abandonReplacedPendingAuth(args) {
|
|
2925
2800
|
if (args.previousPendingAuth && args.previousPendingAuth.sessionId !== args.nextPendingAuth.sessionId && args.conversationId) {
|
|
2926
|
-
await
|
|
2801
|
+
await abandonTurnRecord({
|
|
2927
2802
|
conversationId: args.conversationId,
|
|
2928
|
-
|
|
2803
|
+
turnId: args.previousPendingAuth.sessionId,
|
|
2929
2804
|
errorMessage: "Abandoned by a newer auth-blocked request in the same conversation."
|
|
2930
2805
|
});
|
|
2931
2806
|
}
|
|
@@ -3021,22 +2896,19 @@ export {
|
|
|
3021
2896
|
sanitizeAssistantText,
|
|
3022
2897
|
decideReply,
|
|
3023
2898
|
getAssistantReplyText,
|
|
3024
|
-
getAgentTurnSessionRecord,
|
|
3025
|
-
getAgentTurnSessionRecordForResume,
|
|
3026
|
-
recordAgentTurnSessionSummary,
|
|
3027
|
-
listAgentTurnSessionSummariesForConversation,
|
|
3028
|
-
abandonAgentTurnSessionRecord,
|
|
3029
|
-
failAgentTurnSessionRecord,
|
|
3030
|
-
persistWithRetry,
|
|
3031
2899
|
TurnSliceLimitExceededError,
|
|
3032
2900
|
buildTurnLimitResponse,
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
2901
|
+
persistWithRetry,
|
|
2902
|
+
getTurnRecord,
|
|
2903
|
+
getTurnRecordForResume,
|
|
2904
|
+
recordTurnSummary,
|
|
2905
|
+
listTurnSummaries,
|
|
2906
|
+
abandonTurnRecord,
|
|
2907
|
+
completeTurnRecord,
|
|
2908
|
+
failTurnRecord,
|
|
2909
|
+
continuableMessages,
|
|
2910
|
+
loadTurnCheckpoint,
|
|
2911
|
+
saveTurnCheckpoint,
|
|
3040
2912
|
canReusePendingAuthLink,
|
|
3041
2913
|
getConversationPendingAuth,
|
|
3042
2914
|
clearPendingAuth,
|