@sentry/junior 0.97.1 → 0.98.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 (55) hide show
  1. package/dist/{agent-hooks-XSUIVRQS.js → agent-hooks-OSW6C3CJ.js} +2 -2
  2. package/dist/api/conversations/detail.d.ts +1 -1
  3. package/dist/api/conversations/detail.query.d.ts +1 -1
  4. package/dist/api/conversations/projection.d.ts +0 -1
  5. package/dist/api/conversations/schema.d.ts +31 -8
  6. package/dist/api/people/schema.d.ts +0 -11
  7. package/dist/api/people/shared.d.ts +2 -4
  8. package/dist/api/schema.d.ts +1 -1
  9. package/dist/api/schema.js +2 -2
  10. package/dist/api-reference.d.ts +1 -1
  11. package/dist/api.js +10 -10
  12. package/dist/app.js +39 -28
  13. package/dist/chat/conversations/search.d.ts +24 -0
  14. package/dist/chat/conversations/sql/search.d.ts +4 -0
  15. package/dist/chat/db.d.ts +3 -0
  16. package/dist/chat/runtime/report-progress.d.ts +3 -0
  17. package/dist/chat/runtime/slack-resume.d.ts +2 -0
  18. package/dist/chat/slack/assistant-thread/status-scheduler.d.ts +4 -3
  19. package/dist/chat/slack/outbound.d.ts +6 -0
  20. package/dist/chat/slack/tools/conversation-search.d.ts +71 -0
  21. package/dist/chat/state/turn-session.d.ts +2 -0
  22. package/dist/chat/tool-support/zod-tool.d.ts +1 -1
  23. package/dist/chunk-6DTVCPLO.js +146 -0
  24. package/dist/{chunk-AUUCAFDE.js → chunk-6HHEZENZ.js} +126 -11
  25. package/dist/{chunk-VFH4ZKZT.js → chunk-CRQVIHDI.js} +1 -3
  26. package/dist/{chunk-6U26PYI5.js → chunk-CV22LUIV.js} +7 -17
  27. package/dist/{chunk-GUAP3H7O.js → chunk-DHHU2WSG.js} +2 -2
  28. package/dist/{chunk-XIRPQQVE.js → chunk-DPHWDS6I.js} +297 -171
  29. package/dist/{chunk-T533JLM3.js → chunk-ER45IV77.js} +8 -6
  30. package/dist/{chunk-LUAE4MGW.js → chunk-EUZ44ROE.js} +21 -4
  31. package/dist/{chunk-5HLDPVYS.js → chunk-FTTFV6DC.js} +1 -1
  32. package/dist/{chunk-3F4OPKGF.js → chunk-IO65E4ZD.js} +2 -2
  33. package/dist/{chunk-ON6YKIQ7.js → chunk-KJHAW6VE.js} +4 -10
  34. package/dist/{chunk-FBHZJFHI.js → chunk-NJEDJFK4.js} +1 -1
  35. package/dist/{chunk-35BNLDOJ.js → chunk-OGHXZ4KQ.js} +134 -57
  36. package/dist/{chunk-WJOSZOON.js → chunk-OL6XQ23W.js} +11 -150
  37. package/dist/{chunk-WGXMTFNU.js → chunk-WJ7STXT6.js} +5 -3
  38. package/dist/cli/chat.js +11 -10
  39. package/dist/cli/plugins.js +2 -2
  40. package/dist/cli/upgrade.js +9 -7
  41. package/dist/{db-BWXYIKFQ.js → db-GFSCDAHS.js} +3 -1
  42. package/dist/detail-W4ZSD45K.js +67 -0
  43. package/dist/{legacy-import-3NQJVGQL.js → legacy-import-6WWXMALY.js} +2 -2
  44. package/dist/{list-UXJI5Q5X.js → list-3KZNG3SJ.js} +3 -3
  45. package/dist/{list-A3WFRIYX.js → list-M5BRWW3D.js} +5 -6
  46. package/dist/{plugin-conversations-DDNGC35A.js → plugin-conversations-ZYRWZYEA.js} +2 -2
  47. package/dist/{profile-4RYDXWTF.js → profile-UEIRSVT3.js} +5 -5
  48. package/dist/{runner-JALSYH2P.js → runner-267ZRLZD.js} +8 -7
  49. package/dist/{stats-XKXH3PTY.js → stats-L62E4O4U.js} +7 -17
  50. package/dist/{subagent-TZ55XY37.js → subagent-5HZPQC7V.js} +6 -6
  51. package/migrations/0002_conversation_message_search.sql +1 -0
  52. package/migrations/meta/0002_snapshot.json +987 -0
  53. package/migrations/meta/_journal.json +7 -0
  54. package/package.json +6 -6
  55. package/dist/detail-GA6NXWQJ.js +0 -43
