@sentry/junior 0.108.0 → 0.110.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 (109) hide show
  1. package/dist/{agent-hooks-NU5HK3PS.js → agent-hooks-ELT4ODFF.js} +12 -13
  2. package/dist/api/conversations/access.d.ts +9 -0
  3. package/dist/api/conversations/aggregate.d.ts +12 -1
  4. package/dist/api/conversations/archive.d.ts +3 -5
  5. package/dist/api/conversations/detail.d.ts +156 -7
  6. package/dist/api/conversations/events.d.ts +2 -2
  7. package/dist/api/conversations/list.d.ts +57 -6
  8. package/dist/api/conversations/projection.d.ts +6 -7
  9. package/dist/api/conversations/reporting.d.ts +10 -4
  10. package/dist/api/conversations/routes.d.ts +2 -1
  11. package/dist/api/conversations/shared.d.ts +1 -1
  12. package/dist/api/conversations/stats.d.ts +37 -6
  13. package/dist/api/conversations/stats.query.d.ts +1 -1
  14. package/dist/api/conversations/usage.d.ts +9 -0
  15. package/dist/api/http.d.ts +7 -0
  16. package/dist/api/locations/detail.d.ts +102 -6
  17. package/dist/api/locations/list.d.ts +43 -5
  18. package/dist/api/locations/query.d.ts +4 -2
  19. package/dist/api/locations/routes.d.ts +2 -1
  20. package/dist/api/people/list.d.ts +28 -6
  21. package/dist/api/people/list.query.d.ts +1 -1
  22. package/dist/api/people/profile.d.ts +96 -7
  23. package/dist/api/people/profile.query.d.ts +4 -2
  24. package/dist/api/people/routes.d.ts +2 -1
  25. package/dist/api/people/shared.d.ts +1219 -2
  26. package/dist/api/route.d.ts +18 -4
  27. package/dist/api/schema/common.d.ts +6 -0
  28. package/dist/api/{conversations/schema.d.ts → schema/conversation.d.ts} +88 -9
  29. package/dist/api/{locations/schema.d.ts → schema/location.d.ts} +5 -0
  30. package/dist/api/{people/schema.d.ts → schema/person.d.ts} +37 -45
  31. package/dist/api/schema.d.ts +8 -22
  32. package/dist/api/schema.js +10 -2
  33. package/dist/api.d.ts +5 -1
  34. package/dist/api.js +838 -369
  35. package/dist/app.d.ts +1 -0
  36. package/dist/app.js +1564 -486
  37. package/dist/catalog-runtime-4GI22PXK.js +16 -0
  38. package/dist/chat/agent/resume.d.ts +11 -12
  39. package/dist/chat/conversations/history.d.ts +3 -0
  40. package/dist/chat/conversations/sql/privacy.d.ts +3 -15
  41. package/dist/chat/conversations/sql/store.d.ts +0 -7
  42. package/dist/chat/log-context.d.ts +40 -0
  43. package/dist/chat/logging.d.ts +3 -26
  44. package/dist/chat/pi/messages.d.ts +2 -2
  45. package/dist/chat/pi/sql-model-usage.d.ts +4 -1
  46. package/dist/chat/runtime/slack-resume.d.ts +2 -2
  47. package/dist/chat/services/auth-pause.d.ts +4 -0
  48. package/dist/chat/services/context-compaction.d.ts +1 -0
  49. package/dist/chat/services/turn-session-record.d.ts +0 -2
  50. package/dist/chat/slack/reply.d.ts +7 -19
  51. package/dist/chat/state/conversation.d.ts +1 -0
  52. package/dist/chat/task-execution/queue-signing.d.ts +10 -8
  53. package/dist/chat/task-execution/queue.d.ts +9 -7
  54. package/dist/chat/task-execution/slack-work.d.ts +6 -6
  55. package/dist/{chunk-UD6THJ2I.js → chunk-2T7TIEXI.js} +197 -12
  56. package/dist/{chunk-CEA3A3UA.js → chunk-2ZTTXTSZ.js} +24 -1
  57. package/dist/{chunk-XKB7LGIW.js → chunk-3S7YYHBI.js} +3 -139
  58. package/dist/chunk-3TV7OEHL.js +10 -0
  59. package/dist/{chunk-A5CO2EHL.js → chunk-3UBQI73J.js} +11 -15
  60. package/dist/{chunk-7FBGKXPE.js → chunk-42WRQLJ6.js} +10 -33
  61. package/dist/{chunk-TE4QHJH4.js → chunk-4QU44P7Q.js} +135 -595
  62. package/dist/{chunk-RT5BUHR5.js → chunk-6Y5L4BOM.js} +3 -5
  63. package/dist/{chunk-VIXFG2VC.js → chunk-C4MZ3GP5.js} +1 -2
  64. package/dist/{chunk-3RGQLX2F.js → chunk-C5XAFTHY.js} +1011 -29
  65. package/dist/{chunk-PUAMO4PI.js → chunk-FPK7XY3F.js} +139 -99
  66. package/dist/{chunk-H3QYZL7K.js → chunk-GK47NBDE.js} +28 -3
  67. package/dist/{chunk-O24R4DCK.js → chunk-IYKPU7H6.js} +94 -56
  68. package/dist/chunk-JQFJO5WH.js +477 -0
  69. package/dist/{chunk-VH6KWKG2.js → chunk-L5XQPJ7T.js} +3 -3
  70. package/dist/{chunk-2XXEKR44.js → chunk-MIAKQNYX.js} +43 -31
  71. package/dist/{chunk-KPMPQ6AA.js → chunk-RHHRKSPI.js} +249 -223
  72. package/dist/{chunk-YT72TOEK.js → chunk-SBOBTS27.js} +2 -2
  73. package/dist/{chunk-B5I5LMSP.js → chunk-W6RJZG7F.js} +5 -7
  74. package/dist/chunk-W75ZPAGG.js +44 -0
  75. package/dist/{chunk-SS67LUOK.js → chunk-WNOVRQ67.js} +6 -7
  76. package/dist/{chunk-YIPD3L6L.js → chunk-Y3H2DN6F.js} +8 -10
  77. package/dist/{chunk-DY7TRYB5.js → chunk-Y5FK3CNG.js} +1 -1
  78. package/dist/chunk-ZKIOWEFL.js +125 -0
  79. package/dist/cli/chat.js +23 -24
  80. package/dist/cli/check.js +9 -7
  81. package/dist/cli/plugins.js +16 -17
  82. package/dist/cli/snapshot-warmup.js +11 -10
  83. package/dist/cli/upgrade.js +314 -3505
  84. package/dist/db/db.d.ts +1 -1
  85. package/dist/db/schema/conversations.d.ts +17 -0
  86. package/dist/db/schema.d.ts +17 -0
  87. package/dist/{db-AMRBAT5D.js → db-Y6W6CWWW.js} +8 -7
  88. package/dist/instrumentation.js +4 -3
  89. package/dist/nitro.js +4 -4
  90. package/dist/reporting-schema.d.ts +46 -0
  91. package/dist/{runner-TQH5GAJ4.js → runner-BBFV5GLR.js} +17 -21
  92. package/dist/{skills-ITNHBARB.js → skills-AR4AF32L.js} +8 -6
  93. package/dist/{validation-AGQ46ZGV.js → validation-FBIEPTC2.js} +8 -6
  94. package/dist/version.d.ts +2 -0
  95. package/dist/version.js +7 -0
  96. package/migrations/0006_drop_legacy_conversation_messages.sql +1 -0
  97. package/migrations/0007_thick_sugar_man.sql +23 -0
  98. package/migrations/README.md +12 -9
  99. package/migrations/meta/0006_snapshot.json +948 -0
  100. package/migrations/meta/0007_snapshot.json +1013 -0
  101. package/migrations/meta/_journal.json +14 -0
  102. package/package.json +9 -5
  103. package/dist/catalog-runtime-FOFNPR3P.js +0 -14
  104. package/dist/chunk-AHJR2IFF.js +0 -115
  105. package/dist/chunk-B7PUBQQM.js +0 -53
  106. package/dist/chunk-G3E7SCME.js +0 -28
  107. package/dist/chunk-J3B3FPP2.js +0 -1061
  108. package/dist/chunk-OB42YVAE.js +0 -16
  109. package/dist/chunk-TWINAEZQ.js +0 -1283
