@sentry/junior 0.105.0 → 0.107.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 (133) hide show
  1. package/dist/{agent-hooks-EOFSY2PD.js → agent-hooks-ICPIJAFY.js} +9 -7
  2. package/dist/api/conversations/detail.d.ts +1 -1
  3. package/dist/api/conversations/events.d.ts +14 -0
  4. package/dist/api/conversations/schema.d.ts +196 -463
  5. package/dist/api/schema.d.ts +2 -7
  6. package/dist/api/schema.js +10 -8
  7. package/dist/api.js +377 -810
  8. package/dist/app.js +2284 -2288
  9. package/dist/{catalog-runtime-5CVM2DOF.js → catalog-runtime-FOFNPR3P.js} +3 -2
  10. package/dist/chat/agent/prompt.d.ts +1 -1
  11. package/dist/chat/agent/request.d.ts +28 -29
  12. package/dist/chat/agent/resume.d.ts +7 -7
  13. package/dist/chat/agent/session.d.ts +5 -6
  14. package/dist/chat/agent/tools.d.ts +3 -3
  15. package/dist/chat/agent-dispatch/runner.d.ts +2 -0
  16. package/dist/chat/config.d.ts +3 -2
  17. package/dist/chat/conversations/history.d.ts +582 -167
  18. package/dist/chat/conversations/message-projection.d.ts +6 -0
  19. package/dist/chat/conversations/message-summaries.d.ts +9 -0
  20. package/dist/chat/conversations/messages.d.ts +13 -27
  21. package/dist/chat/conversations/projection.d.ts +30 -53
  22. package/dist/chat/conversations/provenance.d.ts +42 -0
  23. package/dist/chat/conversations/search.d.ts +2 -2
  24. package/dist/chat/conversations/sql/conversation-row.d.ts +1 -1
  25. package/dist/chat/conversations/sql/event-lock.d.ts +3 -0
  26. package/dist/chat/conversations/sql/history.d.ts +3 -3
  27. package/dist/chat/conversations/sql/privacy.d.ts +27 -0
  28. package/dist/chat/conversations/sql/purge.d.ts +2 -10
  29. package/dist/chat/conversations/sql/store.d.ts +0 -5
  30. package/dist/chat/conversations/store.d.ts +8 -13
  31. package/dist/chat/conversations/turn-lifecycle.d.ts +41 -0
  32. package/dist/chat/db.d.ts +4 -10
  33. package/dist/chat/model-profile.d.ts +10 -0
  34. package/dist/chat/oauth-flow.d.ts +0 -5
  35. package/dist/chat/pi/conversation-events.d.ts +33 -0
  36. package/dist/chat/pi/sql-model-usage.d.ts +10 -0
  37. package/dist/chat/plugins/agent-hooks.d.ts +4 -2
  38. package/dist/chat/resource-events/ingest.d.ts +0 -13
  39. package/dist/chat/runtime/delivered-turn-state.d.ts +1 -1
  40. package/dist/chat/runtime/generated-artifacts.d.ts +1 -1
  41. package/dist/chat/runtime/reply-executor.d.ts +2 -0
  42. package/dist/chat/runtime/slack-resume.d.ts +23 -28
  43. package/dist/chat/runtime/slack-runtime.d.ts +2 -0
  44. package/dist/chat/runtime/turn.d.ts +14 -1
  45. package/dist/chat/services/context-compaction-marker.d.ts +0 -1
  46. package/dist/chat/services/context-compaction.d.ts +1 -0
  47. package/dist/chat/services/conversation-memory.d.ts +9 -0
  48. package/dist/chat/services/plugin-auth-orchestration.d.ts +1 -4
  49. package/dist/chat/services/subscribed-decision.d.ts +1 -1
  50. package/dist/chat/services/turn-failure-response.d.ts +10 -3
  51. package/dist/chat/services/turn-limit.d.ts +6 -0
  52. package/dist/chat/services/turn-result.d.ts +7 -12
  53. package/dist/chat/services/{turn-reasoning-level.d.ts → turn-router.d.ts} +12 -8
  54. package/dist/chat/services/turn-session-record.d.ts +15 -17
  55. package/dist/chat/slack/adapter.d.ts +2 -2
  56. package/dist/chat/slack/errors.d.ts +2 -0
  57. package/dist/chat/slack/outbound.d.ts +0 -1
  58. package/dist/chat/slack/reply.d.ts +2 -8
  59. package/dist/chat/slack/tools/channel-capabilities.d.ts +2 -2
  60. package/dist/chat/slack/tools/{send-message.d.ts → send-files.d.ts} +27 -19
  61. package/dist/chat/state/conversation.d.ts +4 -4
  62. package/dist/chat/state/turn-id.d.ts +2 -0
  63. package/dist/chat/state/turn-session-keys.d.ts +6 -0
  64. package/dist/chat/state/turn-session.d.ts +11 -9
  65. package/dist/chat/task-execution/state.d.ts +6 -1
  66. package/dist/chat/task-execution/store.d.ts +1 -1
  67. package/dist/chat/tool-support/pi-tool-adapter.d.ts +1 -1
  68. package/dist/chat/tool-support/tool-execution-report.d.ts +1 -0
  69. package/dist/chat/tools/handoff/tool.d.ts +2 -2
  70. package/dist/chat/tools/sandbox/file-uploads.d.ts +28 -6
  71. package/dist/chat/tools/types.d.ts +6 -9
  72. package/dist/chat/tools/web/image-generate.d.ts +4 -6
  73. package/dist/{chunk-QDKRE3LS.js → chunk-4YF7Z6IA.js} +390 -270
  74. package/dist/{chunk-4PDVOHXP.js → chunk-AUUOHQAT.js} +2 -2
  75. package/dist/chunk-B2Z2H66D.js +250 -0
  76. package/dist/{chunk-QVTVCJR4.js → chunk-CQ7KSO2B.js} +75 -45
  77. package/dist/{chunk-MJSJAHBW.js → chunk-DY7TRYB5.js} +1 -1
  78. package/dist/{chunk-NJJFU6CP.js → chunk-EDLNHZH3.js} +883 -898
  79. package/dist/{chunk-O7ILGCMJ.js → chunk-IGHMVDWI.js} +134 -18
  80. package/dist/chunk-MU6HHZEN.js +99 -0
  81. package/dist/{chunk-BCULHGZK.js → chunk-NVOTGWYX.js} +458 -684
  82. package/dist/{chunk-AEWZ7F65.js → chunk-O24R4DCK.js} +2 -179
  83. package/dist/{chunk-CCFROYAV.js → chunk-PUAMO4PI.js} +115 -125
  84. package/dist/{chunk-VDHMFCDY.js → chunk-RMVOAJRL.js} +30 -13
  85. package/dist/{chunk-7G4J3CR7.js → chunk-SPUAJVVH.js} +6 -4
  86. package/dist/{chunk-M3UAVLEN.js → chunk-SS67LUOK.js} +43 -290
  87. package/dist/{chunk-FXFQNHRU.js → chunk-VFUK3X5B.js} +4 -2
  88. package/dist/{chunk-CCTCIISK.js → chunk-VH6KWKG2.js} +2 -2
  89. package/dist/chunk-VIXFG2VC.js +182 -0
  90. package/dist/{chunk-RMZ65YAA.js → chunk-YFQ7CQDE.js} +687 -21
  91. package/dist/{chunk-77LHNQK4.js → chunk-YIPD3L6L.js} +4 -2
  92. package/dist/{chunk-U26ZWZCN.js → chunk-YNP2ATQX.js} +6 -4
  93. package/dist/{chunk-WR7H7VLJ.js → chunk-YT72TOEK.js} +2 -2
  94. package/dist/cli/chat.js +21 -20
  95. package/dist/cli/check.js +4 -3
  96. package/dist/cli/plugins.js +11 -9
  97. package/dist/cli/snapshot-warmup.js +7 -5
  98. package/dist/cli/upgrade.js +2046 -105
  99. package/dist/db/schema/{agent-steps.d.ts → conversation-events.d.ts} +37 -21
  100. package/dist/db/schema.d.ts +28 -159
  101. package/dist/{db-PDMC67T3.js → db-DIGO4TGW.js} +6 -8
  102. package/dist/{runner-6A7MQRUE.js → runner-ACR2HAIC.js} +209 -101
  103. package/dist/{skills-722AXRV5.js → skills-ITNHBARB.js} +4 -3
  104. package/dist/{validation-EUI2UFRM.js → validation-AGQ46ZGV.js} +4 -3
  105. package/migrations/0005_conversation_events.sql +122 -0
  106. package/migrations/README.md +23 -10
  107. package/migrations/meta/0005_snapshot.json +948 -0
  108. package/migrations/meta/_journal.json +7 -0
  109. package/package.json +6 -6
  110. package/dist/api/conversations/activity.d.ts +0 -40
  111. package/dist/api/conversations/detail-projection.d.ts +0 -11
  112. package/dist/api/conversations/detail.query.d.ts +0 -3
  113. package/dist/api/conversations/subagent.d.ts +0 -10
  114. package/dist/api/conversations/transcript.d.ts +0 -28
  115. package/dist/chat/conversations/legacy-advisor-session.d.ts +0 -7
  116. package/dist/chat/conversations/legacy-import.d.ts +0 -38
  117. package/dist/chat/conversations/sql/legacy-history-import.d.ts +0 -55
  118. package/dist/chat/conversations/sql/messages.d.ts +0 -4
  119. package/dist/chat/conversations/visible-compactions.d.ts +0 -11
  120. package/dist/chat/conversations/visible-messages.d.ts +0 -47
  121. package/dist/chat/services/reply-delivery-plan.d.ts +0 -5
  122. package/dist/chat/state/session-log.d.ts +0 -384
  123. package/dist/chunk-RYDF4GOX.js +0 -1072
  124. package/dist/chunk-XIMUQ4IU.js +0 -147
  125. package/dist/db/schema/conversation-messages.d.ts +0 -153
  126. package/dist/handlers/github-webhook/check-suite.d.ts +0 -3
  127. package/dist/handlers/github-webhook/issue-comment.d.ts +0 -3
  128. package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +0 -3
  129. package/dist/handlers/github-webhook/pull-request-review.d.ts +0 -3
  130. package/dist/handlers/github-webhook/pull-request.d.ts +0 -3
  131. package/dist/handlers/github-webhook/resource.d.ts +0 -10
  132. package/dist/handlers/github-webhook.d.ts +0 -15
  133. package/dist/legacy-import-G333RHJZ.js +0 -18
