@sentry/junior 0.78.0 → 0.79.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.
@@ -8,6 +8,6 @@ export type { JuniorPluginInput, JuniorPluginSet, JuniorPluginSetOptions, } from
8
8
  export type { PluginRunContext, PluginRunTranscriptEntry, PluginTaskContext, PluginTaskDefinition, PluginTasks, } from "@sentry/junior-plugin-api";
9
9
  export { pluginRunContextSchema, pluginRunTranscriptEntrySchema, } from "@sentry/junior-plugin-api";
10
10
  export { createJuniorReporting } from "./reporting";
11
- export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationUsage, HealthReport, JuniorReporting, PluginOperationalReport, PluginOperationalReportFeed, PluginPackageContentItemReport, PluginPackageContentReport, PluginReport, RequesterIdentity, RuntimeInfoReport, SkillReport, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting";
11
+ export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationActivityReport, ConversationActivityStatus, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationSubagentActivityReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, HealthReport, JuniorReporting, PluginOperationalReport, PluginOperationalReportFeed, PluginPackageContentItemReport, PluginPackageContentReport, PluginReport, RequesterIdentity, RuntimeInfoReport, SkillReport, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting";
12
12
  export { juniorVercelConfig } from "./vercel";
13
13
  export type { JuniorVercelConfigOptions } from "./vercel";
package/dist/app.js CHANGED
@@ -71,7 +71,7 @@ import {
71
71
  updateConversationStats,
72
72
  uploadFilesToThread,
73
73
  upsertConversationMessage
74
- } from "./chunk-2MSW5BZY.js";
74
+ } from "./chunk-NNM7YQLL.js";
75
75
  import {
76
76
  CONVERSATION_WORK_CHECK_IN_INTERVAL_MS,
77
77
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
@@ -129,7 +129,7 @@ import {
129
129
  recordAuthorizationCompleted,
130
130
  splitSlackReplyText,
131
131
  truncateStatusText
132
- } from "./chunk-QDQVOMBA.js";
132
+ } from "./chunk-R5T7QY3P.js";
133
133
  import {
134
134
  validatePluginEgressCredentialHooks,
135
135
  validatePluginRegistrations
@@ -2,6 +2,11 @@ import { z } from "zod";
2
2
  import type { PiMessage } from "@/chat/pi/messages";
3
3
  import { type StoredSlackRequester } from "@/chat/requester";
4
4
  declare const authorizationKindSchema: z.ZodUnion<readonly [z.ZodLiteral<"plugin">, z.ZodLiteral<"mcp">]>;
5
+ declare const transcriptRefSchema: z.ZodObject<{
6
+ type: z.ZodLiteral<"advisor_session">;
7
+ parentConversationId: z.ZodString;
8
+ key: z.ZodString;
9
+ }, z.core.$strip>;
5
10
  declare const sessionLogEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
6
11
  schemaVersion: z.ZodLiteral<1>;
7
12
  type: z.ZodLiteral<"pi_message">;
@@ -74,10 +79,50 @@ declare const sessionLogEntrySchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
74
79
  provider: z.ZodString;
75
80
  requesterId: z.ZodString;
76
81
  authorizationId: z.ZodString;
82
+ }, z.core.$strip>, z.ZodObject<{
83
+ schemaVersion: z.ZodLiteral<1>;
84
+ type: z.ZodLiteral<"tool_execution_started">;
85
+ sessionId: z.ZodDefault<z.ZodString>;
86
+ createdAtMs: z.ZodNumber;
87
+ toolCallId: z.ZodString;
88
+ toolName: z.ZodString;
89
+ args: z.ZodOptional<z.ZodUnknown>;
90
+ }, z.core.$strip>, z.ZodObject<{
91
+ schemaVersion: z.ZodLiteral<1>;
92
+ type: z.ZodLiteral<"subagent_started">;
93
+ sessionId: z.ZodDefault<z.ZodString>;
94
+ subagentInvocationId: z.ZodString;
95
+ subagentKind: z.ZodString;
96
+ parentToolCallId: z.ZodOptional<z.ZodString>;
97
+ parentConversationId: z.ZodString;
98
+ parentSessionId: z.ZodOptional<z.ZodString>;
99
+ transcriptRef: z.ZodObject<{
100
+ type: z.ZodLiteral<"advisor_session">;
101
+ parentConversationId: z.ZodString;
102
+ key: z.ZodString;
103
+ }, z.core.$strip>;
104
+ historyMode: z.ZodLiteral<"shared">;
105
+ createdAtMs: z.ZodNumber;
106
+ }, z.core.$strip>, z.ZodObject<{
107
+ schemaVersion: z.ZodLiteral<1>;
108
+ type: z.ZodLiteral<"subagent_ended">;
109
+ sessionId: z.ZodDefault<z.ZodString>;
110
+ subagentInvocationId: z.ZodString;
111
+ outcome: z.ZodUnion<readonly [z.ZodLiteral<"success">, z.ZodLiteral<"error">, z.ZodLiteral<"aborted">]>;
112
+ errorCode: z.ZodOptional<z.ZodString>;
113
+ createdAtMs: z.ZodNumber;
77
114
  }, z.core.$strip>], "type">;
78
115
  /** Requester identity stored with turn-start messages for durable continuation. */