@@ -1,20 +1,21 @@
1
1
  import {
2
- JUNIOR_THREAD_STATE_TTL_MS,
3
- abandonAgentTurnSessionRecord,
4
- buildConversationStatePatch,
5
- getAgentTurnSessionRecord,
6
- getAgentTurnSessionRecordForResume,
7
- upsertAgentTurnSessionRecord
8
- } from "./chunk-J3B3FPP2.js";
9
- import {
10
- addAgentTurnUsage
2
+ addAgentTurnUsage,
3
+ agentTurnUsageSchema
11
4
  } from "./chunk-MU6HHZEN.js";
12
5
  import {
13
6
  getStateAdapter
14
- } from "./chunk-H3QYZL7K.js";
7
+ } from "./chunk-GK47NBDE.js";
15
8
  import {
16
- getConversationEventStore
17
- } from "./chunk-TE4QHJH4.js";
9
+ contextProvenance,
10
+ createSqlConversationEventStore,
11
+ getConversationEventStore,
12
+ getConversationStore,
13
+ getSqlExecutor,
14
+ instructionActors,
15
+ instructionProvenanceFor,
16
+ sanitizePostgresJson,
17
+ withConversationEventLock
18
+ } from "./chunk-4QU44P7Q.js";
18
19
  import {
19
20
  sleep
20
21
  } from "./chunk-4ZNGQH7C.js";
