@sentry/junior 0.103.0 → 0.104.1

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 (77) hide show
  1. package/dist/{agent-hooks-NHIXBJW3.js → agent-hooks-EOFSY2PD.js} +3 -3
  2. package/dist/api/conversations/archive.d.ts +7 -0
  3. package/dist/api/conversations/detail.d.ts +7 -0
  4. package/dist/api/conversations/list.d.ts +109 -0
  5. package/dist/api/conversations/routes.d.ts +3 -0
  6. package/dist/api/conversations/schema.d.ts +72 -69
  7. package/dist/api/conversations/stats.d.ts +7 -0
  8. package/dist/api/conversations/subagent.d.ts +7 -0
  9. package/dist/api/http.d.ts +5 -0
  10. package/dist/api/locations/detail.d.ts +10 -2
  11. package/dist/api/locations/list.d.ts +7 -0
  12. package/dist/api/locations/routes.d.ts +3 -0
  13. package/dist/api/locations/schema.d.ts +4 -3
  14. package/dist/api/people/list.d.ts +7 -0
  15. package/dist/api/people/profile.d.ts +7 -0
  16. package/dist/api/people/routes.d.ts +3 -0
  17. package/dist/api/people/schema.d.ts +35 -33
  18. package/dist/api/people/shared.d.ts +3 -3
  19. package/dist/api/route.d.ts +7 -0
  20. package/dist/api/schema.js +11 -17
  21. package/dist/api.js +2184 -88
  22. package/dist/app.js +107 -101
  23. package/dist/chat/conversations/history.d.ts +6 -6
  24. package/dist/chat/conversations/store.d.ts +1 -0
  25. package/dist/chat/pi/messages.d.ts +2 -2
  26. package/dist/chat/plugins/agent-hooks.d.ts +2 -2
  27. package/dist/chat/resource-events/store.d.ts +2 -2
  28. package/dist/chat/slack/tools/conversation-search.d.ts +2 -2
  29. package/dist/chat/state/session-log.d.ts +2 -2
  30. package/dist/chat/task-execution/store.d.ts +2 -2
  31. package/dist/chat/tools/search-tools.d.ts +2 -2
  32. package/dist/chunk-AIRE7Q2W.js +362 -0
  33. package/dist/{chunk-HOBDQQJY.js → chunk-BCULHGZK.js} +23 -3
  34. package/dist/{chunk-DEQZ23O3.js → chunk-O7ILGCMJ.js} +77 -0
  35. package/dist/{chunk-C4BT2P2K.js → chunk-QDKRE3LS.js} +339 -8
  36. package/dist/{chunk-GY5ZRYPH.js → chunk-QVTVCJR4.js} +5 -5
  37. package/dist/{chunk-JXQWOCBE.js → chunk-RMZ65YAA.js} +2 -2
  38. package/dist/{chunk-E2VS47AZ.js → chunk-RYDF4GOX.js} +2 -2
  39. package/dist/{chunk-OE453VRG.js → chunk-TMWUGGSA.js} +26 -28
  40. package/dist/{chunk-JPXLAJUU.js → chunk-VDHMFCDY.js} +9 -10
  41. package/dist/cli/chat.js +10 -11
  42. package/dist/cli/plugins.js +4 -4
  43. package/dist/cli/upgrade.js +3 -4
  44. package/dist/db/schema/conversations.d.ts +17 -0
  45. package/dist/db/schema.d.ts +17 -0
  46. package/dist/{db-I343BSOM.js → db-PDMC67T3.js} +1 -1
  47. package/dist/{legacy-import-M7VBOQTK.js → legacy-import-G333RHJZ.js} +2 -2
  48. package/dist/reporting-schema.d.ts +4 -4
  49. package/dist/{runner-IEE5G4NV.js → runner-6A7MQRUE.js} +7 -8
  50. package/migrations/0004_useful_magus.sql +1 -0
  51. package/migrations/meta/0004_snapshot.json +1041 -0
  52. package/migrations/meta/_journal.json +7 -0
  53. package/package.json +6 -6
  54. package/dist/api/conversations/list.query.d.ts +0 -103
  55. package/dist/chunk-2RNOB32R.js +0 -17
  56. package/dist/chunk-6FPTHPXF.js +0 -77
  57. package/dist/chunk-6LBNXC4F.js +0 -66
  58. package/dist/chunk-7LLRS2OK.js +0 -215
  59. package/dist/chunk-B43K4C5K.js +0 -102
  60. package/dist/chunk-BQCPAIAB.js +0 -319
  61. package/dist/chunk-FW7DVHTL.js +0 -673
  62. package/dist/chunk-GCGRQJR5.js +0 -84
  63. package/dist/chunk-HRDWLE7Q.js +0 -273
  64. package/dist/chunk-LVUKF5CE.js +0 -63
  65. package/dist/chunk-QNVHH5PU.js +0 -20
  66. package/dist/chunk-QOZOJNSE.js +0 -96
  67. package/dist/chunk-VKFOVMZL.js +0 -331
  68. package/dist/detail-ENVV4HIY.js +0 -68
  69. package/dist/detail-UIOKVIET.js +0 -29
  70. package/dist/list-WR5U6JVM.js +0 -30
  71. package/dist/list-XZ4HKQ4R.js +0 -121
  72. package/dist/list-ZOUSMSEH.js +0 -30
  73. package/dist/plugin-conversations-DTUP4RH6.js +0 -37
  74. package/dist/profile-UO2N3XXG.js +0 -212
  75. package/dist/reporting/plugin-conversations.d.ts +0 -3
  76. package/dist/stats-NZIET4LT.js +0 -174
  77. package/dist/subagent-DY6XKTJ5.js +0 -49