@@ -1,5 +1,11 @@
1
1
  import type { SlackMessageBlock } from "@/chat/slack/footer";
2
+ import { type SlackChannelId } from "@/chat/slack/ids";
2
3
  import { type SlackMessageTs } from "@/chat/slack/timestamp";
4
+ /** Resolve a Slack message permalink without making read-only workflows fail on lookup errors. */
5
+ export declare function getSlackMessagePermalink(args: {
6
+ channelId: SlackChannelId;
7
+ messageTs: SlackMessageTs;
8
+ }): Promise<string | undefined>;
3
9
  /** Post Slack `mrkdwn` text to a conversation or thread via the shared outbound boundary. */
4
10
  export declare function postSlackMessage(input: {
5
11
  blocks?: SlackMessageBlock[];
@@ -0,0 +1,71 @@
1
+ import type { ConversationSearchScope, ConversationSearchStore } from "@/chat/conversations/search";
2
+ import { getSlackMessagePermalink } from "@/chat/slack/outbound";
3
+ interface ConversationSearchToolDeps {
4
+ getPermalink?: typeof getSlackMessagePermalink;
5
+ store?: ConversationSearchStore;
6
+ }
7
+ /** Create a tool that searches retained public Junior threads across an authorized Slack workspace. */
8
+ export declare function createSlackConversationSearchTool(scope: ConversationSearchScope, currentConversationId: string, deps?: ConversationSearchToolDeps): Omit<import("../../tools/definition").AnyToolDefinition, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
9
+ inputSchema: import("../../tools/definition").JsonSchemaObject;
10
+ outputSchema: import("../../tools/definition").JsonSchemaObject;
11
+ prepareArguments(args: unknown): {
12
+ query: string;
13
+ limit?: number | null | undefined;
14
+ };
15
+ execute?: ((input: unknown, options: import("../../tools/definition").ToolExecuteOptions) => {
16
+ [x: string]: unknown;
17
+ ok: boolean;
18
+ status: "error" | "success";
19
+ query: string;
20
+ count: number;
21
+ threads: {
22
+ conversation_id: string;
23
+ thread_ts: string;
24
+ message_id: string;
25
+ message_role: "assistant" | "user";
26
+ message_timestamp: string;
27
+ excerpt: string;
28
+ permalink?: string | undefined;
29
+ }[];
30
+ target?: string | undefined;
31
+ data?: unknown;
32
+ truncated?: boolean | undefined;
33
+ continuation?: {
34
+ arguments: Record<string, unknown>;
35
+ reason?: string | undefined;
36
+ } | undefined;
37
+ error?: string | {
38
+ kind: string;
39
+ message: string;
40
+ retryable?: boolean | undefined;
41
+ } | undefined;
42
+ } | Promise<{
43
+ [x: string]: unknown;
44
+ ok: boolean;
45
+ status: "error" | "success";
46
+ query: string;
47
+ count: number;
48
+ threads: {
49
+ conversation_id: string;
50
+ thread_ts: string;
51
+ message_id: string;
52
+ message_role: "assistant" | "user";
53
+ message_timestamp: string;
54
+ excerpt: string;
55
+ permalink?: string | undefined;
56
+ }[];
57
+ target?: string | undefined;
58
+ data?: unknown;
59
+ truncated?: boolean | undefined;
60
+ continuation?: {
61
+ arguments: Record<string, unknown>;
62
+ reason?: string | undefined;
63
+ } | undefined;
64
+ error?: string | {
65
+ kind: string;
66
+ message: string;
67
+ retryable?: boolean | undefined;
68
+ } | undefined;
69
+ }>) | undefined;
70
+ };
71
+ export {};
@@ -121,6 +121,8 @@ export declare function recordAgentTurnSessionSummary(args: {
121
121
  export declare function listAgentTurnSessionSummaries(limit?: number): Promise<AgentTurnSessionSummary[]>;
122
122
  /** List turn-session summaries for one conversation without the global feed cap. */
123
123
  export declare function listAgentTurnSessionSummariesForConversation(conversationId: string): Promise<AgentTurnSessionSummary[]>;
124
+ /** List retained run summaries without falling back to global history. */
125
+ export declare function listBoundedAgentTurnSessionSummariesForConversation(conversationId: string): Promise<AgentTurnSessionSummary[]>;
124
126
  /** Read complete per-conversation summary indexes with bounded backend load. */
125
127
  export declare function listAgentTurnSessionSummariesForConversations(stateAdapter: StateAdapter, conversationIds: string[]): Promise<Map<string, AgentTurnSessionSummary[]>>;
126
128
  /** Mark an unfinished turn session record as abandoned when a newer turn wins. */
@@ -2,7 +2,7 @@ import { z, type ZodType, type ZodTypeAny } from "zod";
2
2
  import { type JuniorToolResult } from "@/chat/tool-support/structured-result";
3
3
  import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
4
4
  import type { AnyToolDefinition, JsonSchemaObject, ToolExecuteOptions } from "@/chat/tools/definition";
5
- type ZodToolDefinitionBase<TInputSchema extends ZodTypeAny> = Pick<AnyToolDefinition, "identity" | "description" | "exposure" | "annotations" | "promptSnippet" | "promptGuidelines" | "executionMode"> & {
5
+ type ZodToolDefinitionBase<TInputSchema extends ZodTypeAny> = Pick<AnyToolDefinition, "identity" | "source" | "description" | "exposure" | "annotations" | "promptSnippet" | "promptGuidelines" | "executionMode"> & {
6
6
  inputSchema: TInputSchema;
7
7
  prepareArguments?: (args: unknown) => z.input<TInputSchema>;
8
8
  };
@@ -0,0 +1,146 @@
1
+ import {
2
+ isRecord,
3
+ toOptionalNumber,
4
+ toOptionalString
5
+ } from "./chunk-OB42YVAE.js";
6
+
7
+ // src/chat/state/conversation.ts
8
+ function defaultConversationState() {
9
+ const nowMs = Date.now();
10
+ return {
11
+ schemaVersion: 1,
12
+ messages: [],
13
+ compactions: [],
14
+ backfill: {},
15
+ processing: {},
16
+ stats: {
17
+ estimatedContextTokens: 0,
18
+ totalMessageCount: 0,
19
+ compactedMessageCount: 0,
20
+ updatedAtMs: nowMs
21
+ },
22
+ vision: {
23
+ byFileId: {}
24
+ }
25
+ };
26
+ }
27
+ function coercePendingAuthState(value) {
28
+ if (!isRecord(value)) {
29
+ return void 0;
30
+ }
31
+ const kind = value.kind;
32
+ const provider = toOptionalString(value.provider);
33
+ const actorId = toOptionalString(value.actorId);
34
+ const scope = toOptionalString(value.scope);
35
+ const sessionId = toOptionalString(value.sessionId);
36
+ const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
37
+ if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || !sessionId || typeof linkSentAtMs !== "number") {
38
+ return void 0;
39
+ }
40
+ return {
41
+ kind,
42
+ provider,
43
+ actorId,
44
+ ...scope ? { scope } : {},
45
+ sessionId,
46
+ linkSentAtMs
47
+ };
48
+ }
49
+ function coerceThreadConversationState(value) {
50
+ if (!isRecord(value)) {
51
+ return defaultConversationState();
52
+ }
53
+ const root = value;
54
+ const rawConversation = isRecord(root.conversation) ? root.conversation : {};
55
+ const base = defaultConversationState();
56
+ const messages = [];
57
+ const rawCompactions = Array.isArray(rawConversation.compactions) ? rawConversation.compactions : [];
58
+ const compactions = [];
59
+ for (const item of rawCompactions) {
60
+ if (!isRecord(item)) continue;
61
+ const id = toOptionalString(item.id);
62
+ const summary = toOptionalString(item.summary);
63
+ const createdAtMs = toOptionalNumber(item.createdAtMs);
64
+ if (!id || !summary || !createdAtMs) continue;
65
+ const coveredMessageIds = Array.isArray(item.coveredMessageIds) ? item.coveredMessageIds.filter(
66
+ (entry) => typeof entry === "string" && entry.length > 0
67
+ ) : [];
68
+ compactions.push({
69
+ id,
70
+ summary,
71
+ createdAtMs,
72
+ coveredMessageIds
73
+ });
74
+ }
75
+ const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
76
+ const backfill = {
77
+ completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
78
+ source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
79
+ };
80
+ const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
81
+ const processing = {
82
+ activeTurnId: toOptionalString(rawProcessing.activeTurnId),
83
+ lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
84
+ pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
85
+ };
86
+ const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
87
+ const stats = {
88
+ estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
89
+ totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
90
+ compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
91
+ updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
92
+ };
93
+ const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
94
+ const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
95
+ const byFileId = {};
96
+ for (const [fileId, value2] of Object.entries(rawVisionByFileId)) {
97
+ if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
98
+ if (!isRecord(value2)) continue;
99
+ const summary = toOptionalString(value2.summary);
100
+ const analyzedAtMs = toOptionalNumber(value2.analyzedAtMs);
101
+ if (!summary || !analyzedAtMs) continue;
102
+ byFileId[fileId] = {
103
+ summary,
104
+ analyzedAtMs
105
+ };
106
+ }
107
+ return {
108
+ schemaVersion: 1,
109
+ messages,
110
+ compactions,
111
+ backfill,
112
+ processing,
113
+ stats,
114
+ vision: {
115
+ backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
116
+ byFileId
117
+ }
118
+ };
119
+ }
120
+ function buildConversationStatePatch(conversation) {
121
+ const {
122
+ compactions: _compactions,
123
+ messages: _messages,
124
+ ...scratch
125
+ } = conversation;
126
+ return {
127
+ conversation: {
128
+ ...scratch,
129
+ schemaVersion: 1,
130
+ stats: {
131
+ ...conversation.stats,
132
+ totalMessageCount: conversation.messages.length,
133
+ updatedAtMs: Date.now()
134
+ }
135
+ }
136
+ };
137
+ }
138
+
139
+ // src/chat/state/ttl.ts
140
+ var JUNIOR_THREAD_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
141
+
142
+ export {
143
+ JUNIOR_THREAD_STATE_TTL_MS,
144
+ coerceThreadConversationState,
145
+ buildConversationStatePatch
146
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  conversationSummaryFromStoredConversation
3
- } from "./chunk-ON6YKIQ7.js";
3
+ } from "./chunk-KJHAW6VE.js";
4
4
  import {
5
5
  buildSentryConversationUrl,
6
6
  buildSentryTraceUrl
@@ -8,11 +8,11 @@ import {
8
8
  import {
9
9
  loadProjection,
10
10
  projectSteps
11
- } from "./chunk-GUAP3H7O.js";
11
+ } from "./chunk-DHHU2WSG.js";
12
12
  import {
13
13
  getAgentStepStore,
14
14
  getConversationMessageStore
15
- } from "./chunk-35BNLDOJ.js";
15
+ } from "./chunk-OGHXZ4KQ.js";
16
16
  import {
17
17
  unescapeXml,
18
18
  unwrapCurrentInstruction
@@ -24,6 +24,9 @@ import {
24
24
  isRecord
25
25
  } from "./chunk-OB42YVAE.js";
26
26
 
27
+ // src/api/conversations/detail-projection.ts
28
+ import { isDeepStrictEqual } from "util";
29
+
27
30
  // src/api/conversations/transcript.ts
28
31
  var SAFE_METADATA_KEY_LIMIT = 20;
29
32
  var LEGACY_ADVISOR_TASK_OPEN = "<advisor-task>\n";
@@ -64,6 +67,14 @@ function recordField(value, names) {
64
67
  }
65
68
  return void 0;
66
69
  }
70
+ function thinkingOutput(part) {
71
+ const output = recordField(part, ["thinking", "text", "content", "output"]);
72
+ return isRecord(output) ? recordField(output, ["thinking", "text", "content", "output"]) : output;
73
+ }
74
+ function isDisplayableTranscriptPart(part) {
75
+ if (part.type !== "thinking") return true;
76
+ return typeof part.output === "string" && part.output.trim().length > 0;
77
+ }
67
78
  function normalizeTranscriptPart(part, options = {}) {
68
79
  const displayText = (text) => {
69
80
  if (options.unwrapCurrentTask) {
@@ -107,7 +118,7 @@ function normalizeTranscriptPart(part, options = {}) {
107
118
  if (rawType === "thinking") {
108
119
  return {
109
120
  type: "thinking",
110
- output: recordField(part, ["thinking", "text", "content", "output"])
121
+ output: thinkingOutput(part)
111
122
  };
112
123
  }
113
124
  return {
@@ -155,7 +166,7 @@ function normalizeMessage(message, options = {}) {
155
166
  unwrapCurrentTask: role === "user",
156
167
  unwrapLegacyAdvisorTask: options.unwrapLegacyAdvisorTask && role === "user"
157
168
  })
158
- ) : [
169
+ ).filter(isDisplayableTranscriptPart) : [
159
170
  normalizeTranscriptPart(content, {
160
171
  unwrapCurrentTask: role === "user",
161
172
  unwrapLegacyAdvisorTask: options.unwrapLegacyAdvisorTask && role === "user"
@@ -393,9 +404,112 @@ function subagentActivityFromSteps(start, end, options = {}) {
393
404
  }
394
405
 
395
406
  // src/api/conversations/detail-projection.ts
396
- async function currentRunContent(args) {
397
- const steps = await args.stepStore.loadCurrentEpoch(args.conversationId);
398
- const messages = projectSteps(steps).messages;
407
+ var COMPACTION_SUMMARY_PREFIXES = [
408
+ "Context compaction summary for future Junior turns:",
409
+ "Context handoff summary for future Junior turns:"
410
+ ];
411
+ var MODEL_HANDOFF_SUMMARY_PREFIX = "Model handoff checkpoint. Continue the outstanding request now using this summary as the complete prior context:";
412
+ function messageText(message) {
413
+ return normalizeTranscriptMessage(message).parts.filter((part) => part.type === "text").map((part) => part.text ?? "").join("\n").trim();
414
+ }
415
+ function summaryAfterPrefix(message, prefixes) {
416
+ const text = messageText(message);
417
+ const prefix = prefixes.find((candidate) => text.startsWith(candidate));
418
+ if (!prefix) return void 0;
419
+ return text.slice(prefix.length).trim();
420
+ }
421
+ function summaryIndex(messages, provenance, prefixes) {
422
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
423
+ if (provenance[index]?.authority === "context" && summaryAfterPrefix(messages[index], prefixes) !== void 0) {
424
+ return index;
425
+ }
426
+ }
427
+ return -1;
428
+ }
429
+ function matchingPrefix(left, right) {
430
+ const limit = Math.min(left.length, right.length);
431
+ for (let index = 0; index < limit; index += 1) {
432
+ if (!isDeepStrictEqual(left[index], right[index])) return index;
433
+ }
434
+ return limit;
435
+ }
436
+ function historyContent(args) {
437
+ const contextEvents = [];
438
+ const messages = [];
439
+ const epochs = /* @__PURE__ */ new Map();
440
+ for (const step of args.steps) {
441
+ const epoch = epochs.get(step.contextEpoch);
442
+ if (epoch) epoch.push(step);
443
+ else epochs.set(step.contextEpoch, [step]);
444
+ }
445
+ let previousModelId;
446
+ let previousProjection = [];
447
+ for (const steps of epochs.values()) {
448
+ const marker = steps.find(
449
+ (step) => step.entry.type === "context_epoch_started"
450
+ );
451
+ const projection = projectSteps(steps);
452
+ const projected = projection.messages;
453
+ const replacementSummaryIndex = marker?.entry.reason === "compaction" ? summaryIndex(
454
+ projected,
455
+ projection.provenance,
456
+ COMPACTION_SUMMARY_PREFIXES
457
+ ) : marker?.entry.reason === "handoff" ? summaryIndex(projected, projection.provenance, [
458
+ MODEL_HANDOFF_SUMMARY_PREFIX
459
+ ]) : -1;
460
+ const summary = replacementSummaryIndex >= 0 ? summaryAfterPrefix(
461
+ projected[replacementSummaryIndex],
462
+ marker?.entry.reason === "handoff" ? [MODEL_HANDOFF_SUMMARY_PREFIX] : COMPACTION_SUMMARY_PREFIXES
463
+ ) : void 0;
464
+ if (marker?.entry.reason === "compaction") {
465
+ contextEvents.push({
466
+ type: "context_compacted",
467
+ createdAt: new Date(marker.createdAtMs).toISOString(),
468
+ ...marker.entry.modelId ? { modelId: marker.entry.modelId } : {},
469
+ ...args.canExposePayload && summary ? { summary } : {},
470
+ transcriptIndex: messages.length
471
+ });
472
+ } else if (marker?.entry.reason === "handoff") {
473
+ contextEvents.push({
474
+ type: "model_handoff",
475
+ createdAt: new Date(marker.createdAtMs).toISOString(),
476
+ ...previousModelId ? { fromModelId: previousModelId } : {},
477
+ toModelId: marker.entry.modelId,
478
+ ...args.canExposePayload && summary ? { summary } : {},
479
+ transcriptIndex: messages.length
480
+ });
481
+ }
482
+ if (marker?.entry.reason === "rollback") {
483
+ messages.push(
484
+ ...projected.slice(matchingPrefix(previousProjection, projected))
485
+ );
486
+ } else {
487
+ const copiedMessageIndexes = /* @__PURE__ */ new Set();
488
+ projected.forEach((message, index) => {
489
+ if (index === replacementSummaryIndex) return;
490
+ let copiedCompactionMessage = false;
491
+ if (marker?.entry.reason === "compaction" && replacementSummaryIndex >= 0 && index < replacementSummaryIndex) {
492
+ const copiedIndex = messages.findIndex(
493
+ (candidate, candidateIndex) => !copiedMessageIndexes.has(candidateIndex) && isDeepStrictEqual(candidate, message)
494
+ );
495
+ copiedCompactionMessage = copiedIndex >= 0;
496
+ if (copiedCompactionMessage) copiedMessageIndexes.add(copiedIndex);
497
+ }
498
+ if (!copiedCompactionMessage) messages.push(message);
499
+ });
500
+ }
501
+ previousModelId = marker?.entry.modelId ?? previousModelId;
502
+ previousProjection = projected;
503
+ }
504
+ return { contextEvents, messages };
505
+ }
506
+ async function conversationContent(args) {
507
+ const steps = await args.stepStore.loadHistory(args.conversationId);
508
+ const history = historyContent({
509
+ canExposePayload: args.canExposePayload,
510
+ steps
511
+ });
512
+ const messages = history.messages;
399
513
  const transcript = messages.length > 0 ? messages.map((message) => normalizeTranscriptMessage(message)) : (await args.messageStore.list(args.conversationId)).map(
400
514
  visibleMessageTranscript
401
515
  );
@@ -405,6 +519,7 @@ async function currentRunContent(args) {
405
519
  steps,
406
520
  messages
407
521
  }),
522
+ contextEvents: history.contextEvents,
408
523
  transcript
409
524
  };
410
525
  }
@@ -427,12 +542,12 @@ async function buildConversationDetail(args) {
427
542
  conversationId,
428
543
  visibility: conversation.visibility
429
544
  });
430
- const currentContent = transcriptPurgedAtMs === void 0 ? await currentRunContent({
545
+ const currentContent = transcriptPurgedAtMs === void 0 ? await conversationContent({
431
546
  conversationId,
432
547
  messageStore,
433
548
  stepStore,
434
549
  canExposePayload: canExposeSqlContent
435
- }) : { activity: [], transcript: [] };
550
+ }) : { activity: [], contextEvents: [], transcript: [] };
436
551
  const currentTranscript = currentContent.transcript;
437
552
  const traceId = canExposeSqlContent ? traceIdFromTranscript(currentTranscript) : void 0;
438
553
  const sentryTraceUrl = traceId ? buildSentryTraceUrl(traceId) : void 0;
@@ -441,12 +556,12 @@ async function buildConversationDetail(args) {
441
556
  ...conversationSummaryFromStoredConversation({
442
557
  conversation,
443
558
  durationMs: args.durationMs,
444
- nowMs,
445
559
  usage: args.usage
446
560
  }),
447
561
  ...traceId ? { traceId } : {},
448
562
  ...sentryTraceUrl ? { sentryTraceUrl } : {},
449
563
  activity: currentContent.activity,
564
+ contextEvents: currentContent.contextEvents,
450
565
  transcriptAvailable: transcriptExpiredAt === void 0 && canExposeSqlContent && currentTranscript.length > 0,
451
566
  ...currentTranscript.length > 0 ? {
452
567
  transcriptMessageCount: countConversationMessages(currentTranscript)
@@ -2,7 +2,7 @@ import {
2
2
  actorIdentitySchema,
3
3
  conversationStatsItemSchema,
4
4
  conversationSummaryReportSchema
5
- } from "./chunk-LUAE4MGW.js";
5
+ } from "./chunk-EUZ44ROE.js";
6
6
 
7
7
  // src/api/people/schema.ts
8
8
  import { z } from "zod";
@@ -18,7 +18,6 @@ var actorActivityDayReportSchema = z.object({
18
18
  date: z.string(),
19
19
  durationMs: z.number(),
20
20
  failed: z.number(),
21
- hung: z.number(),
22
21
  tokens: z.number().optional()
23
22
  }).strict();
24
23
  var actorTotalsReportSchema = z.object({
@@ -27,7 +26,6 @@ var actorTotalsReportSchema = z.object({
27
26
  conversations: z.number(),
28
27
  durationMs: z.number(),
29
28
  failed: z.number(),
30
- hung: z.number(),
31
29
  tokens: z.number().optional()
32
30
  }).strict();
33
31
  var identifiedActorSchema = actorIdentitySchema.extend({ email: z.string().min(1) }).strict();
@@ -4,7 +4,7 @@ import {
4
4
  juniorDestinations,
5
5
  juniorIdentities,
6
6
  juniorUsers
7
- } from "./chunk-35BNLDOJ.js";
7
+ } from "./chunk-OGHXZ4KQ.js";
8
8
 
9
9
  // src/api/people/shared.ts
10
10
  import { and, asc, desc, eq, sql } from "drizzle-orm";
@@ -12,7 +12,6 @@ var PRIVATE_CONVERSATION_LABEL = "Private Conversation";
12
12
  var SAMPLE_LIMIT = 5e3;
13
13
  var RECENT_LIMIT = 25;
14
14
  var ACTIVITY_DAYS = 366;
15
- var HUNG_PROGRESS_MS = 5 * 60 * 1e3;
16
15
  function normalizeEmail(email) {
17
16
  const normalized = email?.trim().toLowerCase();
18
17
  return normalized || void 0;
@@ -39,17 +38,13 @@ function surfaceFromRow(row) {
39
38
  if (row.conversationId.startsWith("api:")) return "api";
40
39
  return "internal";
41
40
  }
42
- function statusFromRow(row, nowMs) {
41
+ function statusFromRow(row) {
43
42
  if (row.executionStatus === "failed") {
44
43
  return "failed";
45
44
  }
46
45
  if (row.executionStatus === "idle") {
47
46
  return "completed";
48
47
  }
49
- const updatedAtMs = (row.executionUpdatedAt ?? row.updatedAt).getTime();
50
- if (row.executionStatus === "running" && nowMs - updatedAtMs > HUNG_PROGRESS_MS) {
51
- return "hung";
52
- }
53
48
  return "active";
54
49
  }
55
50
  function surfaceLabel(surface) {
@@ -89,7 +84,7 @@ function titleFromRow(row, surface) {
89
84
  channelName: row.channelName ?? void 0
90
85
  }) ?? surfaceLabel(surface);
91
86
  }
92
- function summaryFromRow(row, nowMs) {
87
+ function summaryFromRow(row) {
93
88
  const surface = surfaceFromRow(row);
94
89
  const channel = channelFromConversationId(row.conversationId);
95
90
  const channelName = channelNameFromRow(row);
@@ -103,7 +98,7 @@ function summaryFromRow(row, nowMs) {
103
98
  ).toISOString(),
104
99
  lastSeenAt: row.lastActivityAt.toISOString(),
105
100
  startedAt: row.createdAt.toISOString(),
106
- status: statusFromRow(row, nowMs),
101
+ status: statusFromRow(row),
107
102
  surface,
108
103
  actorIdentity: {
109
104
  email: row.email,
@@ -134,8 +129,7 @@ function emptyTotals() {
134
129
  activeDays: 0,
135
130
  conversations: 0,
136
131
  durationMs: 0,
137
- failed: 0,
138
- hung: 0
132
+ failed: 0
139
133
  };
140
134
  }
141
135
  function emptyStatsItem(label) {
@@ -144,7 +138,6 @@ function emptyStatsItem(label) {
144
138
  conversations: 0,
145
139
  durationMs: 0,
146
140
  failed: 0,
147
- hung: 0,
148
141
  label
149
142
  };
150
143
  }
@@ -154,21 +147,18 @@ function emptyActivityDay(date) {
154
147
  conversations: 0,
155
148
  date,
156
149
  durationMs: 0,
157
- failed: 0,
158
- hung: 0
150
+ failed: 0
159
151
  };
160
152
  }
161
153
  function signals(summary) {
162
154
  return {
163
155
  active: summary.status === "active",
164
- failed: summary.status === "failed",
165
- hung: summary.status === "hung"
156
+ failed: summary.status === "failed"
166
157
  };
167
158
  }
168
159
  function addSignals(target, value) {
169
160
  target.active += value.active ? 1 : 0;
170
161
  target.failed += value.failed ? 1 : 0;
171
- target.hung += value.hung ? 1 : 0;
172
162
  }
173
163
  function identityWithEmail(actor) {
174
164
  const email = normalizeEmail(actor?.email);
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ensureLegacyConversationImport
3
- } from "./chunk-3F4OPKGF.js";
3
+ } from "./chunk-IO65E4ZD.js";
4
4
  import {
5
5
  contextProvenance,
6
6
  getAgentStepStore
7
- } from "./chunk-35BNLDOJ.js";
7
+ } from "./chunk-OGHXZ4KQ.js";
8
8
 
9
9
  // src/chat/conversations/projection.ts
10
10
  import { isDeepStrictEqual } from "util";