@@ -23,22 +24,343 @@ import {
23
24
  escapeXml,
24
25
  getPiMessageRole,
25
26
  resolveGatewayModel,
27
+ retainRuntimeTurnContext,
26
28
  standardModelId,
29
+ stripRuntimeTurnContext,
27
30
  trimTrailingAssistantMessages
28
- } from "./chunk-SS67LUOK.js";
31
+ } from "./chunk-WNOVRQ67.js";
32
+ import {
33
+ toStoredSlackActor
34
+ } from "./chunk-C4MZ3GP5.js";
29
35
  import {
30
36
  getActiveTraceId,
31
37
  logException,
32
38
  logWarn,
33
39
  setSpanAttributes
34
- } from "./chunk-O24R4DCK.js";
40
+ } from "./chunk-IYKPU7H6.js";
35
41
  import {
36
42
  isRecord,
43
+ parseSlackMessageTs,
44
+ toOptionalNumber,
37
45
  toOptionalString
38
- } from "./chunk-OB42YVAE.js";
39
- import {
40
- parseSlackMessageTs
41
- } from "./chunk-7FBGKXPE.js";
46
+ } from "./chunk-42WRQLJ6.js";
47
+
48
+ // src/chat/conversations/projection.ts
49
+ import { isDeepStrictEqual } from "util";
50
+
51
+ // src/chat/pi/messages.ts
52
+ import { z } from "zod";
53
+ var piMessageSchema = z.object({
54
+ role: z.string()
55
+ }).passthrough().transform((value) => value);
56
+ var piContentMessageSchema = z.object({
57
+ content: z.array(z.unknown()),
58
+ role: z.string().min(1)
59
+ }).passthrough().transform((value) => value);
60
+
61
+ // src/chat/pi/conversation-events.ts
62
+ function authorizationObservationMessage(data, createdAtMs) {
63
+ const label = data.kind === "mcp" ? "MCP authorization" : "Authorization";
64
+ return piMessageSchema.parse({
65
+ role: "user",
66
+ content: [
67
+ {
68
+ type: "text",
69
+ text: `${label} completed for provider "${data.provider}". Continue the blocked request and retry the provider operation if needed.`
70
+ }
71
+ ],
72
+ timestamp: createdAtMs
73
+ });
74
+ }
75
+ function messageEventProvenance(data) {
76
+ return data.provenance ?? contextProvenance;
77
+ }
78
+ function durableMessages(message) {
79
+ return stripRuntimeTurnContext([piMessageSchema.parse(message)]);
80
+ }
81
+ function projectConversationEvents(events, options) {
82
+ const messages = [];
83
+ const provenance = [];
84
+ const seqs = [];
85
+ let modelProfile = "standard";
86
+ let modelId;
87
+ for (const event of events) {
88
+ if (options?.maxSeq !== void 0 && event.seq > options.maxSeq) break;
89
+ if (event.data.type === "unknown") {
90
+ throw new Error(
91
+ `Unsupported conversation event "${event.data.originalType}" at seq ${event.seq} (schema version ${event.schemaVersion})`
92
+ );
93
+ }
94
+ if (event.data.type === "compaction" || event.data.type === "handoff" || event.data.type === "rollback") {
95
+ modelProfile = event.data.modelProfile;
96
+ modelId = event.data.modelId;
97
+ for (const replacement of event.data.replacementHistory) {
98
+ for (const message of durableMessages(replacement.message)) {
99
+ messages.push(message);
100
+ provenance.push(replacement.provenance ?? contextProvenance);
101
+ seqs.push(replacement.sourceEventSeq ?? event.seq);
102
+ }
103
+ }
104
+ continue;
105
+ }
106
+ if (event.data.type === "agent_step") {
107
+ for (const message of durableMessages(event.data.message)) {
108
+ messages.push(message);
109
+ provenance.push(messageEventProvenance(event.data));
110
+ seqs.push(event.seq);
111
+ }
112
+ continue;
113
+ }
114
+ if (event.data.type === "authorization_completed") {
115
+ messages.push(
116
+ authorizationObservationMessage(event.data, event.createdAtMs)
117
+ );
118
+ provenance.push(contextProvenance);
119
+ seqs.push(event.seq);
120
+ }
121
+ }
122
+ return { messages, provenance, seqs, modelProfile, modelId };
123
+ }
124
+
125
+ // src/chat/conversations/projection.ts
126
+ function connectedMcpProvidersFromEvents(events) {
127
+ const providers = /* @__PURE__ */ new Set();
128
+ for (const event of events) {
129
+ if (event.data.type === "mcp_provider_connected") {
130
+ providers.add(event.data.provider);
131
+ }
132
+ }
133
+ return [...providers].sort((left, right) => left.localeCompare(right));
134
+ }
135
+ function isUserMessage(message) {
136
+ return message.role === "user";
137
+ }
138
+ function countMatchingPrefix(left, right) {
139
+ const limit = Math.min(left.length, right.length);
140
+ for (let index = 0; index < limit; index += 1) {
141
+ if (!isDeepStrictEqual(left[index], right[index])) {
142
+ return index;
143
+ }
144
+ }
145
+ return limit;
146
+ }
147
+ function normalizeDurableMessage(message) {
148
+ return piMessageSchema.parse(
149
+ JSON.parse(JSON.stringify(sanitizePostgresJson(message)))
150
+ );
151
+ }
152
+ function resolveCommitProvenance(args) {
153
+ if (args.explicitProvenance) {
154
+ if (args.explicitProvenance.length !== args.nextMessages.length) {
155
+ throw new Error("commit provenance must align one-to-one with messages");
156
+ }
157
+ return args.explicitProvenance;
158
+ }
159
+ const matchingPrefix = args.matchingPrefix;
160
+ const provenance = args.nextMessages.map(
161
+ (_, index) => index < matchingPrefix ? args.existing.provenance[index] ?? contextProvenance : contextProvenance
162
+ );
163
+ if (args.newMessageProvenance) {
164
+ for (let index = args.nextMessages.length - 1; index >= matchingPrefix; index -= 1) {
165
+ if (isUserMessage(args.nextMessages[index])) {
166
+ provenance[index] = args.newMessageProvenance;
167
+ break;
168
+ }
169
+ }
170
+ }
171
+ if (args.trailingMessageProvenance) {
172
+ if (args.trailingMessageProvenance.length > provenance.length) {
173
+ throw new Error(
174
+ "trailing commit provenance cannot exceed committed messages"
175
+ );
176
+ }
177
+ const newMessageCount = args.nextMessages.length - matchingPrefix;
178
+ if (args.trailingMessageProvenance.length > newMessageCount) {
179
+ throw new Error(
180
+ "trailing commit provenance must align to newly committed messages"
181
+ );
182
+ }
183
+ const start = provenance.length - args.trailingMessageProvenance.length;
184
+ args.trailingMessageProvenance.forEach((entry, offset) => {
185
+ provenance[start + offset] = entry;
186
+ });
187
+ }
188
+ return provenance;
189
+ }
190
+ async function loadProjection(args) {
191
+ const events = await getConversationEventStore().loadCurrentHistory(
192
+ args.conversationId
193
+ );
194
+ return projectConversationEvents(events).messages;
195
+ }
196
+ async function loadConversationProjection(args) {
197
+ const events = await getConversationEventStore().loadCurrentHistory(
198
+ args.conversationId
199
+ );
200
+ return projectConversationEvents(events);
201
+ }
202
+ async function openConversationProjection(args) {
203
+ const events = await getConversationEventStore().loadCurrentHistory(
204
+ args.conversationId
205
+ );
206
+ return projectConversationEvents(events);
207
+ }
208
+ async function loadTurnProjection(args) {
209
+ const eventStore = getConversationEventStore();
210
+ if (args.committedSeq < 0) {
211
+ return projectConversationEvents(
212
+ await eventStore.loadCurrentHistory(args.conversationId)
213
+ );
214
+ }
215
+ const historyEvents = await eventStore.loadHistoryContaining(
216
+ args.conversationId,
217
+ args.committedSeq,
218
+ args.includeTail ? void 0 : args.committedSeq
219
+ );
220
+ if (!historyEvents) {
221
+ return void 0;
222
+ }
223
+ return projectConversationEvents(historyEvents);
224
+ }
225
+ async function loadConnectedMcpProviders(args) {
226
+ const events = await getConversationEventStore().loadCurrentHistory(
227
+ args.conversationId
228
+ );
229
+ return connectedMcpProvidersFromEvents(events);
230
+ }
231
+ function messageTimestamp(message) {
232
+ const timestamp = message.timestamp;
233
+ return typeof timestamp === "number" ? timestamp : Date.now();
234
+ }
235
+ async function commitMessages(args) {
236
+ const executor = args.executor ?? getSqlExecutor();
237
+ return await withConversationEventLock(
238
+ executor,
239
+ args.conversationId,
240
+ async () => executor.transaction(
241
+ async () => await commitMessagesLocked(args, executor)
242
+ )
243
+ );
244
+ }
245
+ async function commitMessagesLocked(args, executor) {
246
+ const eventStore = createSqlConversationEventStore(executor);
247
+ const currentEvents = await eventStore.loadCurrentHistory(
248
+ args.conversationId
249
+ );
250
+ const current = projectConversationEvents(currentEvents);
251
+ const nextLocalMessages = stripRuntimeTurnContext(args.messages).map(
252
+ normalizeDurableMessage
253
+ );
254
+ const matchingPrefix = countMatchingPrefix(
255
+ current.messages,
256
+ nextLocalMessages
257
+ );
258
+ const nextLocalProvenance = resolveCommitProvenance({
259
+ existing: current,
260
+ nextMessages: nextLocalMessages,
261
+ matchingPrefix,
262
+ ...args.provenance ? { explicitProvenance: args.provenance } : {},
263
+ ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {},
264
+ ...args.newMessageProvenance ? { newMessageProvenance: args.newMessageProvenance } : {}
265
+ });
266
+ if (matchingPrefix === current.messages.length) {
267
+ const newMessages = nextLocalMessages.slice(matchingPrefix);
268
+ await eventStore.append(
269
+ args.conversationId,
270
+ newMessages.map((message, index) => ({
271
+ data: {
272
+ type: "agent_step",
273
+ message,
274
+ provenance: nextLocalProvenance[matchingPrefix + index]
275
+ },
276
+ createdAtMs: messageTimestamp(message)
277
+ }))
278
+ );
279
+ } else {
280
+ throw new Error(
281
+ `Agent history for ${args.conversationId} changed before its committed boundary`
282
+ );
283
+ }
284
+ const committedEvents = await eventStore.loadCurrentHistory(
285
+ args.conversationId
286
+ );
287
+ const committed = projectConversationEvents(committedEvents);
288
+ return {
289
+ committedSeq: committedEvents.at(-1)?.seq ?? -1,
290
+ historyVersion: committedEvents.at(-1)?.historyVersion ?? 0,
291
+ messageSeqs: committed.seqs,
292
+ messages: nextLocalMessages,
293
+ provenance: nextLocalProvenance
294
+ };
295
+ }
296
+ async function recordMcpProviderConnected(args) {
297
+ const eventStore = getConversationEventStore();
298
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
299
+ if (connectedMcpProvidersFromEvents(events).includes(args.provider)) {
300
+ return;
301
+ }
302
+ await eventStore.append(args.conversationId, [
303
+ {
304
+ data: { type: "mcp_provider_connected", provider: args.provider },
305
+ createdAtMs: Date.now()
306
+ }
307
+ ]);
308
+ }
309
+ async function recordAuthorizationRequested(args) {
310
+ const eventStore = getConversationEventStore();
311
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
312
+ if (events.some(
313
+ (event) => event.data.type === "authorization_requested" && event.data.authorizationId === args.authorizationId
314
+ )) {
315
+ return;
316
+ }
317
+ await eventStore.append(args.conversationId, [
318
+ {
319
+ data: {
320
+ type: "authorization_requested",
321
+ kind: args.kind,
322
+ provider: args.provider,
323
+ actorId: args.actorId,
324
+ authorizationId: args.authorizationId,
325
+ delivery: args.delivery
326
+ },
327
+ createdAtMs: Date.now()
328
+ }
329
+ ]);
330
+ }
331
+ async function recordAuthorizationCompleted(args) {
332
+ const eventStore = getConversationEventStore();
333
+ const events = await eventStore.loadCurrentHistory(args.conversationId);
334
+ if (events.some(
335
+ (event) => event.data.type === "authorization_completed" && event.data.authorizationId === args.authorizationId
336
+ )) {
337
+ return;
338
+ }
339
+ await eventStore.append(args.conversationId, [
340
+ {
341
+ data: {
342
+ type: "authorization_completed",
343
+ kind: args.kind,
344
+ provider: args.provider,
345
+ actorId: args.actorId,
346
+ authorizationId: args.authorizationId
347
+ },
348
+ createdAtMs: Date.now()
349
+ }
350
+ ]);
351
+ }
352
+ async function recordToolExecutionStarted(args) {
353
+ await getConversationEventStore().append(args.conversationId, [
354
+ {
355
+ data: {
356
+ type: "tool_execution_started",
357
+ toolCallId: args.toolCallId,
358
+ toolName: args.toolName
359
+ },
360
+ createdAtMs: args.createdAtMs ?? Date.now()
361
+ }
362
+ ]);
363
+ }
42
364
 