@@ -1,78 +1,119 @@
1
1
  import {
2
- ensureLegacyConversationImport
3
- } from "./chunk-RYDF4GOX.js";
2
+ agentTurnUsageSchema
3
+ } from "./chunk-MU6HHZEN.js";
4
4
  import {
5
- usageSchema
6
- } from "./chunk-PDO5BLNM.js";
5
+ getStateAdapter
6
+ } from "./chunk-B2Z2H66D.js";
7
7
  import {
8
8
  contextProvenance,
9
- getAgentStepStore,
9
+ createSqlConversationEventStore,
10
+ getConversationEventStore,
10
11
  getConversationStore,
12
+ getSqlExecutor,
11
13
  instructionActors,
12
- instructionProvenanceFor
13
- } from "./chunk-BCULHGZK.js";
14
+ instructionProvenanceFor,
15
+ sanitizePostgresJson,
16
+ withConversationEventLock
17
+ } from "./chunk-NVOTGWYX.js";
14
18
  import {
15
- getStateAdapter
16
- } from "./chunk-M3UAVLEN.js";
19
+ retainRuntimeTurnContext,
20
+ stripRuntimeTurnContext
21
+ } from "./chunk-SS67LUOK.js";
17
22
  import {
18
- logWarn,
19
23
  toStoredSlackActor
20
- } from "./chunk-AEWZ7F65.js";
24
+ } from "./chunk-VIXFG2VC.js";
25
+ import {
26
+ logWarn
27
+ } from "./chunk-O24R4DCK.js";
28
+ import {
29
+ isRecord,
30
+ toOptionalNumber,
31
+ toOptionalString
32
+ } from "./chunk-OB42YVAE.js";
21
33
 
22
34
  // src/chat/conversations/projection.ts
23
35
  import { isDeepStrictEqual } from "util";