@@ -1,673 +0,0 @@
1
- import {
2
- conversationSummaryFromStoredConversation
3
- } from "./chunk-HRDWLE7Q.js";
4
- import {
5
- buildSentryConversationUrl,
6
- buildSentryTraceUrl
7
- } from "./chunk-LVUKF5CE.js";
8
- import {
9
- loadProjection,
10
- projectSteps
11
- } from "./chunk-BQCPAIAB.js";
12
- import {
13
- getAgentStepStore,
14
- getConversationMessageStore
15
- } from "./chunk-HOBDQQJY.js";
16
- import {
17
- stripRuntimeTurnContext,
18
- unescapeXml,
19
- unwrapCurrentInstruction
20
- } from "./chunk-M3UAVLEN.js";
21
- import {
22
- canExposeConversationPayload
23
- } from "./chunk-RT5BUHR5.js";
24
- import {
25
- isRecord
26
- } from "./chunk-OB42YVAE.js";
27
-
28
- // src/api/conversations/detail-projection.ts
29
- import { isDeepStrictEqual } from "util";
30
-
31
- // src/api/conversations/transcript.ts
32
- var SAFE_METADATA_KEY_LIMIT = 20;
33
- var LEGACY_ADVISOR_TASK_OPEN = "<advisor-task>\n";
34
- var LEGACY_ADVISOR_TASK_CLOSE = "\n</advisor-task>";
35
- var LEGACY_EXECUTOR_CONTEXT_OPEN = "<executor-context>\n";
36
- var LEGACY_EXECUTOR_CONTEXT_CLOSE = "\n</executor-context>";
37
- function unwrapLegacyAdvisorTask(text) {
38
- if (!text.startsWith(LEGACY_ADVISOR_TASK_OPEN) || !text.endsWith(LEGACY_EXECUTOR_CONTEXT_CLOSE)) {
39
- return void 0;
40
- }
41
- const taskEnd = text.indexOf(
42
- LEGACY_ADVISOR_TASK_CLOSE,
43
- LEGACY_ADVISOR_TASK_OPEN.length
44
- );
45
- if (taskEnd < 0) return void 0;
46
- const contextStart = taskEnd + LEGACY_ADVISOR_TASK_CLOSE.length + 2;
47
- if (!text.startsWith(LEGACY_EXECUTOR_CONTEXT_OPEN, contextStart)) {
48
- return void 0;
49
- }
50
- const task = text.slice(LEGACY_ADVISOR_TASK_OPEN.length, taskEnd);
51
- const context = text.slice(
52
- contextStart + LEGACY_EXECUTOR_CONTEXT_OPEN.length,
53
- -LEGACY_EXECUTOR_CONTEXT_CLOSE.length
54
- );
55
- return `${unescapeXml(task)}
56
-
57
- Executor context:
58
- ${unescapeXml(context)}`;
59
- }
60
- function textPart(text) {
61
- return { type: "text", text };
62
- }
63
- function recordField(value, names) {
64
- for (const name of names) {
65
- if (value[name] !== void 0) {
66
- return value[name];
67
- }
68
- }
69
- return void 0;
70
- }
71
- function thinkingOutput(part) {
72
- const output = recordField(part, ["thinking", "text", "content", "output"]);
73
- return isRecord(output) ? recordField(output, ["thinking", "text", "content", "output"]) : output;
74
- }
75
- function isDisplayableTranscriptPart(part) {
76
- if (part.type !== "thinking") return true;
77
- return typeof part.output === "string" && part.output.trim().length > 0;
78
- }
79
- function normalizeTranscriptPart(part, options = {}) {
80
- const displayText = (text) => {
81
- if (options.unwrapCurrentTask) {
82
- const instruction = unwrapCurrentInstruction(text);
83
- if (instruction !== void 0) return instruction;
84
- }
85
- if (options.unwrapLegacyAdvisorTask) {
86
- return unwrapLegacyAdvisorTask(text) ?? text;
87
- }
88
- return text;
89
- };
90
- if (typeof part === "string") {
91
- return textPart(displayText(part));
92
- }
93
- if (!isRecord(part)) {
94
- return { type: "unknown", output: part };
95
- }
96
- const rawType = typeof part.type === "string" ? part.type : "unknown";
97
- if (rawType === "text") {
98
- const text = recordField(part, ["text", "content"]);
99
- return textPart(
100
- typeof text === "string" ? displayText(text) : JSON.stringify(text) ?? ""
101
- );
102
- }
103
- if (rawType === "toolCall") {
104
- return {
105
- type: "tool_call",
106
- ...typeof part.id === "string" ? { id: part.id } : {},
107
- ...typeof part.name === "string" ? { name: part.name } : {},
108
- input: recordField(part, ["arguments", "input", "args"])
109
- };
110
- }
111
- if (rawType === "toolResult") {
112
- return {
113
- type: "tool_result",
114
- ...typeof part.id === "string" ? { id: part.id } : {},
115
- ...typeof part.name === "string" ? { name: part.name } : {},
116
- output: recordField(part, ["result", "output", "content"])
117
- };
118
- }
119
- if (rawType === "thinking") {
120
- return {
121
- type: "thinking",
122
- output: thinkingOutput(part)
123
- };
124
- }
125
- return {
126
- type: "unknown",
127
- ...rawType !== "unknown" ? { sourceType: rawType } : {},
128
- output: part
129
- };
130
- }
131
- function normalizeToolResultMessage(record) {
132
- const content = record.content;
133
- let output = content;
134
- if (Array.isArray(content) && content.length === 1 && isRecord(content[0])) {
135
- const extracted = recordField(content[0], [
136
- "text",
137
- "content",
138
- "output",
139
- "result"
140
- ]);
141
- output = extracted !== void 0 ? extracted : content;
142
- }
143
- return {
144
- type: "tool_result",
145
- ...typeof record.toolCallId === "string" ? { id: record.toolCallId } : {},
146
- ...typeof record.name === "string" ? { name: record.name } : typeof record.toolName === "string" ? { name: record.toolName } : {},
147
- output
148
- };
149
- }
150
- function normalizeTranscriptMessage(message) {
151
- return normalizeMessage(message);
152
- }
153
- function normalizeSubagentTranscriptMessage(message, subagentKind) {
154
- return normalizeMessage(message, {
155
- unwrapLegacyAdvisorTask: subagentKind === "advisor"
156
- });
157
- }
158
- function normalizeMessage(message, options = {}) {
159
- const record = message;
160
- const content = record.content;
161
- const role = transcriptRole(record.role);
162
- return {
163
- role,
164
- ...role === "assistant" && (record.stopReason === "error" || record.stopReason === "aborted") ? { outcome: record.stopReason } : {},
165
- ...typeof record.timestamp === "number" ? { timestamp: record.timestamp } : {},
166
- parts: role === "toolResult" ? [normalizeToolResultMessage(record)] : Array.isArray(content) ? content.map(
167
- (part) => normalizeTranscriptPart(part, {
168
- unwrapCurrentTask: role === "user",
169
- unwrapLegacyAdvisorTask: options.unwrapLegacyAdvisorTask && role === "user"
170
- })
171
- ).filter(isDisplayableTranscriptPart) : [
172
- normalizeTranscriptPart(content, {
173
- unwrapCurrentTask: role === "user",
174
- unwrapLegacyAdvisorTask: options.unwrapLegacyAdvisorTask && role === "user"
175
- })
176
- ]
177
- };
178
- }
179
- function transcriptRole(role) {
180
- return role === "assistant" || role === "system" || role === "tool" || role === "toolResult" || role === "user" ? role : "unknown";
181
- }
182
- function serializedChars(value) {
183
- if (typeof value === "string") return value.length;
184
- return JSON.stringify(value)?.length ?? 0;
185
- }
186
- function serializedBytes(value) {
187
- const serialized = typeof value === "string" ? value : JSON.stringify(value);
188
- return new TextEncoder().encode(serialized ?? "").byteLength;
189
- }
190
- function payloadType(value) {
191
- return Array.isArray(value) ? "array" : typeof value;
192
- }
193
- function payloadKeys(value) {
194
- if (!value || typeof value !== "object" || Array.isArray(value)) {
195
- return void 0;
196
- }
197
- const keys = Object.keys(value).slice(
198
- 0,
199
- SAFE_METADATA_KEY_LIMIT
200
- );
201
- return keys.length > 0 ? keys : void 0;
202
- }
203
- function redactedPayloadFields(prefix, value) {
204
- const keys = payloadKeys(value);
205
- return {
206
- [`${prefix}Type`]: payloadType(value),
207
- [`${prefix}SizeBytes`]: serializedBytes(value),
208
- [`${prefix}SizeChars`]: serializedChars(value),
209
- ...keys ? { [`${prefix}Keys`]: keys } : {}
210
- };
211
- }
212
- function redactTranscriptPart(part) {
213
- if (part.type === "text") {
214
- return {
215
- type: "text",
216
- redacted: true,
217
- bytes: serializedBytes(part.text ?? ""),
218
- chars: serializedChars(part.text ?? "")
219
- };
220
- }
221
- if (part.type === "thinking") {
222
- return {
223
- type: "thinking",
224
- redacted: true,
225
- ...redactedPayloadFields("output", part.output)
226
- };
227
- }
228
- if (part.type === "tool_call") {
229
- return {
230
- type: "tool_call",
231
- redacted: true,
232
- ...part.id ? { id: part.id } : {},
233
- ...part.name ? { name: part.name } : {},
234
- ...redactedPayloadFields("input", part.input)
235
- };
236
- }
237
- if (part.type === "tool_result") {
238
- return {
239
- type: "tool_result",
240
- redacted: true,
241
- ...part.id ? { id: part.id } : {},
242
- ...part.name ? { name: part.name } : {},
243
- ...redactedPayloadFields("output", part.output)
244
- };
245
- }
246
- return {
247
- type: "unknown",
248
- redacted: true,
249
- ...part.sourceType ? { sourceType: part.sourceType } : {},
250
- ...redactedPayloadFields("output", part.output ?? part.input ?? part.text)
251
- };
252
- }
253
- function redactTranscriptMessage(message) {
254
- return {
255
- role: message.role,
256
- ...message.outcome ? { outcome: message.outcome } : {},
257
- ...typeof message.timestamp === "number" ? { timestamp: message.timestamp } : {},
258
- parts: message.parts.map(redactTranscriptPart)
259
- };
260
- }
261
- function isConversationMessageRole(role) {
262
- return role === "user" || role === "assistant";
263
- }
264
- function hasTextPart(message) {
265
- return message.parts.some((part) => {
266
- if (part.type !== "text") return false;
267
- if (part.redacted) return true;
268
- return typeof part.text === "string" && part.text.trim().length > 0;
269
- });
270
- }
271
- function isConversationMessage(message) {
272
- if (!isConversationMessageRole(message.role)) return false;
273
- if (message.role === "assistant") return hasTextPart(message);
274
- return message.parts.length > 0;
275
- }
276
- function countConversationMessages(transcript) {
277
- return transcript.filter(isConversationMessage).length;
278
- }
279
- function traceIdFromTranscript(transcript) {
280
- for (const message of transcript) {
281
- for (const part of message.parts) {
282
- const text = part.text ?? (typeof part.output === "string" ? part.output : typeof part.input === "string" ? part.input : void 0);
283
- const match = text?.match(
284
- /\btrace[_-]?id["']?\s*[:=]\s*["']?([a-f0-9]{16,32})\b/i
285
- );
286
- if (match?.[1]) {
287
- return match[1];
288
- }
289
- }
290
- }
291
- return void 0;
292
- }
293
- function subagentTranscriptReport(activity, options = {}) {
294
- return {
295
- type: "subagent",
296
- ...options.subagentConversationId ? { subagentConversationId: options.subagentConversationId } : {},
297
- createdAt: activity.createdAt,
298
- id: activity.id,
299
- ...activity.modelId ? { modelId: activity.modelId } : {},
300
- status: activity.status,
301
- ...options.subagentSentryConversationUrl ? { subagentSentryConversationUrl: options.subagentSentryConversationUrl } : {},
302
- subagentKind: activity.subagentKind,
303
- transcript: options.transcript ?? [],
304
- transcriptAvailable: Boolean(options.transcript?.length),
305
- ...activity.endedAt ? { endedAt: activity.endedAt } : {},
306
- ...activity.outcome ? { outcome: activity.outcome } : {},
307
- ...activity.parentToolCallId ? { parentToolCallId: activity.parentToolCallId } : {},
308
- ...activity.reasoningLevel ? { reasoningLevel: activity.reasoningLevel } : {},
309
- ...options.transcriptMessageCount !== void 0 ? { transcriptMessageCount: options.transcriptMessageCount } : {},
310
- ...options.transcriptRedacted ? { transcriptRedacted: options.transcriptRedacted } : {},
311
- ...options.transcriptRedactionReason ? { transcriptRedactionReason: options.transcriptRedactionReason } : {},
312
- ...options.transcriptExpired ? { transcriptExpired: options.transcriptExpired } : {},
313
- ...options.transcriptExpiredAt ? { transcriptExpiredAt: options.transcriptExpiredAt } : {},
314
- ...options.unavailableReason ? { unavailableReason: options.unavailableReason } : {}
315
- };
316
- }
317
-
318
- // src/api/conversations/activity.ts
319
- function toolResultStatuses(messages) {
320
- const statuses = /* @__PURE__ */ new Map();
321
- for (const message of messages) {
322
- const record = message;
323
- if (record.role !== "toolResult" || typeof record.toolCallId !== "string") {
324
- continue;
325
- }
326
- statuses.set(record.toolCallId, record.isError ? "error" : "completed");
327
- }
328
- return statuses;
329
- }
330
- function activityPayloadFields(args, canExposePayload) {
331
- if (args === void 0) {
332
- return {};
333
- }
334
- return canExposePayload ? { args } : { redacted: true, ...redactedPayloadFields("input", args) };
335
- }
336
- function buildConversationActivityFromSteps(args) {
337
- const toolStatuses = toolResultStatuses(args.messages);
338
- const subagentEnds = /* @__PURE__ */ new Map();
339
- const subagentsByToolCallId = /* @__PURE__ */ new Map();
340
- const orphanSubagents = [];
341
- for (const step of args.steps) {
342
- if (step.entry.type === "subagent_ended") {
343
- subagentEnds.set(
344
- step.entry.subagentInvocationId,
345
- step
346
- );
347
- }
348
- }
349
- for (const step of args.steps) {
350
- if (step.entry.type !== "subagent_started") {
351
- continue;
352
- }
353
- const start = step;
354
- const parentStatus = start.entry.parentToolCallId ? toolStatuses.get(start.entry.parentToolCallId) : void 0;
355
- const activity = subagentActivityFromSteps(
356
- start,
357
- subagentEnds.get(start.entry.subagentInvocationId),
358
- { canExposeTranscript: args.canExposePayload, parentStatus }
359
- );
360
- if (start.entry.parentToolCallId) {
361
- subagentsByToolCallId.set(start.entry.parentToolCallId, [
362
- ...subagentsByToolCallId.get(start.entry.parentToolCallId) ?? [],
363
- activity
364
- ]);
365
- continue;
366
- }
367
- orphanSubagents.push(activity);
368
- }
369
- const rows = [];
370
- for (const step of args.steps) {
371
- if (step.entry.type !== "tool_execution_started") {
372
- continue;
373
- }
374
- rows.push({
375
- type: "tool_execution",
376
- id: step.entry.toolCallId,
377
- toolCallId: step.entry.toolCallId,
378
- toolName: step.entry.toolName,
379
- createdAt: new Date(step.createdAtMs).toISOString(),
380
- status: toolStatuses.get(step.entry.toolCallId) ?? "running",
381
- subagents: subagentsByToolCallId.get(step.entry.toolCallId) ?? [],
382
- ...activityPayloadFields(step.entry.args, args.canExposePayload)
383
- });
384
- }
385
- return [...rows, ...orphanSubagents].sort(
386
- (left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt) || left.id.localeCompare(right.id)
387
- );
388
- }
389
- function subagentActivityFromSteps(start, end, options = {}) {
390
- return {
391
- type: "subagent",
392
- id: start.entry.subagentInvocationId,
393
- subagentKind: start.entry.subagentKind,
394
- ...start.entry.modelId ? { modelId: start.entry.modelId } : {},
395
- ...start.entry.parentToolCallId ? { parentToolCallId: start.entry.parentToolCallId } : {},
396
- ...start.entry.reasoningLevel ? { reasoningLevel: start.entry.reasoningLevel } : {},
397
- createdAt: new Date(start.createdAtMs).toISOString(),
398
- ...end ? {
399
- endedAt: new Date(end.createdAtMs).toISOString(),
400
- outcome: end.entry.outcome,
401
- status: end.entry.outcome,
402
- // Every subagent is a child conversation whose transcript loads on
403
- // demand; expose the affordance only when the parent is public.
404
- ...options.canExposeTranscript ? { transcriptAvailable: true } : {}
405
- } : { status: options.parentStatus ?? "running" }
406
- };
407
- }
408
-
409
- // src/api/conversations/detail-projection.ts
410
- var COMPACTION_SUMMARY_PREFIXES = [
411
- "Context compaction summary for future Junior turns:",
412
- "Context handoff summary for future Junior turns:"
413
- ];
414
- var MODEL_HANDOFF_SUMMARY_PREFIX = "Model handoff checkpoint. Continue the outstanding request now using this summary as the complete prior context:";
415
- function messageText(message) {
416
- return normalizeTranscriptMessage(message).parts.filter((part) => part.type === "text").map((part) => part.text ?? "").join("\n").trim();
417
- }
418
- function summaryAfterPrefix(message, prefixes) {
419
- const text = messageText(message);
420
- const prefix = prefixes.find((candidate) => text.startsWith(candidate));
421
- if (!prefix) return void 0;
422
- return text.slice(prefix.length).trim();
423
- }
424
- function summaryIndex(messages, provenance, prefixes) {
425
- for (let index = messages.length - 1; index >= 0; index -= 1) {
426
- if (provenance[index]?.authority === "context" && summaryAfterPrefix(messages[index], prefixes) !== void 0) {
427
- return index;
428
- }
429
- }
430
- return -1;
431
- }
432
- function matchingPrefix(left, right) {
433
- const limit = Math.min(left.length, right.length);
434
- for (let index = 0; index < limit; index += 1) {
435
- if (!isDeepStrictEqual(left[index], right[index])) return index;
436
- }
437
- return limit;
438
- }
439
- function historyContent(args) {
440
- const contextEvents = [];
441
- const messages = [];
442
- const epochs = /* @__PURE__ */ new Map();
443
- for (const step of args.steps) {
444
- const epoch = epochs.get(step.contextEpoch);
445
- if (epoch) epoch.push(step);
446
- else epochs.set(step.contextEpoch, [step]);
447
- }
448
- let previousModelId;
449
- let previousProjection = [];
450
- for (const steps of epochs.values()) {
451
- const marker = steps.find(
452
- (step) => step.entry.type === "context_epoch_started"
453
- );
454
- const projection = projectSteps(steps);
455
- const projected = [];
456
- const projectedProvenance = [];
457
- projection.messages.forEach((message, index) => {
458
- for (const retained of stripRuntimeTurnContext([message])) {
459
- projected.push(retained);
460
- projectedProvenance.push(projection.provenance[index]);
461
- }
462
- });
463
- const replacementSummaryIndex = marker?.entry.reason === "compaction" ? summaryIndex(
464
- projected,
465
- projectedProvenance,
466
- COMPACTION_SUMMARY_PREFIXES
467
- ) : marker?.entry.reason === "handoff" ? summaryIndex(projected, projectedProvenance, [
468
- MODEL_HANDOFF_SUMMARY_PREFIX
469
- ]) : -1;
470
- const summary = marker?.entry.reason === "compaction" && replacementSummaryIndex >= 0 ? summaryAfterPrefix(
471
- projected[replacementSummaryIndex],
472
- COMPACTION_SUMMARY_PREFIXES
473
- ) : void 0;
474
- const handoffMessage = marker?.entry.reason === "handoff" && replacementSummaryIndex >= 0 ? messageText(projected[replacementSummaryIndex]) || void 0 : void 0;
475
- if (marker?.entry.reason === "compaction") {
476
- contextEvents.push({
477
- type: "context_compacted",
478
- createdAt: new Date(marker.createdAtMs).toISOString(),
479
- ...marker.entry.modelId ? { modelId: marker.entry.modelId } : {},
480
- ...args.canExposePayload && summary ? { summary } : {},
481
- transcriptIndex: messages.length
482
- });
483
- } else if (marker?.entry.reason === "handoff") {
484
- contextEvents.push({
485
- type: "model_handoff",
486
- createdAt: new Date(marker.createdAtMs).toISOString(),
487
- ...previousModelId ? { fromModelId: previousModelId } : {},
488
- toModelId: marker.entry.modelId,
489
- ...args.canExposePayload && handoffMessage ? { message: handoffMessage } : {},
490
- transcriptIndex: messages.length
491
- });
492
- }
493
- if (marker?.entry.reason === "rollback") {
494
- messages.push(
495
- ...projected.slice(matchingPrefix(previousProjection, projected))
496
- );
497
- } else {
498
- const copiedMessageIndexes = /* @__PURE__ */ new Set();
499
- projected.forEach((message, index) => {
500
- if (index === replacementSummaryIndex) return;
501
- let copiedCompactionMessage = false;
502
- if (marker?.entry.reason === "compaction" && replacementSummaryIndex >= 0 && index < replacementSummaryIndex) {
503
- const copiedIndex = messages.findIndex(
504
- (candidate, candidateIndex) => !copiedMessageIndexes.has(candidateIndex) && isDeepStrictEqual(candidate, message)
505
- );
506
- copiedCompactionMessage = copiedIndex >= 0;
507
- if (copiedCompactionMessage) copiedMessageIndexes.add(copiedIndex);
508
- }
509
- if (!copiedCompactionMessage) messages.push(message);
510
- });
511
- }
512
- previousModelId = marker?.entry.modelId ?? previousModelId;
513
- previousProjection = projected;
514
- }
515
- return { contextEvents, messages };
516
- }
517
- async function conversationContent(args) {
518
- const steps = await args.stepStore.loadHistory(args.conversationId);
519
- const history = historyContent({
520
- canExposePayload: args.canExposePayload,
521
- steps
522
- });
523
- const messages = history.messages;
524
- const transcript = messages.length > 0 ? messages.map((message) => normalizeTranscriptMessage(message)) : (await args.messageStore.list(args.conversationId)).map(
525
- visibleMessageTranscript
526
- );
527
- return {
528
- activity: buildConversationActivityFromSteps({
529
- canExposePayload: args.canExposePayload,
530
- steps,
531
- messages
532
- }),
533
- contextEvents: history.contextEvents,
534
- transcript
535
- };
536
- }
537
- function visibleMessageTranscript(message) {
538
- return {
539
- role: message.role,
540
- timestamp: message.createdAtMs,
541
- parts: [{ type: "text", text: message.text }]
542
- };
543
- }
544
- async function buildConversationDetail(args) {
545
- const { conversation } = args;
546
- const conversationId = conversation.conversationId;
547
- const nowMs = Date.now();
548
- const stepStore = getAgentStepStore();
549
- const messageStore = getConversationMessageStore();
550
- const transcriptPurgedAtMs = conversation.transcriptPurgedAtMs;
551
- const transcriptExpiredAt = transcriptPurgedAtMs !== void 0 ? new Date(transcriptPurgedAtMs).toISOString() : void 0;
552
- const canExposeSqlContent = canExposeConversationPayload({
553
- conversationId,
554
- visibility: conversation.visibility
555
- });
556
- const currentContent = transcriptPurgedAtMs === void 0 ? await conversationContent({
557
- conversationId,
558
- messageStore,
559
- stepStore,
560
- canExposePayload: canExposeSqlContent
561
- }) : { activity: [], contextEvents: [], transcript: [] };
562
- const currentTranscript = currentContent.transcript;
563
- const traceId = canExposeSqlContent ? traceIdFromTranscript(currentTranscript) : void 0;
564
- const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
565
- const sentryConversationUrl = buildSentryConversationUrl(conversationId);
566
- return {
567
- ...conversationSummaryFromStoredConversation({
568
- conversation,
569
- durationMs: args.durationMs,
570
- ...args.locationId ? { locationId: args.locationId } : {},
571
- usage: args.usage
572
- }),
573
- ...traceId ? { traceId } : {},
574
- ...sentryTraceUrl ? { sentryTraceUrl } : {},
575
- activity: currentContent.activity,
576
- contextEvents: currentContent.contextEvents,
577
- transcriptAvailable: transcriptExpiredAt === void 0 && canExposeSqlContent && currentTranscript.length > 0,
578
- ...currentTranscript.length > 0 ? {
579
- transcriptMessageCount: countConversationMessages(currentTranscript)
580
- } : {},
581
- ...!canExposeSqlContent && transcriptExpiredAt === void 0 ? {
582
- transcriptMetadata: currentTranscript.map(redactTranscriptMessage),
583
- transcriptRedacted: true,
584
- transcriptRedactionReason: "non_public_conversation"
585
- } : {},
586
- ...transcriptExpiredAt !== void 0 ? {
587
- transcriptExpired: true,
588
- transcriptExpiredAt,
589
- transcriptMetadata: []
590
- } : {},
591
- transcript: transcriptExpiredAt === void 0 && canExposeSqlContent ? currentTranscript : [],
592
- generatedAt: new Date(nowMs).toISOString(),
593
- ...sentryConversationUrl ? { sentryConversationUrl } : {}
594
- };
595
- }
596
- async function buildConversationSubagent(conversation, subagentId) {
597
- const conversationId = conversation.conversationId;
598
- const stepStore = getAgentStepStore();
599
- const parentSteps = await stepStore.loadHistory(conversationId);
600
- if (conversation?.transcriptPurgedAtMs !== void 0) {
601
- return {
602
- type: "subagent",
603
- createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
604
- id: subagentId,
605
- status: "completed",
606
- subagentKind: "unknown",
607
- transcript: [],
608
- transcriptAvailable: false,
609
- transcriptExpired: true,
610
- transcriptExpiredAt: new Date(
611
- conversation.transcriptPurgedAtMs
612
- ).toISOString()
613
- };
614
- }
615
- const start = parentSteps.find(
616
- (step) => step.entry.type === "subagent_started" && step.entry.subagentInvocationId === subagentId
617
- );
618
- if (!start) {
619
- return {
620
- type: "subagent",
621
- createdAt: (/* @__PURE__ */ new Date(0)).toISOString(),
622
- id: subagentId,
623
- status: "error",
624
- subagentKind: "unknown",
625
- transcript: [],
626
- transcriptAvailable: false,
627
- unavailableReason: "not_found"
628
- };
629
- }
630
- const end = parentSteps.find(
631
- (step) => step.entry.type === "subagent_ended" && step.entry.subagentInvocationId === subagentId
632
- );
633
- const childConversationId = start.entry.childConversationId;
634
- const activity = subagentActivityFromSteps(start, end);
635
- const subagentSentryConversationUrl = buildSentryConversationUrl(childConversationId);
636
- const conversationFields = {
637
- subagentConversationId: childConversationId,
638
- ...subagentSentryConversationUrl ? { subagentSentryConversationUrl } : {}
639
- };
640
- const canExposeTranscript = canExposeConversationPayload({
641
- conversationId,
642
- visibility: conversation?.visibility
643
- });
644
- if (!canExposeTranscript) {
645
- return subagentTranscriptReport(activity, {
646
- ...conversationFields,
647
- transcriptRedacted: true,
648
- transcriptRedactionReason: "non_public_conversation"
649
- });
650
- }
651
- const childMessages = await loadProjection({
652
- conversationId: childConversationId
653
- });
654
- if (childMessages.length === 0) {
655
- return subagentTranscriptReport(activity, {
656
- ...conversationFields,
657
- unavailableReason: "missing_transcript_ref"
658
- });
659
- }
660
- const transcript = childMessages.map(
661
- (message) => normalizeSubagentTranscriptMessage(message, activity.subagentKind)
662
- );
663
- return subagentTranscriptReport(activity, {
664
- ...conversationFields,
665
- transcript,
666
- transcriptMessageCount: countConversationMessages(transcript)
667
- });
668
- }
669
-
670
- export {
671
- buildConversationDetail,
672
- buildConversationSubagent
673
- };