43
365
  // src/chat/state/turn-id.ts
44
366
  function buildDeterministicTurnId(messageId) {
@@ -145,6 +467,531 @@ async function persistWithRetry(persist) {
145
467
  throw lastError;
146
468
  }
147
469
 
470
+ // src/chat/state/turn-session.ts
471
+ import { THREAD_STATE_TTL_MS } from "chat";
472
+ import {
473
+ actorSchema,
474
+ destinationSchema,
475
+ sourceSchema
476
+ } from "@sentry/junior-plugin-api";
477
+ import { z as z2 } from "zod";
478
+
479
+ // src/chat/state/turn-session-keys.ts
480
+ var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
481
+ function agentTurnSessionKey(conversationId, sessionId) {
482
+ return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
483
+ }
484
+ function agentTurnSessionConversationIndexKey(conversationId) {
485
+ return `${AGENT_TURN_SESSION_PREFIX}:conversation:${conversationId}:index`;
486
+ }
487
+
488
+ // src/chat/state/turn-session.ts
489
+ var AGENT_TURN_SESSION_INDEX_KEY = `${AGENT_TURN_SESSION_PREFIX}:index`;
490
+ var AGENT_TURN_SESSION_INDEX_MAX_LENGTH = 5e3;
491
+ var AGENT_TURN_SESSION_TTL_MS = THREAD_STATE_TTL_MS;
492
+ var agentTurnSessionStatusSchema = z2.enum([
493
+ "running",
494
+ "awaiting_resume",
495
+ "completed",
496
+ "failed",
497
+ "abandoned"
498
+ ]);
499
+ var agentTurnSurfaceSchema = z2.enum([
500
+ "slack",
501
+ "api",
502
+ "scheduler",
503
+ "internal"
504
+ ]);
505
+ var agentTurnResumeReasonSchema = z2.enum([
506
+ "timeout",
507
+ "auth",
508
+ "yield",
509
+ "retry"
510
+ ]);
511
+ var nonNegativeNumberSchema = z2.number().finite().nonnegative();
512
+ var seqCursorSchema = z2.number().int().min(-1);
513
+ var agentTurnSessionSummarySchema = z2.object({
514
+ channelName: z2.string().min(1).optional(),
515
+ version: z2.number().int().nonnegative(),
516
+ conversationId: z2.string().min(1),
517
+ cumulativeDurationMs: nonNegativeNumberSchema,
518
+ cumulativeUsage: agentTurnUsageSchema.optional(),
519
+ destination: destinationSchema.optional(),
520
+ source: sourceSchema.optional(),
521
+ lastProgressAtMs: nonNegativeNumberSchema,
522
+ loadedSkillNames: z2.array(z2.string()).optional(),
523
+ modelId: z2.string().min(1).optional(),
524
+ reasoningLevel: z2.string().min(1).optional(),
525
+ actor: actorSchema.optional(),
526
+ resumeReason: agentTurnResumeReasonSchema.optional(),
527
+ resumedFromSliceId: z2.number().int().nonnegative().optional(),
528
+ sessionId: z2.string().min(1),
529
+ sliceId: z2.number().int().nonnegative(),
530
+ startedAtMs: nonNegativeNumberSchema,
531
+ state: agentTurnSessionStatusSchema,
532
+ surface: agentTurnSurfaceSchema.optional(),
533
+ traceId: z2.string().optional(),
534
+ updatedAtMs: nonNegativeNumberSchema
535
+ }).strict();
536
+ var storedAgentTurnSessionRecordSchema = agentTurnSessionSummarySchema.extend({
537
+ actors: z2.array(actorSchema).optional(),
538
+ committedSeq: seqCursorSchema,
539
+ historyVersion: z2.number().int().nonnegative().optional(),
540
+ errorMessage: z2.string().optional(),
541
+ turnStartSeq: seqCursorSchema.optional(),
542
+ runtimeContext: z2.array(piMessageSchema).optional()
543
+ }).strict();
544
+ function conversationExecutionFromSummary(summary) {
545
+ const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
546
+ return {
547
+ status,
548
+ runId: summary.sessionId,
549
+ updatedAtMs: summary.updatedAtMs
550
+ };
551
+ }
552
+ function sessionLogActor(actor) {
553
+ return actor?.platform === "slack" ? toStoredSlackActor(actor) : void 0;
554
+ }
555
+ function parseAgentTurnSessionRecord(value) {
556
+ return storedAgentTurnSessionRecordSchema.parse(value);
557
+ }
558
+ function parseAgentTurnSessionSummary(value) {
559
+ return agentTurnSessionSummarySchema.parse(value);
560
+ }
561
+ async function appendAgentTurnSessionSummary(summary, ttlMs) {
562
+ const stateAdapter = getStateAdapter();
563
+ await Promise.all([
564
+ stateAdapter.appendToList(AGENT_TURN_SESSION_INDEX_KEY, summary, {
565
+ maxLength: AGENT_TURN_SESSION_INDEX_MAX_LENGTH,
566
+ ttlMs
567
+ }),
568
+ stateAdapter.appendToList(
569
+ agentTurnSessionConversationIndexKey(summary.conversationId),
570
+ summary,
571
+ { ttlMs }
572
+ )
573
+ ]);
574
+ }
575
+ async function recordConversationActivityMetadata(args) {
576
+ const conversationStore = args.conversationStore ?? getConversationStore();
577
+ const source = args.summary.destination?.platform === "local" ? "local" : args.summary.surface;
578
+ await conversationStore.recordActivity({
579
+ activityAtMs: args.summary.updatedAtMs,
580
+ channelName: args.summary.channelName,
581
+ conversationId: args.summary.conversationId,
582
+ destination: args.summary.destination,
583
+ nowMs: args.nowMs,
584
+ actor: sessionLogActor(args.summary.actor),
585
+ source,
586
+ visibility: args.destinationVisibility
587
+ });
588
+ await conversationStore.recordExecution({
589
+ channelName: args.summary.channelName,
590
+ conversationId: args.summary.conversationId,
591
+ createdAtMs: args.summary.startedAtMs,
592
+ destination: args.summary.destination,
593
+ execution: conversationExecutionFromSummary(args.summary),
594
+ lastActivityAtMs: args.summary.updatedAtMs,
595
+ metrics: {
596
+ durationMs: args.summary.cumulativeDurationMs,
597
+ ...args.summary.cumulativeUsage ? { usage: args.summary.cumulativeUsage } : {}
598
+ },
599
+ actor: sessionLogActor(args.summary.actor),
600
+ source,
601
+ updatedAtMs: args.nowMs,
602
+ visibility: args.destinationVisibility
603
+ });
604
+ }
605
+ function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true) {
606
+ const piMessages = restoreVolatileContext && (stored.state === "running" || stored.state === "awaiting_resume") ? restoreRuntimeContext(piProjection.messages, stored.runtimeContext) : piProjection.messages;
607
+ return {
608
+ version: stored.version,
609
+ ...stored.channelName ? { channelName: stored.channelName } : {},
610
+ conversationId: stored.conversationId,
611
+ sessionId: stored.sessionId,
612
+ sliceId: stored.sliceId,
613
+ state: stored.state,
614
+ startedAtMs: stored.startedAtMs,
615
+ lastProgressAtMs: stored.lastProgressAtMs,
616
+ updatedAtMs: stored.updatedAtMs,
617
+ piMessages,
618
+ piMessageProvenance: piProjection.provenance,
619
+ actors: stored.actors ?? instructionActors(piProjection.provenance),
620
+ cumulativeDurationMs: stored.cumulativeDurationMs,
621
+ ...stored.destination ? { destination: stored.destination } : {},
622
+ ...stored.source ? { source: stored.source } : {},
623
+ ...stored.cumulativeUsage ? { cumulativeUsage: stored.cumulativeUsage } : {},
624
+ ...stored.resumeReason ? { resumeReason: stored.resumeReason } : {},
625
+ ...stored.errorMessage ? { errorMessage: stored.errorMessage } : {},
626
+ ...stored.loadedSkillNames ? { loadedSkillNames: stored.loadedSkillNames } : {},
627
+ ...stored.modelId ? { modelId: stored.modelId } : {},
628
+ ...stored.reasoningLevel ? { reasoningLevel: stored.reasoningLevel } : {},
629
+ ...stored.actor ? { actor: stored.actor } : {},
630
+ ...stored.resumedFromSliceId !== void 0 ? { resumedFromSliceId: stored.resumedFromSliceId } : {},
631
+ ...stored.surface ? { surface: stored.surface } : {},
632
+ ...stored.traceId ? { traceId: stored.traceId } : {},
633
+ ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
634
+ };
635
+ }
636
+ function restoreRuntimeContext(messages, runtimeContext) {
637
+ if (!runtimeContext || runtimeContext.length === 0) return messages;
638
+ const restored = [...messages];
639
+ for (const runtimeMessage of runtimeContext) {
640
+ const runtime = runtimeMessage;
641
+ const targetIndex = restored.findIndex((message) => {
642
+ const candidate = message;
643
+ return candidate.role === "user" && candidate.timestamp === runtime.timestamp;
644
+ });
645
+ if (targetIndex < 0) {
646
+ restored.push(runtimeMessage);
647
+ continue;
648
+ }
649
+ const target = restored[targetIndex];
650
+ restored[targetIndex] = {
651
+ ...restored[targetIndex],
652
+ content: [
653
+ ...Array.isArray(runtime.content) ? runtime.content : [],
654
+ ...Array.isArray(target.content) ? target.content : []
655
+ ]
656
+ };
657
+ }
658
+ return restored;
659
+ }
660
+ async function getStoredAgentTurnSessionRecord(conversationId, sessionId) {
661
+ const stateAdapter = getStateAdapter();
662
+ await stateAdapter.connect();
663
+ const value = await stateAdapter.get(
664
+ agentTurnSessionKey(conversationId, sessionId)
665
+ );
666
+ return value == null ? void 0 : parseAgentTurnSessionRecord(value);
667
+ }
668
+ async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId, followCurrentReplacement) {
669
+ const parsed = await getStoredAgentTurnSessionRecord(
670
+ conversationId,
671
+ sessionId
672
+ );
673
+ if (!parsed) {
674
+ return void 0;
675
+ }
676
+ const pinnedProjection = await loadTurnProjection({
677
+ conversationId,
678
+ committedSeq: parsed.committedSeq,
679
+ // Unfinished records include the current-epoch tail so parked input
680
+ // appended after the last safe boundary stays model-visible on resume.
681
+ includeTail: parsed.state === "running" || parsed.state === "awaiting_resume"
682
+ });
683
+ if (!pinnedProjection) {
684
+ return void 0;
685
+ }
686
+ const currentHistory = await getConversationEventStore().loadCurrentHistory(conversationId);
687
+ const currentHistoryVersion = currentHistory.at(-1)?.historyVersion ?? parsed.historyVersion ?? 0;
688
+ const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "awaiting_resume") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
689
+ const piProjection = followsReplacement ? projectConversationEvents(currentHistory) : pinnedProjection;
690
+ const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
691
+ return materializeAgentTurnSessionRecord(
692
+ parsed,
693
+ piProjection,
694
+ turnStartMessageIndex,
695
+ !followsReplacement && (parsed.historyVersion === void 0 || parsed.historyVersion === currentHistoryVersion)
696
+ );
697
+ }
698
+ async function getAgentTurnSessionRecord(conversationId, sessionId) {
699
+ return await materializeStoredAgentTurnSessionRecord(
700
+ conversationId,
701
+ sessionId,
702
+ false
703
+ );
704
+ }
705
+ async function getAgentTurnSessionRecordForResume(conversationId, sessionId) {
706
+ return await materializeStoredAgentTurnSessionRecord(
707
+ conversationId,
708
+ sessionId,
709
+ true
710
+ );
711
+ }
712
+ function buildStoredRecord(args) {
713
+ const nowMs = Date.now();
714
+ return {
715
+ version: (args.previousVersion ?? 0) + 1,
716
+ ...args.channelName ? { channelName: args.channelName } : {},
717
+ conversationId: args.conversationId,
718
+ sessionId: args.sessionId,
719
+ sliceId: args.sliceId,
720
+ state: args.state,
721
+ startedAtMs: args.startedAtMs ?? nowMs,
722
+ lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
723
+ updatedAtMs: nowMs,
724
+ committedSeq: args.committedSeq,
725
+ ...args.historyVersion !== void 0 ? { historyVersion: args.historyVersion } : {},
726
+ ...args.turnStartSeq !== void 0 ? { turnStartSeq: args.turnStartSeq } : {},
727
+ ...args.runtimeContext && args.runtimeContext.length > 0 ? { runtimeContext: args.runtimeContext } : {},
728
+ cumulativeDurationMs: args.cumulativeDurationMs,
729
+ ...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
730
+ ...args.destination ? { destination: args.destination } : {},
731
+ ...args.source ? { source: args.source } : {},
732
+ ...args.actor ? { actor: args.actor } : {},
733
+ ...args.actors ? { actors: args.actors } : {},
734
+ ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
735
+ ...args.modelId ? { modelId: args.modelId } : {},
736
+ ...args.reasoningLevel ? { reasoningLevel: args.reasoningLevel } : {},
737
+ ...args.resumeReason ? { resumeReason: args.resumeReason } : {},
738
+ ...args.errorMessage ? { errorMessage: args.errorMessage } : {},
739
+ ...args.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.resumedFromSliceId } : {},
740
+ ...args.surface ? { surface: args.surface } : {},
741
+ ...args.traceId ? { traceId: args.traceId } : {}
742
+ };
743
+ }
744
+ async function setStoredRecord(args) {
745
+ const stateAdapter = getStateAdapter();
746
+ await stateAdapter.connect();
747
+ await recordConversationActivityMetadata({
748
+ conversationStore: args.conversationStore,
749
+ destinationVisibility: args.destinationVisibility,
750
+ nowMs: Date.now(),
751
+ summary: args.record
752
+ });
753
+ await stateAdapter.set(
754
+ agentTurnSessionKey(args.record.conversationId, args.record.sessionId),
755
+ args.record,
756
+ args.ttlMs
757
+ );
758
+ const {
759
+ actors: _actors,
760
+ committedSeq: _committedSeq,
761
+ historyVersion: _historyVersion,
762
+ errorMessage: _errorMessage,
763
+ turnStartSeq: _turnStartSeq,
764
+ runtimeContext: _runtimeContext,
765
+ ...summary
766
+ } = args.record;
767
+ await appendAgentTurnSessionSummary(summary, args.ttlMs);
768
+ return materializeAgentTurnSessionRecord(
769
+ args.record,
770
+ {
771
+ messages: [...args.piMessages],
772
+ provenance: [...args.piMessageProvenance]
773
+ },
774
+ args.turnStartMessageIndex
775
+ );
776
+ }
777
+ async function updateAgentTurnSessionState(args) {
778
+ const parsed = await getStoredAgentTurnSessionRecord(
779
+ args.existing.conversationId,
780
+ args.existing.sessionId
781
+ );
782
+ if (!parsed || parsed.version !== args.existing.version) {
783
+ return void 0;
784
+ }
785
+ return await setStoredRecord({
786
+ piMessages: args.existing.piMessages,
787
+ piMessageProvenance: args.existing.piMessageProvenance,
788
+ ttlMs: AGENT_TURN_SESSION_TTL_MS,
789
+ ...args.existing.turnStartMessageIndex !== void 0 ? { turnStartMessageIndex: args.existing.turnStartMessageIndex } : {},
790
+ record: buildStoredRecord({
791
+ conversationId: args.existing.conversationId,
792
+ sessionId: args.existing.sessionId,
793
+ sliceId: args.existing.sliceId,
794
+ state: args.state,
795
+ committedSeq: parsed.committedSeq,
796
+ ...parsed.historyVersion !== void 0 ? { historyVersion: parsed.historyVersion } : {},
797
+ ...parsed.turnStartSeq !== void 0 ? { turnStartSeq: parsed.turnStartSeq } : {},
798
+ ...parsed.runtimeContext ? { runtimeContext: parsed.runtimeContext } : {},
799
+ ...parsed.channelName ? { channelName: parsed.channelName } : {},
800
+ startedAtMs: parsed.startedAtMs,
801
+ lastProgressAtMs: parsed.lastProgressAtMs,
802
+ previousVersion: parsed.version,
803
+ cumulativeDurationMs: args.existing.cumulativeDurationMs,
804
+ ...args.existing.cumulativeUsage ? { cumulativeUsage: args.existing.cumulativeUsage } : {},
805
+ ...args.existing.destination ? { destination: args.existing.destination } : {},
806
+ ...args.existing.source ? { source: args.existing.source } : {},
807
+ ...args.existing.loadedSkillNames ? { loadedSkillNames: args.existing.loadedSkillNames } : {},
808
+ ...args.existing.modelId ? { modelId: args.existing.modelId } : {},
809
+ ...args.existing.reasoningLevel ? { reasoningLevel: args.existing.reasoningLevel } : {},
810
+ ...args.existing.actor ? { actor: args.existing.actor } : {},
811
+ actors: args.existing.actors,
812
+ ...args.existing.resumeReason ? { resumeReason: args.existing.resumeReason } : {},
813
+ ...args.existing.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.existing.resumedFromSliceId } : {},
814
+ ...args.existing.surface ? { surface: args.existing.surface } : {},
815
+ ...args.existing.traceId ? { traceId: args.existing.traceId } : {},
816
+ ...args.errorMessage ?? args.existing.errorMessage ? { errorMessage: args.errorMessage ?? args.existing.errorMessage } : {}
817
+ })
818
+ });
819
+ }
820
+ async function upsertAgentTurnSessionRecord(args) {
821
+ const existingRecord = await getStoredAgentTurnSessionRecord(
822
+ args.conversationId,
823
+ args.sessionId
824
+ );
825
+ const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
826
+ const instructionActor = args.actor ?? existingRecord?.actor;
827
+ const commit = await commitMessages({
828
+ conversationId: args.conversationId,
829
+ messages: args.piMessages,
830
+ ...instructionActor ? { newMessageProvenance: instructionProvenanceFor(instructionActor) } : {},
831
+ ...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {}
832
+ });
833
+ const durableTurnStartMessageIndex = args.turnStartMessageIndex === void 0 ? void 0 : stripRuntimeTurnContext(
834
+ args.piMessages.slice(0, args.turnStartMessageIndex)
835
+ ).length;
836
+ const runtimeContext = retainRuntimeTurnContext(args.piMessages);
837
+ const retainedRuntimeContext = runtimeContext.length > 0 ? runtimeContext : existingRecord?.historyVersion === commit.historyVersion ? existingRecord.runtimeContext : void 0;
838
+ const turnStartSeq = durableTurnStartMessageIndex === void 0 ? existingRecord?.turnStartSeq : durableTurnStartMessageIndex <= 0 ? -1 : commit.messageSeqs[durableTurnStartMessageIndex - 1] ?? commit.committedSeq;
839
+ const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
840
+ return await setStoredRecord({
841
+ conversationStore: args.conversationStore,
842
+ destinationVisibility: args.destinationVisibility,
843
+ piMessages: commit.messages,
844
+ piMessageProvenance: commit.provenance,
845
+ ttlMs,
846
+ ...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {},
847
+ record: buildStoredRecord({
848
+ ...args.channelName ?? existingRecord?.channelName ? { channelName: args.channelName ?? existingRecord?.channelName } : {},
849
+ conversationId: args.conversationId,
850
+ sessionId: args.sessionId,
851
+ sliceId: args.sliceId,
852
+ state: args.state,
853
+ ...existingRecord?.startedAtMs !== void 0 ? { startedAtMs: existingRecord.startedAtMs } : {},
854
+ ...args.lastProgressAtMs !== void 0 ? { lastProgressAtMs: args.lastProgressAtMs } : {},
855
+ committedSeq: commit.committedSeq,
856
+ historyVersion: commit.historyVersion,
857
+ ...turnStartSeq !== void 0 ? { turnStartSeq } : {},
858
+ ...retainedRuntimeContext ? { runtimeContext: retainedRuntimeContext } : {},
859
+ previousVersion: existingRecord?.version,
860
+ cumulativeDurationMs: args.cumulativeDurationMs ?? existingRecord?.cumulativeDurationMs ?? 0,
861
+ ...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
862
+ ...args.destination ?? existingRecord?.destination ? { destination: args.destination ?? existingRecord?.destination } : {},
863
+ ...args.source ?? existingRecord?.source ? { source: args.source ?? existingRecord?.source } : {},
864
+ ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
865
+ modelId: args.modelId,
866
+ ...args.reasoningLevel ?? existingRecord?.reasoningLevel ? {
867
+ reasoningLevel: args.reasoningLevel ?? existingRecord?.reasoningLevel
868
+ } : {},
869
+ ...args.actor ?? existingRecord?.actor ? { actor: args.actor ?? existingRecord?.actor } : {},
870
+ actors: instructionActors([
871
+ ...(existingRecord?.actors ?? []).map(instructionProvenanceFor),
872
+ ...(args.actors ?? []).map(instructionProvenanceFor),
873
+ ...commit.provenance
874
+ ]),
875
+ ...args.resumeReason ? { resumeReason: args.resumeReason } : {},
876
+ ...args.errorMessage ? { errorMessage: args.errorMessage } : {},
877
+ ...args.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.resumedFromSliceId } : {},
878
+ ...args.surface ?? existingRecord?.surface ? { surface: args.surface ?? existingRecord?.surface } : {},
879
+ ...args.traceId ?? existingRecord?.traceId ? { traceId: args.traceId ?? existingRecord?.traceId } : {}
880
+ })
881
+ });
882
+ }
883
+ async function recordAgentTurnSessionSummary(args) {
884
+ const existing = await getStoredAgentTurnSessionRecord(
885
+ args.conversationId,
886
+ args.sessionId
887
+ );
888
+ const nowMs = Date.now();
889
+ const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
890
+ const summary = {
891
+ version: existing?.version ?? 0,
892
+ ...args.channelName ?? existing?.channelName ? { channelName: args.channelName ?? existing?.channelName } : {},
893
+ conversationId: args.conversationId,
894
+ sessionId: args.sessionId,
895
+ sliceId: args.sliceId,
896
+ startedAtMs: existing?.startedAtMs ?? args.startedAtMs ?? nowMs,
897
+ lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
898
+ state: args.state,
899
+ updatedAtMs: nowMs,
900
+ cumulativeDurationMs: args.cumulativeDurationMs ?? existing?.cumulativeDurationMs ?? 0,
901
+ ...args.cumulativeUsage ?? existing?.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage ?? existing?.cumulativeUsage } : {},
902
+ ...args.destination ?? existing?.destination ? { destination: args.destination ?? existing?.destination } : {},
903
+ ...args.source ?? existing?.source ? { source: args.source ?? existing?.source } : {},
904
+ ...args.actor ?? existing?.actor ? { actor: args.actor ?? existing?.actor } : {},
905
+ ...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : existing?.loadedSkillNames ? { loadedSkillNames: existing.loadedSkillNames } : {},
906
+ ...args.modelId ?? existing?.modelId ? { modelId: args.modelId ?? existing?.modelId } : {},
907
+ ...args.reasoningLevel ?? existing?.reasoningLevel ? { reasoningLevel: args.reasoningLevel ?? existing?.reasoningLevel } : {},
908
+ ...args.resumeReason ? { resumeReason: args.resumeReason } : {},
909
+ ...args.surface ?? existing?.surface ? { surface: args.surface ?? existing?.surface } : {},
910
+ ...args.traceId ?? existing?.traceId ? { traceId: args.traceId ?? existing?.traceId } : {}
911
+ };
912
+ await recordConversationActivityMetadata({
913
+ conversationStore: args.conversationStore,
914
+ destinationVisibility: args.destinationVisibility,
915
+ nowMs,
916
+ summary
917
+ });
918
+ await appendAgentTurnSessionSummary(summary, ttlMs);
919
+ }
920
+ async function readAgentTurnSessionSummariesFromIndex(key, stateAdapter) {
921
+ await stateAdapter.connect();
922
+ const values = await stateAdapter.getList(key);
923
+ const summaries = /* @__PURE__ */ new Map();
924
+ for (const value of [...values].reverse()) {
925
+ let summary;
926
+ try {
927
+ summary = parseAgentTurnSessionSummary(value);
928
+ } catch (error) {
929
+ logWarn(
930
+ "agent_turn_session_summary_parse_failed",
931
+ {},
932
+ {
933
+ "app.state.key": key,
934
+ "exception.message": error instanceof Error ? error.message : String(error)
935
+ },
936
+ "Skipping an invalid turn-session summary index entry"
937
+ );
938
+ continue;
939
+ }
940
+ const summaryKey = `${summary.conversationId}:${summary.sessionId}`;
941
+ if (!summaries.has(summaryKey)) {
942
+ summaries.set(summaryKey, summary);
943
+ }
944
+ }
945
+ return [...summaries.values()].sort(
946
+ (left, right) => right.updatedAtMs - left.updatedAtMs
947
+ );
948
+ }
949
+ async function listAgentTurnSessionSummariesForConversation(conversationId) {
950
+ const stateAdapter = getStateAdapter();
951
+ const summaries = await listBoundedAgentTurnSessionSummariesForConversation(conversationId);
952
+ if (summaries.length > 0) {
953
+ return summaries;
954
+ }
955
+ return (await readAgentTurnSessionSummariesFromIndex(
956
+ AGENT_TURN_SESSION_INDEX_KEY,
957
+ stateAdapter
958
+ )).filter((summary) => summary.conversationId === conversationId);
959
+ }
960
+ async function listBoundedAgentTurnSessionSummariesForConversation(conversationId) {
961
+ return readAgentTurnSessionSummariesFromIndex(
962
+ agentTurnSessionConversationIndexKey(conversationId),
963
+ getStateAdapter()
964
+ );
965
+ }
966
+ async function abandonAgentTurnSessionRecord(args) {
967
+ const existing = await getAgentTurnSessionRecord(
968
+ args.conversationId,
969
+ args.sessionId
970
+ );
971
+ if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned") {
972
+ return void 0;
973
+ }
974
+ return await updateAgentTurnSessionState({
975
+ existing,
976
+ state: "abandoned",
977
+ errorMessage: args.errorMessage ?? existing.errorMessage
978
+ });
979
+ }
980
+ async function failAgentTurnSessionRecord(args) {
981
+ const existing = await getAgentTurnSessionRecord(
982
+ args.conversationId,
983
+ args.sessionId
984
+ );
985
+ if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
986
+ return void 0;
987
+ }
988
+ return await updateAgentTurnSessionState({
989
+ existing,
990
+ state: "failed",
991
+ errorMessage: args.errorMessage ?? existing.errorMessage
992
+ });
993
+ }
994
+
148
995
  // src/chat/services/turn-limit.ts
