@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.
Files changed (104) hide show
  1. package/dist/{agent-hooks-GCUTGKY3.js → agent-hooks-SLJADYE5.js} +10 -10
  2. package/dist/api/conversations/detail.d.ts +1 -0
  3. package/dist/api/schema/conversation.d.ts +2 -0
  4. package/dist/api/schema.js +1 -1
  5. package/dist/api.js +40 -22
  6. package/dist/app.js +400 -461
  7. package/dist/{catalog-runtime-DWVYKDC6.js → catalog-runtime-RQIMSDPR.js} +5 -5
  8. package/dist/chat/agent/request.d.ts +1 -1
  9. package/dist/chat/agent/resume.d.ts +6 -18
  10. package/dist/chat/agent/skills.d.ts +0 -1
  11. package/dist/chat/agent/tools.d.ts +1 -2
  12. package/dist/chat/agent-dispatch/types.d.ts +1 -1
  13. package/dist/chat/app/conversation-work.d.ts +24 -0
  14. package/dist/chat/conversations/message-search.d.ts +32 -0
  15. package/dist/chat/conversations/projection.d.ts +11 -3
  16. package/dist/chat/conversations/sql/message-search.d.ts +4 -0
  17. package/dist/chat/conversations/store.d.ts +1 -1
  18. package/dist/chat/conversations/tool-source.d.ts +5 -0
  19. package/dist/chat/db.d.ts +2 -2
  20. package/dist/chat/runtime/agent-run-outcome.d.ts +1 -1
  21. package/dist/chat/runtime/reply-executor.d.ts +5 -5
  22. package/dist/chat/runtime/slack-resume.d.ts +12 -4
  23. package/dist/chat/slack/message/attachments.d.ts +2 -0
  24. package/dist/chat/slack/message/blocks.d.ts +2 -0
  25. package/dist/chat/slack/message/content.d.ts +18 -0
  26. package/dist/chat/slack/message/identity.d.ts +7 -0
  27. package/dist/chat/slack/tools/{conversation-search.d.ts → conversation-message-search.d.ts} +21 -14
  28. package/dist/chat/slack/tools/user-lookup.d.ts +2 -1
  29. package/dist/chat/state/locks.d.ts +28 -0
  30. package/dist/chat/task-execution/checkpoint.d.ts +84 -0
  31. package/dist/chat/task-execution/paused-turn.d.ts +29 -0
  32. package/dist/chat/task-execution/queue-signing.d.ts +1 -0
  33. package/dist/chat/task-execution/queue.d.ts +7 -1
  34. package/dist/chat/task-execution/slack-work.d.ts +1 -1
  35. package/dist/chat/task-execution/state.d.ts +4 -4
  36. package/dist/chat/task-execution/store.d.ts +2 -2
  37. package/dist/chat/task-execution/turn-cursor-keys.d.ts +6 -0
  38. package/dist/chat/{state/turn-session.d.ts → task-execution/turn-cursor.d.ts} +52 -51
  39. package/dist/chat/task-execution/turn-wake.d.ts +29 -0
  40. package/dist/chat/tools/{query-conversation-events.d.ts → search-conversation-events.d.ts} +3 -3
  41. package/dist/chat/tools/types.d.ts +1 -1
  42. package/dist/{chunk-SBZP2J5Q.js → chunk-37EVZP66.js} +48 -35
  43. package/dist/{chunk-A67GZN73.js → chunk-37JJDS4H.js} +1 -1
  44. package/dist/{chunk-BBCWYXHB.js → chunk-47OSH2ZO.js} +7 -7
  45. package/dist/{chunk-4X5KM7Z6.js → chunk-AISNSNDV.js} +14 -14
  46. package/dist/{chunk-RIFJUJSI.js → chunk-B63BF443.js} +1 -1
  47. package/dist/{chunk-SSDTQDYQ.js → chunk-BLETMLRX.js} +566 -268
  48. package/dist/{chunk-I44JB7HQ.js → chunk-F7REGDKO.js} +1 -1
  49. package/dist/{chunk-DVO5VKYQ.js → chunk-FEKQHPVA.js} +5 -5
  50. package/dist/{chunk-SUK7NC5A.js → chunk-FGHADGIC.js} +2 -2
  51. package/dist/{chunk-7KAW7UF4.js → chunk-FXDDDSPL.js} +4 -4
  52. package/dist/{chunk-BEGGFVFR.js → chunk-GDQEU4I7.js} +414 -542
  53. package/dist/{chunk-UDQMY6RS.js → chunk-GYEKOX7N.js} +43 -1
  54. package/dist/{chunk-W7JFV5SB.js → chunk-H6U2U3EP.js} +14 -17
  55. package/dist/{chunk-WTX2RFJG.js → chunk-HQE6EJS5.js} +1 -1
  56. package/dist/{chunk-IUFM5JWV.js → chunk-JAL3CH4G.js} +3 -0
  57. package/dist/{chunk-E7HCWOHG.js → chunk-JLMVCNXL.js} +2 -1
  58. package/dist/{chunk-PELZ2ZSY.js → chunk-JUA6VU2Z.js} +33 -63
  59. package/dist/{chunk-SHEEZSLY.js → chunk-LVW7BLLT.js} +2 -2
  60. package/dist/{chunk-GKA74DRA.js → chunk-M6ZWP4RI.js} +1 -1
  61. package/dist/{chunk-HNO2E5WB.js → chunk-MCFIXAV5.js} +1 -1
  62. package/dist/{chunk-3GU4PWA3.js → chunk-MKRIZ4VG.js} +29 -30
  63. package/dist/{chunk-QUZJDAQQ.js → chunk-OFPYOC3M.js} +93 -90
  64. package/dist/{chunk-ZF3N3JD6.js → chunk-PB222GIA.js} +1 -1
  65. package/dist/{chunk-PKH6762I.js → chunk-PH4Q3A6W.js} +1 -1
  66. package/dist/{chunk-STFCL3K4.js → chunk-QG7OOKIN.js} +1 -1
  67. package/dist/{chunk-WSOLZJJS.js → chunk-QSXZ42KX.js} +3 -3
  68. package/dist/{chunk-44RJWAND.js → chunk-SCGVWUZX.js} +6 -6
  69. package/dist/{chunk-CNTWB2SM.js → chunk-T5GXFFLA.js} +1 -1
  70. package/dist/{chunk-LDWNYCZ6.js → chunk-TCEJ7XXM.js} +1 -1
  71. package/dist/{chunk-QE3TICD4.js → chunk-V6SRLKUB.js} +2 -2
  72. package/dist/{chunk-54JMYQYS.js → chunk-W65V465O.js} +2 -2
  73. package/dist/{chunk-KCBZSJRU.js → chunk-WR76IVA5.js} +5 -2
  74. package/dist/{chunk-ZZCTGAIT.js → chunk-X22KCSCO.js} +4 -4
  75. package/dist/{chunk-HD6BHR25.js → chunk-X2WMBTS3.js} +32 -34
  76. package/dist/{chunk-KQBXT5B7.js → chunk-ZDGHGY74.js} +6 -6
  77. package/dist/cli/chat.js +32 -32
  78. package/dist/cli/check.js +6 -6
  79. package/dist/cli/plugins.js +13 -13
  80. package/dist/cli/snapshot-create.js +9 -9
  81. package/dist/cli/upgrade.js +7 -7
  82. package/dist/{db-EFLHTBU6.js → db-WWOMXWTT.js} +9 -9
  83. package/dist/instrumentation.js +4 -4
  84. package/dist/nitro.js +4 -4
  85. package/dist/{oauth-callback-server-RO3IQKWM.js → oauth-callback-server-A6ZZX74U.js} +25 -25
  86. package/dist/{runner-LOWW7JKA.js → runner-KSGB2UAV.js} +19 -20
  87. package/dist/{skills-ZLMUG7AE.js → skills-26QPWKVS.js} +6 -6
  88. package/dist/spawn-5LAJXZ2P.js +39 -0
  89. package/dist/{validation-MAKEZNTS.js → validation-OPSRGFGH.js} +6 -6
  90. package/dist/version.js +1 -1
  91. package/dist/{work-5OAGC3I4.js → work-VMSW7STJ.js} +17 -16
  92. package/dist/worker-2JNFB7C7.js +24 -0
  93. package/package.json +5 -5
  94. package/dist/chat/agent/session.d.ts +0 -19
  95. package/dist/chat/conversations/search.d.ts +0 -24
  96. package/dist/chat/conversations/sql/search.d.ts +0 -4
  97. package/dist/chat/runtime/agent-continue-runner.d.ts +0 -37
  98. package/dist/chat/services/agent-continue.d.ts +0 -29
  99. package/dist/chat/services/turn-session-record.d.ts +0 -164
  100. package/dist/chat/slack/legacy-attachments.d.ts +0 -4
  101. package/dist/chat/slack/message.d.ts +0 -12
  102. package/dist/chat/state/turn-session-keys.d.ts +0 -6
  103. package/dist/spawn-R3LUV6RF.js +0 -38
  104. package/dist/worker-KTGGRTTP.js +0 -24