24
- function authorizationObservationMessage(entry, createdAtMs) {
25
- const label = entry.kind === "mcp" ? "MCP authorization" : "Authorization";
26
- return {
36
+
37
+ // src/chat/pi/messages.ts
38
+ import { z } from "zod";
39
+ var piMessageSchema = z.object({
40
+ role: z.string()
41
+ }).passthrough().transform((value) => value);
42
+ var piContentMessageSchema = z.object({
43
+ content: z.array(z.unknown()),
44
+ role: z.string().min(1)
45
+ }).passthrough().transform((value) => value);
46
+
47
+ // src/chat/pi/conversation-events.ts
48
+ function authorizationObservationMessage(data, createdAtMs) {
49
+ const label = data.kind === "mcp" ? "MCP authorization" : "Authorization";
50
+ return piMessageSchema.parse({
27
51
  role: "user",
28
52
  content: [
29
53
  {
30
54
  type: "text",
31
- text: `${label} completed for provider "${entry.provider}". Continue the blocked request and retry the provider operation if needed.`
55
+ text: `${label} completed for provider "${data.provider}". Continue the blocked request and retry the provider operation if needed.`
32
56
  }
33
57
  ],
34
58
  timestamp: createdAtMs
35
- };
59
+ });
36
60
  }
37
- function piEntryProvenance(entry) {
38
- return entry.provenance ?? contextProvenance;
61
+ function messageEventProvenance(data) {
62
+ return data.provenance ?? contextProvenance;
39
63
  }
40
- function projectSteps(steps, opts) {
64
+ function durableMessages(message) {
65
+ return stripRuntimeTurnContext([piMessageSchema.parse(message)]);
66
+ }
67
+ function projectConversationEvents(events, options) {
41
68
  const messages = [];
42
69
  const provenance = [];
43
70
  const seqs = [];
44
71
  let modelProfile = "standard";
45
72
  let modelId;
46
- for (const step of steps) {
47
- if (opts?.maxSeq !== void 0 && step.seq > opts.maxSeq) {
48
- break;
73
+ for (const event of events) {
74
+ if (options?.maxSeq !== void 0 && event.seq > options.maxSeq) break;
75
+ if (event.data.type === "unknown") {
76
+ throw new Error(
77
+ `Unsupported conversation event "${event.data.originalType}" at seq ${event.seq} (schema version ${event.schemaVersion})`
78
+ );
49
79
  }
50
- if (step.entry.type === "context_epoch_started") {
51
- modelProfile = step.entry.modelProfile ?? "standard";
52
- modelId = step.entry.modelId;
80
+ if (event.data.type === "compaction" || event.data.type === "handoff" || event.data.type === "rollback") {
81
+ modelProfile = event.data.modelProfile;
82
+ modelId = event.data.modelId;
83
+ for (const replacement of event.data.replacementHistory) {
84
+ for (const message of durableMessages(replacement.message)) {
85
+ messages.push(message);
86
+ provenance.push(replacement.provenance ?? contextProvenance);
87
+ seqs.push(replacement.sourceEventSeq ?? event.seq);
88
+ }
89
+ }
53
90
  continue;
54
91
  }
55
- if (step.entry.type === "pi_message") {
56
- messages.push(step.entry.message);
57
- provenance.push(piEntryProvenance(step.entry));
58
- seqs.push(step.seq);
92
+ if (event.data.type === "agent_step") {
93
+ for (const message of durableMessages(event.data.message)) {
94
+ messages.push(message);
95
+ provenance.push(messageEventProvenance(event.data));
96
+ seqs.push(event.seq);
97
+ }
59
98
  continue;
60
99
  }
61
- if (step.entry.type === "authorization_completed") {
100
+ if (event.data.type === "authorization_completed") {
62
101
  messages.push(
63
- authorizationObservationMessage(step.entry, step.createdAtMs)
102
+ authorizationObservationMessage(event.data, event.createdAtMs)
64
103
  );
65
104
  provenance.push(contextProvenance);
66
- seqs.push(step.seq);
105
+ seqs.push(event.seq);
67
106
  }
68
107
  }
69
108
  return { messages, provenance, seqs, modelProfile, modelId };
70
109
  }
71
- function connectedMcpProvidersFromSteps(steps) {
110
+
111
+ // src/chat/conversations/projection.ts
112
+ function connectedMcpProvidersFromEvents(events) {
72
113
  const providers = /* @__PURE__ */ new Set();
73
- for (const step of steps) {
74
- if (step.entry.type === "mcp_provider_connected") {
75
- providers.add(step.entry.provider);
114
+ for (const event of events) {
115
+ if (event.data.type === "mcp_provider_connected") {
116
+ providers.add(event.data.provider);
76
117
  }
77
118
  }
78
119
  return [...providers].sort((left, right) => left.localeCompare(right));
@@ -89,6 +130,11 @@ function countMatchingPrefix(left, right) {
89
130
  }
90
131
  return limit;
91
132
  }
133
+ function normalizeDurableMessage(message) {
134
+ return piMessageSchema.parse(
135
+ JSON.parse(JSON.stringify(sanitizePostgresJson(message)))
136
+ );
137
+ }
92
138
  function resolveCommitProvenance(args) {
93
139
  if (args.explicitProvenance) {
94
140
  if (args.explicitProvenance.length !== args.nextMessages.length) {
@@ -127,150 +173,136 @@ function resolveCommitProvenance(args) {
127
173
  }
128
174
  return provenance;
129
175
  }
130
- async function importLegacyIfNeeded(args) {
131
- await ensureLegacyConversationImport({ conversationId: args.conversationId });
132
- }
133
176
  async function loadProjection(args) {
134
- await importLegacyIfNeeded(args);
135
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
136
- return projectSteps(steps).messages;
177
+ const events = await getConversationEventStore().loadCurrentHistory(
178
+ args.conversationId
179
+ );
180
+ return projectConversationEvents(events).messages;
137
181
  }
138
182
  async function loadConversationProjection(args) {
139
- await importLegacyIfNeeded(args);
140
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
141
- const { messages, provenance, modelProfile, modelId } = projectSteps(steps);
142
- return { messages, provenance, modelProfile, modelId };
183
+ const events = await getConversationEventStore().loadCurrentHistory(
184
+ args.conversationId
185
+ );
186
+ return projectConversationEvents(events);
143
187
  }
144
188
  async function openConversationProjection(args) {
145
- await importLegacyIfNeeded(args);
146
- const stepStore = getAgentStepStore();
147
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
148
- const projection = projectSteps(steps);
149
- if (steps.some(
150
- (step) => step.entry.type === "context_epoch_started" || step.entry.type === "pi_message"
151
- )) {
152
- return projection;
153
- }
154
- await stepStore.startEpoch(args.conversationId, {
155
- reason: "initial",
156
- modelProfile: "standard",
157
- modelId: args.modelId,
158
- messages: []
159
- });
160
- return {
161
- messages: projection.messages,
162
- provenance: projection.provenance,
163
- modelProfile: "standard",
164
- modelId: args.modelId
165
- };
189
+ const events = await getConversationEventStore().loadCurrentHistory(
190
+ args.conversationId
191
+ );
192
+ return projectConversationEvents(events);
166
193
  }
167
194
  async function loadTurnProjection(args) {
168
- await importLegacyIfNeeded(args);
169
- const stepStore = getAgentStepStore();
195
+ const eventStore = getConversationEventStore();
170
196
  if (args.committedSeq < 0) {
171
- return projectSteps(await stepStore.loadCurrentEpoch(args.conversationId));
197
+ return projectConversationEvents(
198
+ await eventStore.loadCurrentHistory(args.conversationId)
199
+ );
172
200
  }
173
- const history = await stepStore.loadHistory(args.conversationId);
174
- const committedStep = history.find((step) => step.seq === args.committedSeq);
175
- if (!committedStep) {
201
+ const historyEvents = await eventStore.loadHistoryContaining(
202
+ args.conversationId,
203
+ args.committedSeq,
204
+ args.includeTail ? void 0 : args.committedSeq
205
+ );
206
+ if (!historyEvents) {
176
207
  return void 0;
177
208
  }
178
- const epochSteps = history.filter(
179
- (step) => step.contextEpoch === committedStep.contextEpoch
180
- );
181
- return args.includeTail ? projectSteps(epochSteps) : projectSteps(epochSteps, { maxSeq: args.committedSeq });
209
+ return projectConversationEvents(historyEvents);
182
210
  }
183
211
  async function loadConnectedMcpProviders(args) {
184
- await importLegacyIfNeeded(args);
185
- const steps = await getAgentStepStore().loadCurrentEpoch(args.conversationId);
186
- return connectedMcpProvidersFromSteps(steps);
212
+ const events = await getConversationEventStore().loadCurrentHistory(
213
+ args.conversationId
214
+ );
215
+ return connectedMcpProvidersFromEvents(events);
187
216
  }
188
217
  function messageTimestamp(message) {
189
218
  const timestamp = message.timestamp;
190
219
  return typeof timestamp === "number" ? timestamp : Date.now();
191
220
  }
192
221
  async function commitMessages(args) {
193
- const stepStore = getAgentStepStore();
194
- const currentSteps = await stepStore.loadCurrentEpoch(args.conversationId);
195
- const existing = projectSteps(currentSteps);
196
- const matchingPrefix = countMatchingPrefix(existing.messages, args.messages);
197
- const nextProvenance = resolveCommitProvenance({
198
- existing,
199
- nextMessages: args.messages,
222
+ const executor = args.executor ?? getSqlExecutor();
223
+ return await withConversationEventLock(
224
+ executor,
225
+ args.conversationId,
226
+ async () => executor.transaction(
227
+ async () => await commitMessagesLocked(args, executor)
228
+ )
229
+ );
230
+ }
231
+ async function commitMessagesLocked(args, executor) {
232
+ const eventStore = createSqlConversationEventStore(executor);
233
+ const currentEvents = await eventStore.loadCurrentHistory(
234
+ args.conversationId
235
+ );
236
+ const current = projectConversationEvents(currentEvents);
237
+ const nextLocalMessages = stripRuntimeTurnContext(args.messages).map(
238
+ normalizeDurableMessage
239
+ );
240
+ const matchingPrefix = countMatchingPrefix(
241
+ current.messages,
242
+ nextLocalMessages
243
+ );
244
+ const nextLocalProvenance = resolveCommitProvenance({
245
+ existing: current,
246
+ nextMessages: nextLocalMessages,
200
247
  matchingPrefix,
201
248
  ...args.provenance ? { explicitProvenance: args.provenance } : {},
202
249
  ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {},
203
250
  ...args.newMessageProvenance ? { newMessageProvenance: args.newMessageProvenance } : {}
204
251
  });
205
- if (currentSteps.length === 0) {
206
- await stepStore.startEpoch(args.conversationId, {
207
- reason: "initial",
208
- modelProfile: "standard",
209
- modelId: args.modelId,
210
- messages: args.messages.map((message, index) => ({
211
- message,
212
- createdAtMs: messageTimestamp(message),
213
- provenance: nextProvenance[index]
214
- }))
215
- });
216
- } else if (matchingPrefix === existing.messages.length) {
217
- const newMessages = args.messages.slice(matchingPrefix);
218
- await stepStore.append(
252
+ if (matchingPrefix === current.messages.length) {
253
+ const newMessages = nextLocalMessages.slice(matchingPrefix);
254
+ await eventStore.append(
219
255
  args.conversationId,
220
256
  newMessages.map((message, index) => ({
221
- entry: {
222
- type: "pi_message",
257
+ data: {
258
+ type: "agent_step",
223
259
  message,
224
- provenance: nextProvenance[matchingPrefix + index]
260
+ provenance: nextLocalProvenance[matchingPrefix + index]
225
261
  },
226
262
  createdAtMs: messageTimestamp(message)
227
263
  }))
228
264
  );
229
265
  } else {
230
- await stepStore.startEpoch(args.conversationId, {
231
- reason: "rollback",
232
- modelProfile: existing.modelProfile,
233
- modelId: args.modelId,
234
- messages: args.messages.map((message, index) => ({
235
- message,
236
- createdAtMs: messageTimestamp(message),
237
- provenance: nextProvenance[index]
238
- }))
239
- });
266
+ throw new Error(
267
+ `Agent history for ${args.conversationId} changed before its committed boundary`
268
+ );
240
269
  }
241
- const committed = projectSteps(
242
- await stepStore.loadCurrentEpoch(args.conversationId)
270
+ const committedEvents = await eventStore.loadCurrentHistory(
271
+ args.conversationId
243
272
  );
273
+ const committed = projectConversationEvents(committedEvents);
244
274
  return {
245
- committedSeq: committed.seqs.at(-1) ?? -1,
275
+ committedSeq: committedEvents.at(-1)?.seq ?? -1,
276
+ historyVersion: committedEvents.at(-1)?.historyVersion ?? 0,
246
277
  messageSeqs: committed.seqs,
247
- provenance: nextProvenance
278
+ messages: nextLocalMessages,
279
+ provenance: nextLocalProvenance
248
280
  };
249
281
  }
250
282
  async function recordMcpProviderConnected(args) {
251
- const stepStore = getAgentStepStore();
252
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
253
- if (connectedMcpProvidersFromSteps(steps).includes(args.provider)) {
283
+ const eventStore = getConversationEventStore();
284
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
285
+ if (connectedMcpProvidersFromEvents(events).includes(args.provider)) {
254
286
  return;
255
287
  }
256
- await stepStore.append(args.conversationId, [
288
+ await eventStore.append(args.conversationId, [
257
289
  {
258
- entry: { type: "mcp_provider_connected", provider: args.provider },
290
+ data: { type: "mcp_provider_connected", provider: args.provider },
259
291
  createdAtMs: Date.now()
260
292
  }
261
293
  ]);
262
294
  }
263
295
  async function recordAuthorizationRequested(args) {
264
- const stepStore = getAgentStepStore();
265
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
266
- if (steps.some(
267
- (step) => step.entry.type === "authorization_requested" && step.entry.authorizationId === args.authorizationId
296
+ const eventStore = getConversationEventStore();
297
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
298
+ if (events.some(
299
+ (event) => event.data.type === "authorization_requested" && event.data.authorizationId === args.authorizationId
268
300
  )) {
269
301
  return;
270
302
  }
271
- await stepStore.append(args.conversationId, [
303
+ await eventStore.append(args.conversationId, [
272
304
  {
273
- entry: {
305
+ data: {
274
306
  type: "authorization_requested",
275
307
  kind: args.kind,
276
308
  provider: args.provider,
@@ -283,16 +315,16 @@ async function recordAuthorizationRequested(args) {
283
315
  ]);
284
316
  }
285
317
  async function recordAuthorizationCompleted(args) {
286
- const stepStore = getAgentStepStore();
287
- const steps = await stepStore.loadCurrentEpoch(args.conversationId);
288
- if (steps.some(
289
- (step) => step.entry.type === "authorization_completed" && step.entry.authorizationId === args.authorizationId
318
+ const eventStore = getConversationEventStore();
319
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
320
+ if (events.some(
321
+ (event) => event.data.type === "authorization_completed" && event.data.authorizationId === args.authorizationId
290
322
  )) {
291
323
  return;
292
324
  }
293
- await stepStore.append(args.conversationId, [
325
+ await eventStore.append(args.conversationId, [
294
326
  {
295
- entry: {
327
+ data: {
296
328
  type: "authorization_completed",
297
329
  kind: args.kind,
298
330
  provider: args.provider,
@@ -304,107 +336,132 @@ async function recordAuthorizationCompleted(args) {
304
336
  ]);
305
337
  }
306
338
  async function recordToolExecutionStarted(args) {
307
- await getAgentStepStore().append(args.conversationId, [
339
+ await getConversationEventStore().append(args.conversationId, [
308
340
  {
309
- entry: {
341
+ data: {
310
342
  type: "tool_execution_started",
311
343
  toolCallId: args.toolCallId,
312
- toolName: args.toolName,
313
- ...args.args !== void 0 ? { args: args.args } : {}
344
+ toolName: args.toolName
314
345
  },
315
346
  createdAtMs: args.createdAtMs ?? Date.now()
316
347
  }
317
348
  ]);
318
349
  }
319
350
 
320
- // src/chat/usage.ts
321
- var agentTurnUsageSchema = usageSchema;
322
- var COMPONENT_USAGE_FIELDS = [
323
- "inputTokens",
324
- "outputTokens",
325
- "cachedInputTokens",
326
- "cacheCreationTokens"
327
- ];
328
- var COST_COMPONENT_FIELDS = [
329
- "input",
330
- "output",
331
- "cacheRead",
332
- "cacheWrite"
333
- ];
334
- function hasAgentTurnUsage(usage) {
335
- return Boolean(
336
- usage && (Object.entries(usage).some(
337
- ([field, value]) => field !== "cost" && typeof value === "number" && Number.isFinite(value)
338
- ) || Object.values(usage.cost ?? {}).some(
339
- (value) => typeof value === "number" && Number.isFinite(value)
340
- ))
341
- );
342
- }
343
- function getFiniteCount(value) {
344
- return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
345
- }
346
- function getFiniteCost(value) {
347
- return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : void 0;
351
+ // src/chat/state/conversation.ts
352
+ function defaultConversationState() {
353
+ const nowMs = Date.now();
354
+ return {
355
+ schemaVersion: 1,
356
+ messages: [],
357
+ compactions: [],
358
+ backfill: {},
359
+ processing: {},
360
+ stats: {
361
+ estimatedContextTokens: 0,
362
+ totalMessageCount: 0,
363
+ compactedMessageCount: 0,
364
+ updatedAtMs: nowMs
365
+ },
366
+ vision: {
367
+ byFileId: {}
368
+ }
369
+ };
348
370
  }
349
- function addCost(left, right) {
350
- return Math.round(((left ?? 0) + right) * 1e12) / 1e12;
371
+ function coercePendingAuthState(value) {
372
+ if (!isRecord(value)) {
373
+ return void 0;
374
+ }
375
+ const kind = value.kind;
376
+ const provider = toOptionalString(value.provider);
377
+ const actorId = toOptionalString(value.actorId);
378
+ const authSessionId = toOptionalString(value.authSessionId);
379
+ const scope = toOptionalString(value.scope);
380
+ const sessionId = toOptionalString(value.sessionId);
381
+ const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
382
+ if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || kind === "mcp" && !authSessionId || !sessionId || typeof linkSentAtMs !== "number") {
383
+ return void 0;
384
+ }
385
+ const base = {
386
+ provider,
387
+ actorId,
388
+ ...scope ? { scope } : {},
389
+ sessionId,
390
+ linkSentAtMs
391
+ };
392
+ return kind === "mcp" ? { ...base, authSessionId, kind } : { ...base, kind };
351
393
  }
352
- function getComponentTotal(usage) {
353
- let total;
354
- for (const field of COMPONENT_USAGE_FIELDS) {
355
- const value = getFiniteCount(usage[field]);
356
- if (value === void 0) continue;
357
- total = (total ?? 0) + value;
394
+ function coerceThreadConversationState(value) {
395
+ if (!isRecord(value)) {
396
+ return defaultConversationState();
358
397
  }
359
- return total;
360
- }
361
- function addAgentTurnUsage(...usages) {
362
- const components = {};
363
- let componentTotal;
364
- let totalOnlyTokens;
365
- let reasoningTokens;
366
- const cost = {};
367
- for (const usage of usages) {
368
- if (!usage) continue;
369
- const reasoning = getFiniteCount(usage.reasoningTokens);
370
- if (reasoning !== void 0) {
371
- reasoningTokens = (reasoningTokens ?? 0) + reasoning;
372
- }
373
- if (usage.cost) {
374
- for (const field of [...COST_COMPONENT_FIELDS, "total"]) {
375
- const value = getFiniteCost(usage.cost[field]);
376
- if (value === void 0) continue;
377
- cost[field] = addCost(cost[field], value);
378
- }
398
+ const root = value;
399
+ const rawConversation = isRecord(root.conversation) ? root.conversation : {};
400
+ const base = defaultConversationState();
401
+ const messages = [];
402
+ const compactions = [];
403
+ const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
404
+ const backfill = {
405
+ completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
406
+ source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
407
+ };
408
+ const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
409
+ const processing = {
410
+ activeTurnId: toOptionalString(rawProcessing.activeTurnId),
411
+ lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
412
+ pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
413
+ };
414
+ const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
415
+ const stats = {
416
+ estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
417
+ totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
418
+ compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
419
+ updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
420
+ };
421
+ const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
422
+ const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
423
+ const byFileId = {};
424
+ for (const [fileId, value2] of Object.entries(rawVisionByFileId)) {
425
+ if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
426
+ if (!isRecord(value2)) continue;
427
+ const summary = toOptionalString(value2.summary);
428
+ const analyzedAtMs = toOptionalNumber(value2.analyzedAtMs);
429
+ if (!summary || !analyzedAtMs) continue;
430
+ byFileId[fileId] = {
431
+ summary,
432
+ analyzedAtMs
433
+ };
434
+ }
435
+ return {
436
+ schemaVersion: 1,
437
+ messages,
438
+ compactions,
439
+ backfill,
440
+ processing,
441
+ stats,
442
+ vision: {
443
+ backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
444
+ byFileId
379
445
  }
380
- const usageComponentTotal = getComponentTotal(usage);
381
- if (usageComponentTotal !== void 0) {
382
- componentTotal = (componentTotal ?? 0) + usageComponentTotal;
383
- for (const field of COMPONENT_USAGE_FIELDS) {
384
- const value = getFiniteCount(usage[field]);
385
- if (value === void 0) continue;
386
- components[field] = (components[field] ?? 0) + value;
446
+ };
447
+ }
448
+ function buildConversationStatePatch(conversation) {
449
+ const {
450
+ compactions: _compactions,
451
+ messages: _messages,
452
+ ...scratch
453
+ } = conversation;
454
+ return {
455
+ conversation: {
456
+ ...scratch,
457
+ schemaVersion: 1,
458
+ stats: {
459
+ ...conversation.stats,
460
+ totalMessageCount: conversation.messages.length,
461
+ updatedAtMs: Date.now()
387
462
  }
388
- continue;
389
- }
390
- const totalTokens = getFiniteCount(usage.totalTokens);
391
- if (totalTokens !== void 0) {
392
- totalOnlyTokens = (totalOnlyTokens ?? 0) + totalTokens;
393
463
  }
394
- }
395
- if (totalOnlyTokens !== void 0) {
396
- return {
397
- totalTokens: totalOnlyTokens + (componentTotal ?? 0),
398
- ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
399
- ...Object.keys(cost).length > 0 ? { cost } : {}
400
- };
401
- }
402
- const result = {
403
- ...components,
404
- ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
405
- ...Object.keys(cost).length > 0 ? { cost } : {}
406
464
  };
407
- return hasAgentTurnUsage(result) ? result : void 0;
408
465
  }
409
466
 
410
467
  // src/chat/state/turn-session.ts
@@ -414,67 +471,74 @@ import {
414
471
  destinationSchema,
415
472
  sourceSchema
416
473
  } from "@sentry/junior-plugin-api";
417
- import { z } from "zod";
474
+ import { z as z2 } from "zod";
475
+
476
+ // src/chat/state/turn-session-keys.ts
418
477
  var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
478
+ function agentTurnSessionKey(conversationId, sessionId) {
479
+ return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
480
+ }
481
+ function agentTurnSessionConversationIndexKey(conversationId) {
482
+ return `${AGENT_TURN_SESSION_PREFIX}:conversation:${conversationId}:index`;
483
+ }
484
+
485
+ // src/chat/state/turn-session.ts
419
486
  var AGENT_TURN_SESSION_INDEX_KEY = `${AGENT_TURN_SESSION_PREFIX}:index`;
420
487
  var AGENT_TURN_SESSION_INDEX_MAX_LENGTH = 5e3;
421
488
  var AGENT_TURN_SESSION_INDEX_READ_CONCURRENCY = 25;
422
489
  var AGENT_TURN_SESSION_TTL_MS = THREAD_STATE_TTL_MS;
423
- var agentTurnSessionStatusSchema = z.enum([
490
+ var agentTurnSessionStatusSchema = z2.enum([
424
491
  "running",
425
492
  "awaiting_resume",
426
493
  "completed",
427
494
  "failed",
428
495
  "abandoned"
429
496
  ]);
430
- var agentTurnSurfaceSchema = z.enum([
497
+ var agentTurnSurfaceSchema = z2.enum([
431
498
  "slack",
432
499
  "api",
433
500
  "scheduler",
434
501
  "internal"
435
502
  ]);
436
- var agentTurnResumeReasonSchema = z.enum([
503
+ var agentTurnResumeReasonSchema = z2.enum([
437
504
  "timeout",
438
505
  "auth",
439
- "yield"
506
+ "yield",
507
+ "retry"
440
508
  ]);
441
- var nonNegativeNumberSchema = z.number().finite().nonnegative();
442
- var seqCursorSchema = z.number().int().min(-1);
443
- var agentTurnSessionSummarySchema = z.object({
444
- channelName: z.string().min(1).optional(),
445
- version: z.number().int().nonnegative(),
446
- conversationId: z.string().min(1),
509
+ var nonNegativeNumberSchema = z2.number().finite().nonnegative();
510
+ var seqCursorSchema = z2.number().int().min(-1);
511
+ var agentTurnSessionSummarySchema = z2.object({
512
+ channelName: z2.string().min(1).optional(),
513
+ version: z2.number().int().nonnegative(),
514
+ conversationId: z2.string().min(1),
447
515
  cumulativeDurationMs: nonNegativeNumberSchema,
448
516
  cumulativeUsage: agentTurnUsageSchema.optional(),
449
517
  destination: destinationSchema.optional(),
450
518
  source: sourceSchema.optional(),
451
519
  lastProgressAtMs: nonNegativeNumberSchema,
452
- loadedSkillNames: z.array(z.string()).optional(),
453
- modelId: z.string().min(1).optional(),
454
- reasoningLevel: z.string().min(1).optional(),
520
+ loadedSkillNames: z2.array(z2.string()).optional(),
521
+ modelId: z2.string().min(1).optional(),
522
+ reasoningLevel: z2.string().min(1).optional(),
455
523
  actor: actorSchema.optional(),
456
524
  resumeReason: agentTurnResumeReasonSchema.optional(),
457
- resumedFromSliceId: z.number().int().nonnegative().optional(),
458
- sessionId: z.string().min(1),
459
- sliceId: z.number().int().nonnegative(),
525
+ resumedFromSliceId: z2.number().int().nonnegative().optional(),
526
+ sessionId: z2.string().min(1),
527
+ sliceId: z2.number().int().nonnegative(),
460
528
  startedAtMs: nonNegativeNumberSchema,
461
529
  state: agentTurnSessionStatusSchema,
462
530
  surface: agentTurnSurfaceSchema.optional(),
463
- traceId: z.string().optional(),
531
+ traceId: z2.string().optional(),
464
532
  updatedAtMs: nonNegativeNumberSchema
465
533
  }).strict();
466
534
  var storedAgentTurnSessionRecordSchema = agentTurnSessionSummarySchema.extend({
467
- actors: z.array(actorSchema).optional(),
535
+ actors: z2.array(actorSchema).optional(),
468
536
  committedSeq: seqCursorSchema,
469
- errorMessage: z.string().optional(),
470
- turnStartSeq: seqCursorSchema.optional()
537
+ historyVersion: z2.number().int().nonnegative().optional(),
538
+ errorMessage: z2.string().optional(),
539
+ turnStartSeq: seqCursorSchema.optional(),
540
+ runtimeContext: z2.array(piMessageSchema).optional()
471
541
  }).strict();
472
- function agentTurnSessionKey(conversationId, sessionId) {
473
- return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
474
- }
475
- function agentTurnSessionConversationIndexKey(conversationId) {
476
- return `${AGENT_TURN_SESSION_PREFIX}:conversation:${conversationId}:index`;
477
- }
478
542
  function conversationExecutionFromSummary(summary) {
479
543
  const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
480
544
  return {
@@ -490,13 +554,6 @@ function parseAgentTurnSessionRecord(value) {
490
554
  return storedAgentTurnSessionRecordSchema.parse(value);
491
555
  }
492
556
  function parseAgentTurnSessionSummary(value) {
493
- if (typeof value === "object" && value !== null && !Array.isArray(value) && "requester" in value) {
494
- const { requester, ...summary } = value;
495
- return agentTurnSessionSummarySchema.parse({
496
- ...summary,
497
- ...summary.actor === void 0 ? { actor: requester } : {}
498
- });
499
- }
500
557
  return agentTurnSessionSummarySchema.parse(value);
501
558
  }
502
559
  async function appendAgentTurnSessionSummary(summary, ttlMs) {
@@ -543,7 +600,8 @@ async function recordConversationActivityMetadata(args) {
543
600
  visibility: args.destinationVisibility
544
601
  });
545
602
  }
546
- function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessageIndex) {
603
+ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true) {
604
+ const piMessages = restoreVolatileContext && (stored.state === "running" || stored.state === "awaiting_resume") ? restoreRuntimeContext(piProjection.messages, stored.runtimeContext) : piProjection.messages;
547
605
  return {
548
606
  version: stored.version,
549
607
  ...stored.channelName ? { channelName: stored.channelName } : {},
@@ -554,7 +612,7 @@ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessag
554
612
  startedAtMs: stored.startedAtMs,
555
613
  lastProgressAtMs: stored.lastProgressAtMs,
556
614
  updatedAtMs: stored.updatedAtMs,
557
- piMessages: piProjection.messages,
615
+ piMessages,
558
616
  piMessageProvenance: piProjection.provenance,
559
617
  actors: stored.actors ?? instructionActors(piProjection.provenance),
560
618
  cumulativeDurationMs: stored.cumulativeDurationMs,
@@ -573,6 +631,30 @@ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessag
573
631
  ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
574
632
  };
575
633
  }
634
+ function restoreRuntimeContext(messages, runtimeContext) {
635
+ if (!runtimeContext || runtimeContext.length === 0) return messages;
636
+ const restored = [...messages];
637
+ for (const runtimeMessage of runtimeContext) {
638
+ const runtime = runtimeMessage;
639
+ const targetIndex = restored.findIndex((message) => {
640
+ const candidate = message;
641
+ return candidate.role === "user" && candidate.timestamp === runtime.timestamp;
642
+ });
643
+ if (targetIndex < 0) {
644
+ restored.push(runtimeMessage);
645
+ continue;
646
+ }
647
+ const target = restored[targetIndex];
648
+ restored[targetIndex] = {
649
+ ...restored[targetIndex],
650
+ content: [
651
+ ...Array.isArray(runtime.content) ? runtime.content : [],
652
+ ...Array.isArray(target.content) ? target.content : []
653
+ ]
654
+ };
655
+ }
656
+ return restored;
657
+ }
576
658
  async function getStoredAgentTurnSessionRecord(conversationId, sessionId) {
577
659
  const stateAdapter = getStateAdapter();
578
660
  await stateAdapter.connect();
@@ -581,7 +663,7 @@ async function getStoredAgentTurnSessionRecord(conversationId, sessionId) {
581
663
  );
582
664
  return value == null ? void 0 : parseAgentTurnSessionRecord(value);
583
665
  }
584
- async function getAgentTurnSessionRecord(conversationId, sessionId) {
666
+ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId, followCurrentReplacement) {
585
667
  const parsed = await getStoredAgentTurnSessionRecord(
586
668
  conversationId,
587
669
  sessionId
@@ -589,21 +671,40 @@ async function getAgentTurnSessionRecord(conversationId, sessionId) {
589
671
  if (!parsed) {
590
672
  return void 0;
591
673
  }
592
- const piProjection = await loadTurnProjection({
674
+ const pinnedProjection = await loadTurnProjection({
593
675
  conversationId,
594
676
  committedSeq: parsed.committedSeq,
595
677
  // Unfinished records include the current-epoch tail so parked input
596
678
  // appended after the last safe boundary stays model-visible on resume.
597
679
  includeTail: parsed.state === "running" || parsed.state === "awaiting_resume"
598
680
  });
599
- if (!piProjection) {
681
+ if (!pinnedProjection) {
600
682
  return void 0;
601
683
  }
602
- const turnStartMessageIndex = parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
684
+ const currentHistory = await getConversationEventStore().loadCurrentHistory(conversationId);
685
+ const currentHistoryVersion = currentHistory.at(-1)?.historyVersion ?? parsed.historyVersion ?? 0;
686
+ const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "awaiting_resume") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
687
+ const piProjection = followsReplacement ? projectConversationEvents(currentHistory) : pinnedProjection;
688
+ const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
603
689
  return materializeAgentTurnSessionRecord(
604
690
  parsed,
605
691
  piProjection,
606
- turnStartMessageIndex
692
+ turnStartMessageIndex,
693
+ !followsReplacement && (parsed.historyVersion === void 0 || parsed.historyVersion === currentHistoryVersion)
694
+ );
695
+ }
696
+ async function getAgentTurnSessionRecord(conversationId, sessionId) {
697
+ return await materializeStoredAgentTurnSessionRecord(
698
+ conversationId,
699
+ sessionId,
700
+ false
701
+ );
702
+ }
703
+ async function getAgentTurnSessionRecordForResume(conversationId, sessionId) {
704
+ return await materializeStoredAgentTurnSessionRecord(
705
+ conversationId,
706
+ sessionId,
707
+ true
607
708
  );
608
709
  }
609
710
  function buildStoredRecord(args) {
@@ -619,7 +720,9 @@ function buildStoredRecord(args) {
619
720
  lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
620
721
  updatedAtMs: nowMs,
621
722
  committedSeq: args.committedSeq,
723
+ ...args.historyVersion !== void 0 ? { historyVersion: args.historyVersion } : {},
622
724
  ...args.turnStartSeq !== void 0 ? { turnStartSeq: args.turnStartSeq } : {},
725
+ ...args.runtimeContext && args.runtimeContext.length > 0 ? { runtimeContext: args.runtimeContext } : {},
623
726
  cumulativeDurationMs: args.cumulativeDurationMs,
624
727
  ...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
625
728
  ...args.destination ? { destination: args.destination } : {},
@@ -653,8 +756,10 @@ async function setStoredRecord(args) {
653
756
  const {
654
757
  actors: _actors,
655
758
  committedSeq: _committedSeq,
759
+ historyVersion: _historyVersion,
656
760
  errorMessage: _errorMessage,
657
761
  turnStartSeq: _turnStartSeq,
762
+ runtimeContext: _runtimeContext,
658
763
  ...summary
659
764
  } = args.record;
660
765
  await appendAgentTurnSessionSummary(summary, args.ttlMs);
@@ -686,7 +791,9 @@ async function updateAgentTurnSessionState(args) {
686
791
  sliceId: args.existing.sliceId,
687
792
  state: args.state,
688
793
  committedSeq: parsed.committedSeq,
794
+ ...parsed.historyVersion !== void 0 ? { historyVersion: parsed.historyVersion } : {},
689
795
  ...parsed.turnStartSeq !== void 0 ? { turnStartSeq: parsed.turnStartSeq } : {},
796
+ ...parsed.runtimeContext ? { runtimeContext: parsed.runtimeContext } : {},
690
797
  ...parsed.channelName ? { channelName: parsed.channelName } : {},
691
798
  startedAtMs: parsed.startedAtMs,
692
799
  lastProgressAtMs: parsed.lastProgressAtMs,
@@ -717,17 +824,21 @@ async function upsertAgentTurnSessionRecord(args) {
717
824
  const instructionActor = args.actor ?? existingRecord?.actor;
718
825
  const commit = await commitMessages({
719
826
  conversationId: args.conversationId,
720
- modelId: args.modelId,
721
827
  messages: args.piMessages,
722
828
  ...instructionActor ? { newMessageProvenance: instructionProvenanceFor(instructionActor) } : {},
723
829
  ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {}
724
830
  });
725
- const turnStartSeq = args.turnStartMessageIndex === void 0 ? existingRecord?.turnStartSeq : args.turnStartMessageIndex <= 0 ? -1 : commit.messageSeqs[args.turnStartMessageIndex - 1] ?? commit.committedSeq;
726
- const turnStartMessageIndex = args.turnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
831
+ const durableTurnStartMessageIndex = args.turnStartMessageIndex === void 0 ? void 0 : stripRuntimeTurnContext(
832
+ args.piMessages.slice(0, args.turnStartMessageIndex)
833
+ ).length;
834
+ const runtimeContext = retainRuntimeTurnContext(args.piMessages);
835
+ const retainedRuntimeContext = runtimeContext.length > 0 ? runtimeContext : existingRecord?.historyVersion === commit.historyVersion ? existingRecord.runtimeContext : void 0;
836
+ const turnStartSeq = durableTurnStartMessageIndex === void 0 ? existingRecord?.turnStartSeq : durableTurnStartMessageIndex <= 0 ? -1 : commit.messageSeqs[durableTurnStartMessageIndex - 1] ?? commit.committedSeq;
837
+ const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
727
838
  return await setStoredRecord({
728
839
  conversationStore: args.conversationStore,
729
840
  destinationVisibility: args.destinationVisibility,
730
- piMessages: args.piMessages,
841
+ piMessages: commit.messages,
731
842
  piMessageProvenance: commit.provenance,
732
843
  ttlMs,
733
844
  ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {},
@@ -740,7 +851,9 @@ async function upsertAgentTurnSessionRecord(args) {
740
851
  ...existingRecord?.startedAtMs !== void 0 ? { startedAtMs: existingRecord.startedAtMs } : {},
741
852
  ...args.lastProgressAtMs !== void 0 ? { lastProgressAtMs: args.lastProgressAtMs } : {},
742
853
  committedSeq: commit.committedSeq,
854
+ historyVersion: commit.historyVersion,
743
855
  ...turnStartSeq !== void 0 ? { turnStartSeq } : {},
856
+ ...retainedRuntimeContext ? { runtimeContext: retainedRuntimeContext } : {},
744
857
  previousVersion: existingRecord?.version,
745
858
  cumulativeDurationMs: args.cumulativeDurationMs ?? existingRecord?.cumulativeDurationMs ?? 0,
746
859
  ...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
@@ -917,8 +1030,11 @@ async function failAgentTurnSessionRecord(args) {
917
1030
  });
918
1031
  }
919
1032
 
1033
+ // src/chat/state/ttl.ts
1034
+ var JUNIOR_THREAD_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
1035
+
920
1036
  export {
921
- projectSteps,
1037
+ piMessageSchema,
922
1038
  loadProjection,
923
1039
  loadConversationProjection,
924
1040
  openConversationProjection,
@@ -928,14 +1044,18 @@ export {
928
1044
  recordAuthorizationRequested,
929
1045
  recordAuthorizationCompleted,
930
1046
  recordToolExecutionStarted,
931
- hasAgentTurnUsage,
932
- addAgentTurnUsage,
1047
+ AGENT_TURN_SESSION_PREFIX,
1048
+ agentTurnSessionKey,
1049
+ agentTurnSessionConversationIndexKey,
933
1050
  getAgentTurnSessionRecord,
1051
+ getAgentTurnSessionRecordForResume,
934
1052
  upsertAgentTurnSessionRecord,
935
1053
  recordAgentTurnSessionSummary,
936
1054
  listAgentTurnSessionSummariesForConversation,
937
- listBoundedAgentTurnSessionSummariesForConversation,
938
1055
  listAgentTurnSessionSummariesForConversations,
939
1056
  abandonAgentTurnSessionRecord,
940
- failAgentTurnSessionRecord
1057
+ failAgentTurnSessionRecord,
1058
+ JUNIOR_THREAD_STATE_TTL_MS,
1059
+ coerceThreadConversationState,
1060
+ buildConversationStatePatch
941
1061
  };