149
996
  var TurnSliceLimitExceededError = class extends Error {
150
997
  constructor(maxSlices) {
@@ -162,9 +1009,9 @@ function logSessionRecordError(error, eventName, args, attributes, message) {
162
1009
  error,
163
1010
  eventName,
164
1011
  {
165
- slackThreadId: args.logContext.threadId,
166
- slackUserId: args.logContext.actorId,
167
- slackChannelId: args.logContext.channelId,
1012
+ messageConversationId: args.logContext.threadId,
1013
+ userId: args.logContext.actorId,
1014
+ destinationName: args.logContext.channelId,
168
1015
  runId: args.logContext.runId,
169
1016
  assistantUserName: args.logContext.assistantUserName,
170
1017
  modelId: args.modelId
@@ -319,7 +1166,6 @@ async function completeDeliveredTurn(args) {
319
1166
  sliceId: args.sliceId,
320
1167
  allMessages: args.messages,
321
1168
  loadedSkillNames: args.loadedSkillNames,
322
- logContext: args.logContext,
323
1169
  modelId: args.modelId,
324
1170
  actor: args.actor,
325
1171
  reasoningLevel: args.reasoningLevel,
@@ -530,6 +1376,122 @@ async function persistYieldSessionRecord(args) {
530
1376
  }
531
1377
  }
532
1378
 
1379
+ // src/chat/state/conversation.ts
1380
+ function defaultConversationState() {
1381
+ const nowMs = Date.now();
1382
+ return {
1383
+ schemaVersion: 1,
1384
+ messages: [],
1385
+ compactions: [],
1386
+ backfill: {},
1387
+ processing: {},
1388
+ stats: {
1389
+ estimatedContextTokens: 0,
1390
+ totalMessageCount: 0,
1391
+ compactedMessageCount: 0,
1392
+ updatedAtMs: nowMs
1393
+ },
1394
+ vision: {
1395
+ byFileId: {}
1396
+ }
1397
+ };
1398
+ }
1399
+ function coercePendingAuthState(value) {
1400
+ if (!isRecord(value)) {
1401
+ return void 0;
1402
+ }
1403
+ const kind = value.kind;
1404
+ const provider = toOptionalString(value.provider);
1405
+ const actorId = toOptionalString(value.actorId);
1406
+ const authSessionId = toOptionalString(value.authSessionId);
1407
+ const scope = toOptionalString(value.scope);
1408
+ const sessionId = toOptionalString(value.sessionId);
1409
+ const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
1410
+ if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || kind === "mcp" && !authSessionId || !sessionId || typeof linkSentAtMs !== "number") {
1411
+ return void 0;
1412
+ }
1413
+ const base = {
1414
+ provider,
1415
+ actorId,
1416
+ ...scope ? { scope } : {},
1417
+ sessionId,
1418
+ linkSentAtMs
1419
+ };
1420
+ return kind === "mcp" ? { ...base, authSessionId, kind } : { ...base, kind };
1421
+ }
1422
+ function coerceThreadConversationState(value) {
1423
+ if (!isRecord(value)) {
1424
+ return defaultConversationState();
1425
+ }
1426
+ const root = value;
1427
+ const rawConversation = isRecord(root.conversation) ? root.conversation : {};
1428
+ const base = defaultConversationState();
1429
+ const messages = [];
1430
+ const compactions = [];
1431
+ const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
1432
+ const backfill = {
1433
+ completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
1434
+ source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
1435
+ };
1436
+ const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
1437
+ const processing = {
1438
+ activeTurnId: toOptionalString(rawProcessing.activeTurnId),
1439
+ lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
1440
+ pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
1441
+ };
1442
+ const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
1443
+ const stats = {
1444
+ estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
1445
+ totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
1446
+ compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
1447
+ updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
1448
+ };
1449
+ const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
1450
+ const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
1451
+ const byFileId = {};
1452
+ for (const [fileId, value2] of Object.entries(rawVisionByFileId)) {
1453
+ if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
1454
+ if (!isRecord(value2)) continue;
1455
+ const summary = toOptionalString(value2.summary);
1456
+ const analyzedAtMs = toOptionalNumber(value2.analyzedAtMs);
1457
+ if (!summary || !analyzedAtMs) continue;
1458
+ byFileId[fileId] = {
1459
+ summary,
1460
+ analyzedAtMs
1461
+ };
1462
+ }
1463
+ return {
1464
+ schemaVersion: 1,
1465
+ messages,
1466
+ compactions,
1467
+ backfill,
1468
+ processing,
1469
+ stats,
1470
+ vision: {
1471
+ backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
1472
+ byFileId
1473
+ }
1474
+ };
1475
+ }
1476
+ function buildConversationStatePatch(conversation) {
1477
+ const {
1478
+ compactions: _compactions,
1479
+ messages: _messages,
1480
+ ...scratch
1481
+ } = conversation;
1482
+ return {
1483
+ conversation: {
1484
+ ...scratch,
1485
+ schemaVersion: 1,
1486
+ stats: {
1487
+ ...conversation.stats,
1488
+ totalMessageCount: conversation.messages.length,
1489
+ updatedAtMs: Date.now()
1490
+ }
1491
+ }
1492
+ };
1493
+ }
1494
+
533
1495
  // src/chat/services/context-budget.ts
534
1496
  var COMPACTION_TRIGGER_INPUT_RATIO = 0.75;
535
1497
  var COMPACTION_OUTPUT_RESERVE_RATIO = 0.25;
@@ -804,9 +1766,9 @@ async function summarizeConversationChunk(messages, conversation, context, deps)
804
1766
  logWarn(
805
1767
  "conversation_compaction_summary_failed",
806
1768
  {
807
- slackThreadId: context.threadId,
808
- slackUserId: context.actorId,
809
- slackChannelId: context.channelId,
1769
+ messageConversationId: context.threadId,
1770
+ userId: context.actorId,
1771
+ destinationName: context.channelId,
810
1772
  runId: context.runId,
811
1773
  assistantUserName: botConfig.userName,
812
1774
  modelId: botConfig.fastModelId
@@ -923,10 +1885,10 @@ function getConversationMessageSlackTs(message) {
923
1885
 
924
1886
  // src/chat/conversations/messages.ts
925
1887
  import { createHash } from "crypto";
926
- import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
1888
+ import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
927
1889
 
928
1890
  // src/chat/conversations/message-summaries.ts
929
- import { isDeepStrictEqual } from "util";
1891
+ import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
930
1892
  function projectConversationMessageSummaries(events) {
931
1893
  for (let index = events.length - 1; index >= 0; index -= 1) {
932
1894
  const data = events[index]?.data;
@@ -940,7 +1902,7 @@ async function persistConversationMessageSummaries(args) {
940
1902
  const eventStore = getConversationEventStore();
941
1903
  const history = await eventStore.loadMessageHistory(args.conversationId);
942
1904
  const existing = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
943
- if (isDeepStrictEqual(existing, args.conversation.compactions)) {
1905
+ if (isDeepStrictEqual2(existing, args.conversation.compactions)) {
944
1906
  return;
945
1907
  }
946
1908
  const liveMessageIds = new Set(
@@ -1078,7 +2040,7 @@ async function persistConversationMessages(args) {
1078
2040
  const events = args.conversation.messages.flatMap((message) => {
1079
2041
  const stored = toStoredConversationMessage(message);
1080
2042
  const existing = existingMessages.get(stored.messageId);
1081
- const changed = existing !== void 0 && !isDeepStrictEqual2(toStoredConversationMessage(existing), stored);
2043
+ const changed = existing !== void 0 && !isDeepStrictEqual3(toStoredConversationMessage(existing), stored);
1082
2044
  const messageEvent = existing ? changed ? [
1083
2045
  {
1084
2046
  idempotencyKey: `message:${stored.messageId}:update:${messageStateKey(stored)}`,
@@ -1372,6 +2334,9 @@ function createChannelConfigurationService(storage) {
1372
2334
  };
1373
2335
  }
1374
2336
 
2337
+ // src/chat/state/ttl.ts
2338
+ var JUNIOR_THREAD_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
2339
+
1375
2340
  // src/chat/runtime/thread-state.ts
1376
2341
  function threadStateKey(threadId) {
1377
2342
  return `thread-state:${threadId}`;
@@ -1565,6 +2530,15 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
1565
2530
  }
1566
2531
 
1567
2532
  export {
2533
+ loadProjection,
2534
+ loadConversationProjection,
2535
+ openConversationProjection,
2536
+ loadConnectedMcpProviders,
2537
+ commitMessages,
2538
+ recordMcpProviderConnected,
2539
+ recordAuthorizationRequested,
2540
+ recordAuthorizationCompleted,
2541
+ recordToolExecutionStarted,
1568
2542
  buildDeterministicTurnId,
1569
2543
  buildDeterministicAssistantMessageId,
1570
2544
  ConversationTurnBoundaryError,
@@ -1579,6 +2553,12 @@ export {
1579
2553
  markTurnClosed,
1580
2554
  markTurnCompleted,
1581
2555
  markTurnFailed,
2556
+ getAgentTurnSessionRecord,
2557
+ getAgentTurnSessionRecordForResume,
2558
+ recordAgentTurnSessionSummary,
2559
+ listAgentTurnSessionSummariesForConversation,
2560
+ abandonAgentTurnSessionRecord,
2561
+ failAgentTurnSessionRecord,
1582
2562
  persistWithRetry,
1583
2563
  TurnSliceLimitExceededError,
1584
2564
  buildTurnLimitResponse,
@@ -1590,11 +2570,13 @@ export {
1590
2570
  persistContinuationSessionRecord,
1591
2571
  persistYieldSessionRecord,
1592
2572
  getInterruptionMarker,
2573
+ JUNIOR_THREAD_STATE_TTL_MS,
1593
2574
  canReusePendingAuthLink,
1594
2575
  getConversationPendingAuth,
1595
2576
  clearPendingAuth,
1596
2577
  abandonReplacedPendingAuth,
1597
2578
  isPendingAuthLatestRequest,
2579
+ coerceThreadConversationState,
1598
2580
  persistConversationMessageSummaries,
1599
2581
  estimateTextTokens,
1600
2582
  getAgentContextCompactionTriggerTokens,