@@ -21,18 +21,21 @@ import {
21
21
  instructionProvenanceFor,
22
22
  sanitizePostgresJson,
23
23
  withConversationEventLock
24
- } from "./chunk-SBZP2J5Q.js";
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
- getStateAdapter
31
- } from "./chunk-UDQMY6RS.js";
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-3GU4PWA3.js";
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-RIFJUJSI.js";
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
- function countMatchingPrefix(left, right) {
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 = countMatchingPrefix(
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 Error(
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/state/turn-session.ts
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/state/turn-session-keys.ts
1411
- var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
1412
- function agentTurnSessionKey(conversationId, sessionId) {
1413
- return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
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 agentTurnSessionConversationIndexKey(conversationId) {
1416
- return `${AGENT_TURN_SESSION_PREFIX}:conversation:${conversationId}:index`;
1477
+ function turnCursorMutationKey(conversationId, turnId) {
1478
+ return `${TURN_CURSOR_PREFIX}:mutation:${conversationId}:${turnId}`;
1417
1479
  }
1418
1480
 
1419
- // src/chat/state/turn-session.ts
1420
- var AGENT_TURN_SESSION_RESUME_TTL_MS = 24 * 60 * 60 * 1e3;
1421
- var AGENT_TURN_SESSION_TERMINAL_TTL_MS = 60 * 60 * 1e3;
1422
- function executionMetricsForSession(conversation, sessionId) {
1423
- return conversation?.executionMetrics?.runId === sessionId ? conversation.executionMetrics : void 0;
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 agentTurnSessionStatusSchema = z2.enum([
1512
+ var turnStatusSchema = z2.enum([
1431
1513
  "running",
1432
- "awaiting_resume",
1514
+ "paused",
1433
1515
  "completed",
1434
1516
  "failed",
1435
1517
  "abandoned"
1436
1518
  ]);
1437
- var agentTurnSurfaceSchema = z2.enum([
1519
+ var turnSurfaceSchema = z2.enum([
1438
1520
  "slack",
1439
1521
  "api",
1440
1522
  "scheduler",
1441
1523
  "internal"
1442
1524
  ]);
1443
- var agentTurnResumeReasonSchema = z2.enum([
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 agentTurnSessionSummarySchema = z2.object({
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: agentTurnResumeReasonSchema.optional(),
1458
- sessionId: z2.string().min(1),
1540
+ resumeReason: turnPauseReasonSchema.optional(),
1541
+ turnId: z2.string().min(1),
1459
1542
  sliceId: z2.number().int().nonnegative(),
1460
- state: agentTurnSessionStatusSchema,
1461
- surface: agentTurnSurfaceSchema.optional(),
1543
+ state: turnStatusSchema,
1544
+ surface: turnSurfaceSchema.optional(),
1462
1545
  updatedAtMs: nonNegativeNumberSchema
1463
1546
  }).strip();
1464
- var storedAgentTurnSessionRecordSchema = z2.object({
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: agentTurnResumeReasonSchema.optional(),
1555
+ resumeReason: turnPauseReasonSchema.optional(),
1472
1556
  resumedFromSliceId: z2.number().int().nonnegative().optional(),
1473
- sessionId: z2.string().min(1),
1557
+ turnId: z2.string().min(1),
1474
1558
  sliceId: z2.number().int().nonnegative(),
1475
1559
  startedAtMs: nonNegativeNumberSchema,
1476
- state: agentTurnSessionStatusSchema,
1477
- surface: agentTurnSurfaceSchema.optional(),
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.sessionId,
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 parseAgentTurnSessionRecord(value) {
1498
- return storedAgentTurnSessionRecordSchema.parse(value);
1581
+ function parseTurnRecord(value) {
1582
+ return storedTurnRecordSchema.parse(value);
1583
+ }
1584
+ function parseTurnSummary(value) {
1585
+ return storedTurnSummarySchema.parse(value);
1499
1586
  }
1500
- function parseAgentTurnSessionSummary(value) {
1501
- return agentTurnSessionSummarySchema.parse(value);
1587
+ function storeTurnSummary(summary) {
1588
+ return storedTurnSummarySchema.parse(summary);
1502
1589
  }
1503
- function agentTurnSessionRecordTtlMs(state, ttlMs) {
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 === "awaiting_resume" ? AGENT_TURN_SESSION_RESUME_TTL_MS : AGENT_TURN_SESSION_TERMINAL_TTL_MS;
1594
+ return state === "running" || state === "paused" ? TURN_CURSOR_RESUME_TTL_MS : TURN_CURSOR_TERMINAL_TTL_MS;
1508
1595
  }
1509
- function agentTurnSessionIndexTtlMs(ttlMs) {
1510
- return Math.max(1, ttlMs ?? AGENT_TURN_SESSION_RESUME_TTL_MS);
1596
+ function turnCursorIndexTtlMs(ttlMs) {
1597
+ return Math.max(1, ttlMs ?? TURN_CURSOR_RESUME_TTL_MS);
1511
1598
  }
1512
- function projectAgentTurnSessionSummary(record) {
1513
- return agentTurnSessionSummarySchema.parse(record);
1599
+ function projectTurnSummary(record) {
1600
+ return parseTurnSummary(record);
1514
1601
  }
1515
- async function appendAgentTurnSessionSummary(summary, ttlMs) {
1602
+ async function appendTurnSummary(summary, ttlMs) {
1516
1603
  const stateAdapter = getStateAdapter();
1517
1604
  await stateAdapter.appendToList(
1518
- agentTurnSessionConversationIndexKey(summary.conversationId),
1519
- agentTurnSessionSummarySchema.parse(summary),
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 materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true, runtimeMetadata) {
1560
- const restoredProjection = restoreVolatileContext && (stored.state === "running" || stored.state === "awaiting_resume") ? restoreRuntimeContext(piProjection, stored.runtimeContext) : piProjection;
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
- sessionId: stored.sessionId,
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: runtimeMetadata?.cumulativeDurationMs ?? 0,
1661
+ cumulativeDurationMs: runtimeMetrics?.cumulativeDurationMs ?? 0,
1574
1662
  ...definedProps({
1575
- channelName: runtimeMetadata?.channelName,
1576
- cumulativeUsage: runtimeMetadata?.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 getStoredAgentTurnSessionRecord(conversationId, sessionId) {
1713
+ async function getStoredTurnRecord(conversationId, turnId) {
1629
1714
  const stateAdapter = getStateAdapter();
1630
1715
  await stateAdapter.connect();
1631
- const value = await stateAdapter.get(
1632
- agentTurnSessionKey(conversationId, sessionId)
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 materializeStoredAgentTurnSessionRecord(conversationId, sessionId, followCurrentReplacement) {
1637
- const parsed = await getStoredAgentTurnSessionRecord(
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 === "awaiting_resume"
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 === "awaiting_resume") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
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 = executionMetricsForSession(conversation, sessionId);
1661
- return materializeAgentTurnSessionRecord(
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 getAgentTurnSessionRecord(conversationId, sessionId) {
1674
- return await materializeStoredAgentTurnSessionRecord(
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 getAgentTurnSessionRecordForResume(conversationId, sessionId) {
1681
- return await materializeStoredAgentTurnSessionRecord(
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
- sessionId: args.sessionId,
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 = storedAgentTurnSessionRecordSchema.parse(args.record);
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: { ...storedRecord, ...args.runtimeMetadata }
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
- agentTurnSessionKey(storedRecord.conversationId, storedRecord.sessionId),
1807
+ turnCursorKey(storedRecord.conversationId, storedRecord.turnId),
1729
1808
  storedRecord,
1730
1809
  args.ttlMs
1731
1810
  );
1732
- await appendAgentTurnSessionSummary(
1733
- projectAgentTurnSessionSummary(storedRecord),
1734
- agentTurnSessionIndexTtlMs(args.indexTtlMs)
1811
+ await args.fence();
1812
+ await appendTurnSummary(
1813
+ projectTurnSummary(storedRecord),
1814
+ turnCursorIndexTtlMs(args.indexTtlMs)
1735
1815
  );
1736
- return materializeAgentTurnSessionRecord(
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.runtimeMetadata
1824
+ args.runtimeMetrics
1745
1825
  );
1746
1826
  }
1747
- async function updateAgentTurnSessionState(args) {
1748
- const parsed = await getStoredAgentTurnSessionRecord(
1827
+ async function updateTurnState(args) {
1828
+ const parsed = await getStoredTurnRecord(
1749
1829
  args.existing.conversationId,
1750
- args.existing.sessionId
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: agentTurnSessionRecordTtlMs(args.state),
1759
- runtimeMetadata: {
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
- sessionId: args.existing.sessionId,
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 upsertAgentTurnSessionRecord(args) {
1797
- const existingRecord = await getStoredAgentTurnSessionRecord(
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.sessionId
1884
+ args.turnId
1800
1885
  );
1801
1886
  const conversation = await (args.conversationStore ?? getConversationStore()).get({
1802
1887
  conversationId: args.conversationId
1803
1888
  });
1804
- const executionMetrics = executionMetricsForSession(
1805
- conversation,
1806
- args.sessionId
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 = agentTurnSessionRecordTtlMs(args.state, args.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.sessionId
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
- // TODO(#1267): remove the temporary caller-to-SQL metadata write once the
1845
- // SQL-only turn-session readers have completed rollout.
1846
- runtimeMetadata: {
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
- sessionId: args.sessionId,
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 === "awaiting_resume" ? retainedRuntimeContext : void 0,
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 recordAgentTurnSessionSummary(args) {
1884
- const stored = await getStoredAgentTurnSessionRecord(
1963
+ async function recordTurnSummary(args) {
1964
+ await withTurnCursorMutation(
1885
1965
  args.conversationId,
1886
- args.sessionId
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 = executionMetricsForSession(
1893
- conversation,
1894
- args.sessionId
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 priorSummary = (await listAgentTurnSessionSummariesForConversation(args.conversationId)).find((summary2) => summary2.sessionId === args.sessionId);
1897
- const existing = stored ?? priorSummary;
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 = agentTurnSessionIndexTtlMs(args.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
- sessionId: args.sessionId,
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 appendAgentTurnSessionSummary(summary, ttlMs);
2026
+ await fence();
2027
+ await appendTurnSummary(summary, ttlMs);
1939
2028
  }
1940
- async function readAgentTurnSessionSummariesFromIndex(key) {
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 = parseAgentTurnSessionSummary(value);
2037
+ summary = parseTurnSummary(value);
1949
2038
  } catch {
1950
2039
  continue;
1951
2040
  }
1952
- const summaryKey = `${summary.conversationId}:${summary.sessionId}`;
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 listAgentTurnSessionSummariesForConversation(conversationId) {
1962
- return readAgentTurnSessionSummariesFromIndex(
1963
- agentTurnSessionConversationIndexKey(conversationId)
1964
- );
2050
+ async function listTurnSummaries(conversationId) {
2051
+ return readTurnSummaries(turnCursorIndexKey(conversationId));
1965
2052
  }
1966
- async function abandonAgentTurnSessionRecord(args) {
1967
- const existing = await getAgentTurnSessionRecord(
2053
+ async function abandonTurnRecord(args) {
2054
+ return await withTurnCursorMutation(
1968
2055
  args.conversationId,
1969
- args.sessionId
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 failAgentTurnSessionRecord(args) {
1981
- const existing = await getAgentTurnSessionRecord(
2071
+ async function completeTurnRecord(args) {
2072
+ return await withTurnCursorMutation(
1982
2073
  args.conversationId,
1983
- args.sessionId
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
- // src/chat/services/persist-retry.ts
1996
- var PERSIST_ATTEMPTS = 3;
1997
- async function persistWithRetry(persist) {
1998
- let lastError;
1999
- for (let attempt = 1; attempt <= PERSIST_ATTEMPTS; attempt += 1) {
2000
- try {
2001
- await persist();
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/services/turn-session-record.ts
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 resumableBoundary(messages, fallbackMessages) {
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(fallbackMessages ?? []);
2139
+ return trimTrailingAssistantMessages(fallback ?? []);
2074
2140
  }
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
- );
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
- resumedFromSessionRecord: hasAwaitingResumeRecord,
2085
- currentSliceId: hasAwaitingResumeRecord ? existingSessionRecord.sliceId : 1,
2086
- existingSessionRecord
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 persistRunningSessionRecord(args) {
2175
+ async function saveRunning(args) {
2090
2176
  if (args.messages.length === 0 || !isContinuablePiBoundary(args.messages)) {
2091
- return false;
2177
+ return void 0;
2092
2178
  }
2093
2179
  try {
2094
- const latestSessionRecord = await getAgentTurnSessionRecord(
2095
- args.conversationId,
2096
- args.sessionId
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: latestSessionRecord?.cumulativeDurationMs,
2122
- cumulativeUsage: latestSessionRecord?.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
- return true;
2128
- } catch (recordError) {
2129
- logSessionRecordError(
2130
- recordError,
2131
- "agent.turn.running_session_record.failed",
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 false;
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 persistAuthPauseSessionRecord(args) {
2219
- const nextSliceId = args.currentSliceId + 1;
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 latestSessionRecord = await getAgentTurnSessionRecord(
2222
- args.conversationId,
2223
- args.sessionId
2224
- );
2225
- const piMessages = resumableBoundary(
2226
- args.messages,
2227
- latestSessionRecord?.piMessages
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
- return await upsertAgentTurnSessionRecord({
2233
- ...sessionWriteContext(
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
- latestSessionRecord?.cumulativeDurationMs,
2254
- args.currentDurationMs
2221
+ latest?.cumulativeDurationMs,
2222
+ args.durationMs
2255
2223
  ),
2256
- cumulativeUsage: addAgentTurnUsage(
2257
- latestSessionRecord?.cumulativeUsage,
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.resumeReason
2227
+ piMessages: messages,
2228
+ resumeReason: args.reason
2328
2229
  };
2329
- if (nextSliceId > botConfig.maxSlicesPerTurn) {
2330
- return await upsertAgentTurnSessionRecord({
2331
- ...shared,
2332
- ...definedProps2({
2333
- resumedFromSliceId: latestSessionRecord?.resumedFromSliceId
2334
- }),
2335
- errorMessage: new TurnSliceLimitExceededError(
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 upsertAgentTurnSessionRecord({
2343
- ...shared,
2344
- errorMessage: args.errorMessage,
2345
- resumedFromSliceId: args.currentSliceId,
2241
+ return await upsertTurnRecord({
2242
+ ...base,
2243
+ ...definedProps2({
2244
+ resumedFromSliceId: keepSlice ? latest?.resumedFromSliceId : args.sliceId
2245
+ }),
2346
2246
  sliceId: nextSliceId,
2347
- state: "awaiting_resume"
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 persistYieldSessionRecord(args) {
2363
- try {
2364
- const piMessages = [...args.messages];
2365
- if (piMessages.length === 0 || !isContinuablePiBoundary(piMessages)) {
2366
- return void 0;
2367
- }
2368
- const latestSessionRecord = await getAgentTurnSessionRecord(
2369
- args.conversationId,
2370
- args.sessionId
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
- return await upsertAgentTurnSessionRecord({
2373
- ...sessionWriteContext(
2374
- {
2375
- actor: args.actor,
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
- latestSessionRecord?.cumulativeDurationMs,
2393
- args.currentDurationMs
2394
- ),
2395
- cumulativeUsage: addAgentTurnUsage(
2396
- latestSessionRecord?.cumulativeUsage,
2397
- args.currentUsage
2280
+ latest?.cumulativeDurationMs,
2281
+ args.durationMs
2398
2282
  ),
2399
- resumedFromSliceId: latestSessionRecord?.resumedFromSliceId
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
- errorMessage: args.errorMessage,
2402
- piMessages,
2403
- resumeReason: "yield",
2404
- sliceId: args.currentSliceId,
2405
- state: "awaiting_resume"
2288
+ piMessages: args.messages,
2289
+ sliceId,
2290
+ state: args.mode
2406
2291
  });
2407
- } catch (recordError) {
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 abandonAgentTurnSessionRecord({
2801
+ await abandonTurnRecord({
2927
2802
  conversationId: args.conversationId,
2928
- sessionId: args.previousPendingAuth.sessionId,
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
- loadTurnSessionRecord,
3034
- persistRunningSessionRecord,
3035
- persistCompletedSessionRecord,
3036
- completeDeliveredTurn,
3037
- persistAuthPauseSessionRecord,
3038
- persistContinuationSessionRecord,
3039
- persistYieldSessionRecord,
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,