79
116
  export type SessionLogEntry = z.infer<typeof sessionLogEntrySchema>;
80
117
  export type AuthorizationKind = z.infer<typeof authorizationKindSchema>;
118
+ export type TranscriptRef = z.infer<typeof transcriptRefSchema>;
119
+ export type SessionActivityEntry = Extract<SessionLogEntry, {
120
+ type: "tool_execution_started";
121
+ }> | Extract<SessionLogEntry, {
122
+ type: "subagent_started";
123
+ }> | Extract<SessionLogEntry, {
124
+ type: "subagent_ended";
125
+ }>;
81
126
  interface Scope {
82
127
  conversationId: string;
83
128
  }
@@ -94,6 +139,11 @@ export interface SessionProjection {
94
139
  messages: PiMessage[];
95
140
  requester?: StoredSlackRequester;
96
141
  }
142
+ /** Load chronological host-only runtime activity entries for reporting. */
143
+ export declare function loadActivityEntries(args: Scope & {
144
+ store?: SessionLogStore;
145
+ sessionId?: string;
146
+ }): Promise<SessionActivityEntry[]>;
97
147
  /** Load the committed Pi-message projection for a conversation. */
98
148
  export declare function loadMessages(args: Scope & {
99
149
  store?: SessionLogStore;
@@ -142,6 +192,40 @@ export declare function recordAuthorizationCompleted(args: Scope & {
142
192
  authorizationId: string;
143
193
  ttlMs: number;
144
194
  }): Promise<void>;
195
+ /** Record a host-observed parent tool start without adding it to Pi replay. */
196
+ export declare function recordToolExecutionStarted(args: Scope & {
197
+ args?: unknown;
198
+ createdAtMs?: number;
199
+ sessionId?: string;
200
+ store?: SessionLogStore;
201
+ toolCallId: string;
202
+ toolName: string;
203
+ ttlMs: number;
204
+ }): Promise<void>;
205
+ /** Record that a child agent execution became visible from this parent run. */
206
+ export declare function recordSubagentStarted(args: Scope & {
207
+ createdAtMs?: number;
208
+ historyMode: "shared";
209
+ parentConversationId: string;
210
+ parentSessionId?: string;
211
+ parentToolCallId?: string;
212
+ sessionId?: string;
213
+ store?: SessionLogStore;
214
+ subagentInvocationId: string;
215
+ subagentKind: string;
216
+ transcriptRef: TranscriptRef;
217
+ ttlMs: number;
218
+ }): Promise<void>;
219
+ /** Record the terminal state for a previously-started child agent execution. */
220
+ export declare function recordSubagentEnded(args: Scope & {
221
+ createdAtMs?: number;
222
+ errorCode?: string;
223
+ outcome: "success" | "error" | "aborted";
224
+ sessionId?: string;
225
+ store?: SessionLogStore;
226
+ subagentInvocationId: string;
227
+ ttlMs: number;
228
+ }): Promise<void>;
145
229
  /**
146
230
  * Append conversation-log entries and advance the current Pi projection.
147
231
  *
@@ -23,6 +23,7 @@ export interface AdvisorToolRuntimeContext {
23
23
  conversationPrivacy?: ConversationPrivacy;
24
24
  getTools: () => AgentTool[];
25
25
  logContext?: LogContext;
26
+ parentSessionId?: string;
26
27
  store?: AdvisorSessionStore;
27
28
  streamFn?: StreamFn;
28
29
  }
@@ -27,8 +27,11 @@ import {
27
27
  loadConnectedMcpProviders,
28
28
  recordAuthorizationRequested,
29
29
  recordMcpProviderConnected,
30
+ recordSubagentEnded,
31
+ recordSubagentStarted,
32
+ recordToolExecutionStarted,
30
33
  upsertAgentTurnSessionRecord
31
- } from "./chunk-QDQVOMBA.js";
34
+ } from "./chunk-R5T7QY3P.js";
32
35
  import {
33
36
  createPluginEmbedder,
34
37
  createPluginHookRunner,
@@ -678,7 +681,7 @@ function markTurnFailed(args) {
678
681
 
679
682
  // src/chat/respond.ts
680
683
  import { Agent as Agent2 } from "@earendil-works/pi-agent-core";
681
- import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS3 } from "chat";
684
+ import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS4 } from "chat";
682
685
 
683
686
  // src/chat/capabilities/catalog.ts
684
687
  var cachedCatalog;
@@ -5796,6 +5799,7 @@ import {
5796
5799
  Agent
5797
5800
  } from "@earendil-works/pi-agent-core";
5798
5801
  import { Type as Type21 } from "@sinclair/typebox";
5802
+ import { THREAD_STATE_TTL_MS } from "chat";
5799
5803
 
5800
5804
  // src/chat/tools/advisor/session-store.ts
5801
5805
  var ADVISOR_SESSION_TTL_MS = JUNIOR_THREAD_STATE_TTL_MS;
@@ -5892,7 +5896,7 @@ function createAdvisorTool(context) {
5892
5896
  description: "Curated evidence packet: relevant requirements, constraints, current plan, alternatives, code snippets, diffs, command output, and open questions."
5893
5897
  })
5894
5898
  }),
5895
- execute: async ({ question, context: suppliedContext }) => {
5899
+ execute: async ({ question, context: suppliedContext }, { toolCallId } = {}) => {
5896
5900
  if (typeof question !== "string" || !question.trim()) {
5897
5901
  return failure(
5898
5902
  "invalid_question",
@@ -5914,6 +5918,32 @@ function createAdvisorTool(context) {
5914
5918
  );
5915
5919
  }
5916
5920
  const conversationId = context.conversationId;
5921
+ const advisorSessionKey = getAdvisorSessionKey(conversationId);
5922
+ const subagentInvocationId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : `advisor:${Date.now()}`;
5923
+ const endSubagent = async (outcome, errorCode) => recordSubagentEnded({
5924
+ conversationId,
5925
+ sessionId: context.parentSessionId,
5926
+ subagentInvocationId,
5927
+ outcome,
5928
+ ...errorCode ? { errorCode } : {},
5929
+ ttlMs: THREAD_STATE_TTL_MS
5930
+ });
5931
+ await recordSubagentStarted({
5932
+ conversationId,
5933
+ sessionId: context.parentSessionId,
5934
+ parentConversationId: conversationId,
5935
+ parentSessionId: context.parentSessionId,
5936
+ ...typeof toolCallId === "string" && toolCallId.length > 0 ? { parentToolCallId: toolCallId } : {},
5937
+ subagentInvocationId,
5938
+ subagentKind: "advisor",
5939
+ transcriptRef: {
5940
+ type: "advisor_session",
5941
+ parentConversationId: conversationId,
5942
+ key: advisorSessionKey
5943
+ },
5944
+ historyMode: "shared",
5945
+ ttlMs: THREAD_STATE_TTL_MS
5946
+ });
5917
5947
  const conversationPrivacy = context.conversationPrivacy ?? "private";
5918
5948
  const requestText = [
5919
5949
  "<advisor-task>",
@@ -5951,6 +5981,7 @@ function createAdvisorTool(context) {
5951
5981
  advisorMessages = await store.load(conversationId);
5952
5982
  } catch {
5953
5983
  setSpanStatus("error");
5984
+ await endSubagent("error", "session_unavailable");
5954
5985
  return failure(
5955
5986
  "session_unavailable",
5956
5987
  "Advisor guidance is unavailable because advisor history could not be loaded. Continue without assuming advisor history."
@@ -5965,7 +5996,7 @@ function createAdvisorTool(context) {
5965
5996
  thinkingLevel: context.config.thinkingLevel,
5966
5997
  tools: context.getTools()
5967
5998
  },
5968
- sessionId: getAdvisorSessionKey(conversationId),
5999
+ sessionId: advisorSessionKey,
5969
6000
  streamFn: context.streamFn
5970
6001
  });
5971
6002
  advisorAgent.state.messages = advisorMessages;
@@ -5974,6 +6005,7 @@ function createAdvisorTool(context) {
5974
6005
  await advisorAgent.prompt(requestMessage);
5975
6006
  } catch {
5976
6007
  setSpanStatus("error");
6008
+ await endSubagent("error", "unavailable");
5977
6009
  return failure(
5978
6010
  "unavailable",
5979
6011
  "Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
@@ -5992,6 +6024,10 @@ function createAdvisorTool(context) {
5992
6024
  });
5993
6025
  if (!assistant || assistant.stopReason === "error" || assistant.stopReason === "aborted") {
5994
6026
  setSpanStatus("error");
6027
+ await endSubagent(
6028
+ assistant?.stopReason === "aborted" ? "aborted" : "error",
6029
+ "unavailable"
6030
+ );
5995
6031
  return failure(
5996
6032
  "unavailable",
5997
6033
  "Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
@@ -6002,11 +6038,13 @@ function createAdvisorTool(context) {
6002
6038
  await store.save(conversationId, advisorAgent.state.messages);
6003
6039
  } catch {
6004
6040
  setSpanStatus("error");
6041
+ await endSubagent("error", "session_unavailable");
6005
6042
  return failure(
6006
6043
  "session_unavailable",
6007
6044
  "Advisor guidance is unavailable because advisor history could not be saved. Retry the advisor call or continue without assuming advisor history."
6008
6045
  );
6009
6046
  }
6047
+ await endSubagent("success");
6010
6048
  setSpanStatus("ok");
6011
6049
  return success(memo);
6012
6050
  },
@@ -9200,7 +9238,7 @@ function normalizeToolResult(result, isSandboxResult) {
9200
9238
  import { PluginToolInputError } from "@sentry/junior-plugin-api";
9201
9239
 
9202
9240
  // src/chat/services/plugin-auth-orchestration.ts
9203
- import { THREAD_STATE_TTL_MS } from "chat";
9241
+ import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS2 } from "chat";
9204
9242
 
9205
9243
  // src/chat/mcp/auth-store.ts
9206
9244
  import {
@@ -9630,7 +9668,7 @@ function createPluginAuthOrchestration(input) {
9630
9668
  sessionId: input.sessionId
9631
9669
  }),
9632
9670
  delivery: reusingPendingLink ? "private_link_reused" : "private_link_sent",
9633
- ttlMs: THREAD_STATE_TTL_MS
9671
+ ttlMs: THREAD_STATE_TTL_MS2
9634
9672
  });
9635
9673
  }
9636
9674
  pendingPause = new PluginAuthorizationPauseError(
@@ -10767,7 +10805,7 @@ async function persistYieldSessionRecord(args) {
10767
10805
  }
10768
10806
 
10769
10807
  // src/chat/services/mcp-auth-orchestration.ts
10770
- import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS2 } from "chat";
10808
+ import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS3 } from "chat";
10771
10809
 
10772
10810
  // src/chat/mcp/oauth.ts
10773
10811
  import { randomUUID as randomUUID3 } from "crypto";
@@ -11145,7 +11183,7 @@ function createMcpAuthOrchestration(input) {
11145
11183
  sessionId
11146
11184
  }),
11147
11185
  delivery: reusingPendingLink ? "private_link_reused" : "private_link_sent",
11148
- ttlMs: THREAD_STATE_TTL_MS2
11186
+ ttlMs: THREAD_STATE_TTL_MS3
11149
11187
  });
11150
11188
  pendingPause = new McpAuthorizationPauseError(
11151
11189
  provider,
@@ -11442,7 +11480,7 @@ async function generateAssistantReply(messageText2, context) {
11442
11480
  await recordMcpProviderConnected({
11443
11481
  conversationId: timeoutResumeConversationId,
11444
11482
  provider,
11445
- ttlMs: THREAD_STATE_TTL_MS3
11483
+ ttlMs: THREAD_STATE_TTL_MS4
11446
11484
  });
11447
11485
  connectedMcpProviders.add(provider);
11448
11486
  };
@@ -11536,6 +11574,19 @@ async function generateAssistantReply(messageText2, context) {
11536
11574
  canRecordMcpProviders = Boolean(
11537
11575
  turnSessionState.canUseTurnSession && sessionConversationId && sessionId
11538
11576
  );
11577
+ const recordParentToolExecutionStart = async (event) => {
11578
+ if (!turnSessionState.canUseTurnSession || !sessionConversationId || !sessionId) {
11579
+ return;
11580
+ }
11581
+ await recordToolExecutionStarted({
11582
+ conversationId: sessionConversationId,
11583
+ sessionId,
11584
+ toolCallId: event.toolCallId,
11585
+ toolName: event.toolName,
11586
+ args: event.args,
11587
+ ttlMs: THREAD_STATE_TTL_MS4
11588
+ });
11589
+ };
11539
11590
  const persistedConfigurationValues = context.channelConfiguration ? await context.channelConfiguration.resolveValues() : {};
11540
11591
  configurationValues = {
11541
11592
  ...getConfigDefaults(),
@@ -11755,6 +11806,7 @@ async function generateAssistantReply(messageText2, context) {
11755
11806
  config: botConfig.advisor,
11756
11807
  conversationId: sessionConversationId,
11757
11808
  conversationPrivacy,
11809
+ parentSessionId: sessionId,
11758
11810
  logContext: spanContext,
11759
11811
  getTools: () => advisorTools,
11760
11812
  streamFn: createTracedStreamFn({ conversationPrivacy })
@@ -12081,6 +12133,9 @@ async function generateAssistantReply(messageText2, context) {
12081
12133
  }
12082
12134
  });
12083
12135
  const unsubscribe = agent.subscribe((event) => {
12136
+ if (event.type === "tool_execution_start") {
12137
+ return recordParentToolExecutionStart(event);
12138
+ }
12084
12139
  if (event.type === "turn_end" && event.toolResults.length > 0) {
12085
12140
  return persistSafeBoundary([...agent.state.messages]).then(
12086
12141
  () => void 0
@@ -99,13 +99,56 @@ var authorizationCompletedEntrySchema = z.object({
99
99
  requesterId: z.string().min(1),
100
100
  authorizationId: z.string().min(1)
101
101
  });
102
+ var transcriptRefSchema = z.object({
103
+ type: z.literal("advisor_session"),
104
+ parentConversationId: z.string().min(1),
105
+ key: z.string().min(1)
106
+ });
107
+ var toolExecutionStartedEntrySchema = z.object({
108
+ schemaVersion: z.literal(AGENT_SESSION_LOG_SCHEMA_VERSION),
109
+ type: z.literal("tool_execution_started"),
110
+ sessionId: z.string().min(1).default(INITIAL_SESSION_ID),
111
+ createdAtMs: z.number().int().nonnegative(),
112
+ toolCallId: z.string().min(1),
113
+ toolName: z.string().min(1),
114
+ args: z.unknown().optional()
115
+ });
116
+ var subagentStartedEntrySchema = z.object({
117
+ schemaVersion: z.literal(AGENT_SESSION_LOG_SCHEMA_VERSION),
118
+ type: z.literal("subagent_started"),
119
+ sessionId: z.string().min(1).default(INITIAL_SESSION_ID),
120
+ subagentInvocationId: z.string().min(1),
121
+ subagentKind: z.string().min(1),
122
+ parentToolCallId: z.string().min(1).optional(),
123
+ parentConversationId: z.string().min(1),
124
+ parentSessionId: z.string().min(1).optional(),
125
+ transcriptRef: transcriptRefSchema,
126
+ historyMode: z.literal("shared"),
127
+ createdAtMs: z.number().int().nonnegative()
128
+ });
129
+ var subagentEndedEntrySchema = z.object({
130
+ schemaVersion: z.literal(AGENT_SESSION_LOG_SCHEMA_VERSION),
131
+ type: z.literal("subagent_ended"),
132
+ sessionId: z.string().min(1).default(INITIAL_SESSION_ID),
133
+ subagentInvocationId: z.string().min(1),
134
+ outcome: z.union([
135
+ z.literal("success"),
136
+ z.literal("error"),
137
+ z.literal("aborted")
138
+ ]),
139
+ errorCode: z.string().min(1).optional(),
140
+ createdAtMs: z.number().int().nonnegative()
141
+ });
102
142
  var sessionLogEntrySchema = z.discriminatedUnion("type", [
103
143
  piMessageEntrySchema,
104
144
  projectionResetEntrySchema,
105
145
  requesterRecordedEntrySchema,
106
146
  mcpProviderConnectedEntrySchema,
107
147
  authorizationRequestedEntrySchema,
108
- authorizationCompletedEntrySchema
148
+ authorizationCompletedEntrySchema,
149
+ toolExecutionStartedEntrySchema,
150
+ subagentStartedEntrySchema,
151
+ subagentEndedEntrySchema
109
152
  ]);
110
153
  function key(scope) {
111
154
  const prefix = getChatConfig().state.keyPrefix;
@@ -133,6 +176,9 @@ function countMatchingPrefix(left, right) {
133
176
  function entrySessionId(entry) {
134
177
  return entry.sessionId ?? INITIAL_SESSION_ID;
135
178
  }
179
+ function isActivityEntry(entry) {
180
+ return entry.type === "tool_execution_started" || entry.type === "subagent_started" || entry.type === "subagent_ended";
181
+ }
136
182
  function latestProjectionResetIndex(entries) {
137
183
  for (let index = entries.length - 1; index >= 0; index -= 1) {
138
184
  if (entries[index]?.type === "projection_reset") {
@@ -257,6 +303,43 @@ function authorizationCompletedEntry(args) {
257
303
  authorizationId: args.authorizationId
258
304
  };
259
305
  }
306
+ function toolExecutionStartedEntry(args) {
307
+ return {
308
+ schemaVersion: AGENT_SESSION_LOG_SCHEMA_VERSION,
309
+ type: "tool_execution_started",
310
+ sessionId: args.sessionId,
311
+ createdAtMs: args.createdAtMs,
312
+ toolCallId: args.toolCallId,
313
+ toolName: args.toolName,
314
+ ...args.args !== void 0 ? { args: args.args } : {}
315
+ };
316
+ }
317
+ function subagentStartedEntry(args) {
318
+ return {
319
+ schemaVersion: AGENT_SESSION_LOG_SCHEMA_VERSION,
320
+ type: "subagent_started",
321
+ sessionId: args.sessionId,
322
+ subagentInvocationId: args.subagentInvocationId,
323
+ subagentKind: args.subagentKind,
324
+ ...args.parentToolCallId ? { parentToolCallId: args.parentToolCallId } : {},
325
+ parentConversationId: args.parentConversationId,
326
+ ...args.parentSessionId ? { parentSessionId: args.parentSessionId } : {},
327
+ transcriptRef: args.transcriptRef,
328
+ historyMode: args.historyMode,
329
+ createdAtMs: args.createdAtMs
330
+ };
331
+ }
332
+ function subagentEndedEntry(args) {
333
+ return {
334
+ schemaVersion: AGENT_SESSION_LOG_SCHEMA_VERSION,
335
+ type: "subagent_ended",
336
+ sessionId: args.sessionId,
337
+ subagentInvocationId: args.subagentInvocationId,
338
+ outcome: args.outcome,
339
+ ...args.errorCode ? { errorCode: args.errorCode } : {},
340
+ createdAtMs: args.createdAtMs
341
+ };
342
+ }
260
343
  function decode(value) {
261
344
  if (typeof value === "string") {
262
345
  return decode(JSON.parse(value));
@@ -286,13 +369,13 @@ function project(entries, sessionId) {
286
369
  messages.push(authorizationObservationMessage(entry));
287
370
  continue;
288
371
  }
289
- if (entry.type === "mcp_provider_connected" || entry.type === "authorization_requested") {
372
+ if (entry.type === "projection_reset") {
373
+ messages = [...entry.messages];
374
+ if (entry.requester) {
375
+ requester = entry.requester;
376
+ }
290
377
  continue;
291
378
  }
292
- messages = [...entry.messages];
293
- if (entry.requester) {
294
- requester = entry.requester;
295
- }
296
379
  }
297
380
  return { messages, requester };
298
381
  }
@@ -404,6 +487,10 @@ async function loadEntries(args) {
404
487
  const store = args.store ?? await defaultStore();
405
488
  return await store.read(args);
406
489
  }
490
+ async function loadActivityEntries(args) {
491
+ const entries = await loadEntries(args);
492
+ return projectionEntries(entries, args.sessionId).filter(isActivityEntry);
493
+ }
407
494
  async function loadMessages(args) {
408
495
  const messageCount = normalizeMessageCount(args.messageCount);
409
496
  if (messageCount === 0) {
@@ -482,6 +569,64 @@ async function recordAuthorizationCompleted(args) {
482
569
  ttlMs: args.ttlMs
483
570
  });
484
571
  }
572
+ async function recordToolExecutionStarted(args) {
573
+ const store = args.store ?? await defaultStore();
574
+ const entries = await store.read(args);
575
+ const sessionId = args.sessionId ?? currentSessionId(entries);
576
+ await store.append({
577
+ scope: args,
578
+ entries: [
579
+ toolExecutionStartedEntry({
580
+ args: args.args,
581
+ createdAtMs: args.createdAtMs ?? Date.now(),
582
+ sessionId,
583
+ toolCallId: args.toolCallId,
584
+ toolName: args.toolName
585
+ })
586
+ ],
587
+ ttlMs: args.ttlMs
588
+ });
589
+ }
590
+ async function recordSubagentStarted(args) {
591
+ const store = args.store ?? await defaultStore();
592
+ const entries = await store.read(args);
593
+ const sessionId = args.sessionId ?? currentSessionId(entries);
594
+ await store.append({
595
+ scope: args,
596
+ entries: [
597
+ subagentStartedEntry({
598
+ createdAtMs: args.createdAtMs ?? Date.now(),
599
+ historyMode: args.historyMode,
600
+ parentConversationId: args.parentConversationId,
601
+ parentSessionId: args.parentSessionId,
602
+ parentToolCallId: args.parentToolCallId,
603
+ sessionId,
604
+ subagentInvocationId: args.subagentInvocationId,
605
+ subagentKind: args.subagentKind,
606
+ transcriptRef: args.transcriptRef
607
+ })
608
+ ],
609
+ ttlMs: args.ttlMs
610
+ });
611
+ }
612
+ async function recordSubagentEnded(args) {
613
+ const store = args.store ?? await defaultStore();
614
+ const entries = await store.read(args);
615
+ const sessionId = args.sessionId ?? currentSessionId(entries);
616
+ await store.append({
617
+ scope: args,
618
+ entries: [
619
+ subagentEndedEntry({
620
+ createdAtMs: args.createdAtMs ?? Date.now(),
621
+ errorCode: args.errorCode,
622
+ outcome: args.outcome,
623
+ sessionId,
624
+ subagentInvocationId: args.subagentInvocationId
625
+ })
626
+ ],
627
+ ttlMs: args.ttlMs
628
+ });
629
+ }
485
630
  async function commitMessages(args) {
486
631
  const store = args.store ?? await defaultStore();
487
632
  const entries = await store.read(args);
@@ -2039,11 +2184,15 @@ export {
2039
2184
  buildPluginSystemPromptContributions,
2040
2185
  buildSystemPrompt,
2041
2186
  buildTurnContextPrompt,
2187
+ loadActivityEntries,
2042
2188
  loadProjection,
2043
2189
  loadConnectedMcpProviders,
2044
2190
  recordMcpProviderConnected,
2045
2191
  recordAuthorizationRequested,
2046
2192
  recordAuthorizationCompleted,
2193
+ recordToolExecutionStarted,
2194
+ recordSubagentStarted,
2195
+ recordSubagentEnded,
2047
2196
  commitMessages,
2048
2197
  getAgentTurnSessionRecord,
2049
2198
  upsertAgentTurnSessionRecord,
package/dist/cli/chat.js CHANGED
@@ -193,7 +193,7 @@ async function runPrompt(options, io, pluginSet) {
193
193
  defaultStateAdapterForLocalChat();
194
194
  await configureLocalChatPlugins(pluginSet);
195
195
  const conversationId = newRunConversationId();
196
- const { runLocalAgentTurn } = await import("../runner-WW4GJFUB.js");
196
+ const { runLocalAgentTurn } = await import("../runner-GEZ5FN4R.js");
197
197
  const result = await runLocalAgentTurn(
198
198
  {
199
199
  conversationId,
@@ -217,7 +217,7 @@ async function runInteractive(io, pluginSet) {
217
217
  defaultStateAdapterForLocalChat();
218
218
  await configureLocalChatPlugins(pluginSet);
219
219
  const conversationId = newRunConversationId();
220
- const { runLocalAgentTurn } = await import("../runner-WW4GJFUB.js");
220
+ const { runLocalAgentTurn } = await import("../runner-GEZ5FN4R.js");
221
221
  const rl = readline.createInterface({
222
222
  input: io.input,
223
223
  output: io.output,
@@ -67,6 +67,7 @@ export interface TranscriptMessage {
67
67
  timestamp?: number;
68
68
  }
69
69
  export interface ConversationRunReport extends ConversationSummaryReport {
70
+ activity?: ConversationActivityReport[];
70
71
  transcriptAvailable: boolean;
71
72
  transcriptMetadata?: TranscriptMessage[];
72
73
  transcriptMessageCount?: number;
@@ -74,6 +75,35 @@ export interface ConversationRunReport extends ConversationSummaryReport {
74
75
  transcriptRedactionReason?: "non_public_conversation";
75
76
  transcript: TranscriptMessage[];
76
77
  }
78
+ export type ConversationActivityStatus = "aborted" | "completed" | "error" | "running" | "success";
79
+ interface ActivityPayloadMetadata {
80
+ inputKeys?: string[];
81
+ inputSizeBytes?: number;
82
+ inputSizeChars?: number;
83
+ inputType?: string;
84
+ }
85
+ export interface ConversationSubagentActivityReport {
86
+ type: "subagent";
87
+ createdAt: string;
88
+ endedAt?: string;
89
+ id: string;
90
+ outcome?: "success" | "error" | "aborted";
91
+ parentToolCallId?: string;
92
+ status: ConversationActivityStatus;
93
+ subagentKind: string;
94
+ }
95
+ export interface ConversationToolActivityReport extends ActivityPayloadMetadata {
96
+ type: "tool_execution";
97
+ args?: unknown;
98
+ createdAt: string;
99
+ id: string;
100
+ redacted?: boolean;
101
+ status: ConversationActivityStatus;
102
+ subagents: ConversationSubagentActivityReport[];
103
+ toolCallId: string;
104
+ toolName: string;
105
+ }
106
+ export type ConversationActivityReport = ConversationToolActivityReport | ConversationSubagentActivityReport;
77
107
  export interface ConversationReport {
78
108
  conversationId: string;
79
109
  /** Always-populated display title, computed the same way as per-run reports. */
@@ -1,6 +1,6 @@
1
1
  import type { PluginOperationalReport } from "@sentry/junior-plugin-api";
2
2
  import { type ConversationFeed, type PluginConversationSummary, type ConversationReport, type ConversationStatsReport } from "./reporting/conversations";
3
- export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationUsage, RequesterIdentity, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting/conversations";
3
+ export type { PluginConversationStatus, PluginConversations, PluginConversationSummary, ConversationActivityReport, ConversationActivityStatus, ConversationFeed, ConversationReport, ConversationReportStatus, ConversationRunReport, ConversationSubagentActivityReport, ConversationStatsItem, ConversationStatsReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, RequesterIdentity, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./reporting/conversations";
4
4
  export interface HealthReport {
5
5
  status: "ok";
6
6
  service: string;
package/dist/reporting.js CHANGED
@@ -10,8 +10,9 @@ import {
10
10
  import {
11
11
  buildSystemPrompt,
12
12
  getAgentTurnSessionRecord,
13
- listAgentTurnSessionSummariesForConversation
14
- } from "./chunk-QDQVOMBA.js";
13
+ listAgentTurnSessionSummariesForConversation,
14
+ loadActivityEntries
15
+ } from "./chunk-R5T7QY3P.js";
15
16
  import {
16
17
  getPluginOperationalReports
17
18
  } from "./chunk-SSWBYEFH.js";
@@ -665,6 +666,86 @@ function redactTranscriptMessage(message) {
665
666
  parts: message.parts.map(redactTranscriptPart)
666
667
  };
667
668
  }
669
+ function toolResultStatuses(messages) {
670
+ const statuses = /* @__PURE__ */ new Map();
671
+ for (const message of messages) {
672
+ const record = message;
673
+ if (record.role !== "toolResult" || typeof record.toolCallId !== "string") {
674
+ continue;
675
+ }
676
+ statuses.set(record.toolCallId, record.isError ? "error" : "completed");
677
+ }
678
+ return statuses;
679
+ }
680
+ function activityPayloadFields(args, canExposePayload) {
681
+ if (args === void 0) {
682
+ return {};
683
+ }
684
+ return canExposePayload ? { args } : { redacted: true, ...redactedPayloadFields("input", args) };
685
+ }
686
+ function subagentActivity(entry, options) {
687
+ const end = options.end;
688
+ return {
689
+ type: "subagent",
690
+ id: entry.subagentInvocationId,
691
+ subagentKind: entry.subagentKind,
692
+ ...entry.parentToolCallId ? { parentToolCallId: entry.parentToolCallId } : {},
693
+ createdAt: new Date(entry.createdAtMs).toISOString(),
694
+ ...end ? {
695
+ endedAt: new Date(end.createdAtMs).toISOString(),
696
+ outcome: end.outcome,
697
+ status: end.outcome
698
+ } : { status: options.parentStatus ?? "running" }
699
+ };
700
+ }
701
+ function buildConversationActivity(args) {
702
+ const toolStatuses = toolResultStatuses(args.messages);
703
+ const subagentEnds = /* @__PURE__ */ new Map();
704
+ const subagentsByToolCallId = /* @__PURE__ */ new Map();
705
+ const orphanSubagents = [];
706
+ for (const entry of args.entries) {
707
+ if (entry.type === "subagent_ended") {
708
+ subagentEnds.set(entry.subagentInvocationId, entry);
709
+ }
710
+ }
711
+ for (const entry of args.entries) {
712
+ if (entry.type !== "subagent_started") {
713
+ continue;
714
+ }
715
+ const parentStatus = entry.parentToolCallId ? toolStatuses.get(entry.parentToolCallId) : void 0;
716
+ const activity = subagentActivity(entry, {
717
+ end: subagentEnds.get(entry.subagentInvocationId),
718
+ parentStatus
719
+ });
720
+ if (entry.parentToolCallId) {
721
+ subagentsByToolCallId.set(entry.parentToolCallId, [
722
+ ...subagentsByToolCallId.get(entry.parentToolCallId) ?? [],
723
+ activity
724
+ ]);
725
+ continue;
726
+ }
727
+ orphanSubagents.push(activity);
728
+ }
729
+ const rows = [];
730
+ for (const entry of args.entries) {
731
+ if (entry.type !== "tool_execution_started") {
732
+ continue;
733
+ }
734
+ rows.push({
735
+ type: "tool_execution",
736
+ id: entry.toolCallId,
737
+ toolCallId: entry.toolCallId,
738
+ toolName: entry.toolName,
739
+ createdAt: new Date(entry.createdAtMs).toISOString(),
740
+ status: toolStatuses.get(entry.toolCallId) ?? "running",
741
+ subagents: subagentsByToolCallId.get(entry.toolCallId) ?? [],
742
+ ...activityPayloadFields(entry.args, args.canExposePayload)
743
+ });
744
+ }
745
+ return [...rows, ...orphanSubagents].sort(
746
+ (left, right) => Date.parse(left.createdAt) - Date.parse(right.createdAt) || left.id.localeCompare(right.id)
747
+ );
748
+ }
668
749
  function isConversationMessageRole(role) {
669
750
  return role === "user" || role === "assistant";
670
751
  }
@@ -864,10 +945,13 @@ async function readConversationReport(conversationId, options = {}) {
864
945
  );
865
946
  const runs = await Promise.all(
866
947
  summaries.map(async (summary) => {
867
- const sessionRecord = await getAgentTurnSessionRecord(
868
- summary.conversationId,
869
- summary.sessionId
870
- );
948
+ const [sessionRecord, activityEntries] = await Promise.all([
949
+ getAgentTurnSessionRecord(summary.conversationId, summary.sessionId),
950
+ loadActivityEntries({
951
+ conversationId: summary.conversationId,
952
+ sessionId: summary.sessionId
953
+ })
954
+ ]);
871
955
  const scopedMessages = sessionRecord?.piMessages ? turnScopedMessages(
872
956
  sessionRecord.piMessages,
873
957
  sessionRecord.turnStartMessageIndex
@@ -876,6 +960,11 @@ async function readConversationReport(conversationId, options = {}) {
876
960
  const normalizedTranscript = scopedMessages.messages.map(
877
961
  normalizeTranscriptMessage
878
962
  );
963
+ const activity = buildConversationActivity({
964
+ canExposePayload: canExposeTranscript,
965
+ entries: activityEntries,
966
+ messages: scopedMessages.messages
967
+ });
879
968
  const transcriptMessageCount = countConversationMessages(normalizedTranscript);
880
969
  const transcript = canExposeTranscript ? [
881
970
  ...scopedMessages.startsAtRunBoundary && normalizedTranscript.length > 0 && sessionRecord?.source ? [systemPromptMessage(sessionRecord.source)] : [],
@@ -888,6 +977,7 @@ async function readConversationReport(conversationId, options = {}) {
888
977
  ...sessionReportFromSummary(summary, nowMs, details),
889
978
  ...traceId ? { traceId } : {},
890
979
  ...sentryTraceUrl ? { sentryTraceUrl } : {},
980
+ activity,
891
981
  transcriptAvailable: Boolean(sessionRecord) && canExposeTranscript,
892
982
  ...sessionRecord && transcriptMessageCount > 0 ? { transcriptMessageCount } : {},
893
983
  ...!canExposeTranscript ? {
@@ -911,6 +1001,7 @@ async function readConversationReport(conversationId, options = {}) {
911
1001
  const effectiveRuns = runs.length > 0 || !conversation ? runs : [
912
1002
  {
913
1003
  ...sessionReportFromConversation(conversation, nowMs, details),
1004
+ activity: [],
914
1005
  transcriptAvailable: false,
915
1006
  transcript: []
916
1007
  }
@@ -14,7 +14,7 @@ import {
14
14
  startActiveTurn,
15
15
  updateConversationStats,
16
16
  upsertConversationMessage
17
- } from "./chunk-2MSW5BZY.js";
17
+ } from "./chunk-NNM7YQLL.js";
18
18
  import {
19
19
  coerceThreadConversationState
20
20
  } from "./chunk-Z4CIQ3EB.js";
@@ -23,7 +23,7 @@ import "./chunk-KNFROR7R.js";
23
23
  import {
24
24
  commitMessages,
25
25
  loadProjection
26
- } from "./chunk-QDQVOMBA.js";
26
+ } from "./chunk-R5T7QY3P.js";
27
27
  import "./chunk-SSWBYEFH.js";
28
28
  import "./chunk-RARSKPVT.js";
29
29
  import "./chunk-237T7XAN.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.78.0",
3
+ "version": "0.79.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -70,7 +70,7 @@
70
70
  "pg": "^8.16.3",
71
71
  "yaml": "^2.9.0",
72
72
  "zod": "^4.4.3",
73
- "@sentry/junior-plugin-api": "0.78.0"
73
+ "@sentry/junior-plugin-api": "0.79.0"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@emnapi/core": "^1.10.0",
@@ -86,9 +86,9 @@
86
86
  "typescript": "^6.0.3",
87
87
  "vercel": "^54.4.0",
88
88
  "vitest": "^4.1.7",
89
- "@sentry/junior-memory": "0.78.0",
90
- "@sentry/junior-testing": "0.0.0",
91
- "@sentry/junior-scheduler": "0.78.0"
89
+ "@sentry/junior-scheduler": "0.79.0",
90
+ "@sentry/junior-memory": "0.79.0",
91
+ "@sentry/junior-testing": "0.0.0"
92
92
  },
93
93
  "scripts": {
94
94
  "build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",