@sentry/junior 0.77.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.
@@ -449,7 +449,7 @@ function destinationUpsertFromDestination(args) {
449
449
  };
450
450
  }
451
451
  function executionStatusFromValue(value) {
452
- if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
452
+ if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
453
453
  return value;
454
454
  }
455
455
  throw new Error("Conversation record execution status is invalid");
@@ -727,8 +727,8 @@ var SqlStore = class {
727
727
  executionUpdatedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_updated_at else ${juniorConversations.executionUpdatedAt} end`,
728
728
  executionStatus: sql2`case when ${incomingExecutionIsFresh} then excluded.execution_status else ${juniorConversations.executionStatus} end`,
729
729
  runId: sql2`case when ${incomingExecutionIsFresh} then excluded.run_id else ${juniorConversations.runId} end`,
730
- lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_checkpoint_at else ${juniorConversations.lastCheckpointAt} end`,
731
- lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then excluded.last_enqueued_at else ${juniorConversations.lastEnqueuedAt} end`
730
+ lastCheckpointAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_checkpoint_at, ${juniorConversations.lastCheckpointAt}) else ${juniorConversations.lastCheckpointAt} end`,
731
+ lastEnqueuedAt: sql2`case when ${incomingExecutionIsFresh} then coalesce(excluded.last_enqueued_at, ${juniorConversations.lastEnqueuedAt}) else ${juniorConversations.lastEnqueuedAt} end`
732
732
  }
733
733
  });
734
734
  }
@@ -74,7 +74,7 @@ function normalizeSource(value) {
74
74
  return void 0;
75
75
  }
76
76
  function normalizeExecutionStatus(value) {
77
- if (value === "awaiting_resume" || value === "idle" || value === "pending" || value === "running") {
77
+ if (value === "awaiting_resume" || value === "failed" || value === "idle" || value === "pending" || value === "running") {
78
78
  return value;
79
79
  }
80
80
  return void 0;
@@ -231,8 +231,11 @@ function isLeaseActive(lease, nowMs) {
231
231
  function pendingMessages(conversation) {
232
232
  return [...conversation.execution.pendingMessages].sort(compareMessages);
233
233
  }
234
+ function isRunnableStatus(status) {
235
+ return status !== "failed" && status !== "idle";
236
+ }
234
237
  function hasRunnableWork(conversation) {
235
- return conversation.execution.status !== "idle" || pendingMessages(conversation).length > 0;
238
+ return isRunnableStatus(conversation.execution.status) || pendingMessages(conversation).length > 0;
236
239
  }
237
240
  function executionWithPendingMessages(execution, pending) {
238
241
  const pendingMessages2 = [...pending].sort(compareMessages);
@@ -736,6 +739,48 @@ async function recordConversationActivity(args) {
736
739
  });
737
740
  });
738
741
  }
742
+ async function recordConversationExecution(args) {
743
+ const nowMs = args.updatedAtMs;
744
+ await withConversationMutation(args, async (state) => {
745
+ const existing = await readConversation(state, args.conversationId);
746
+ if (existing && args.destination) {
747
+ assertSameConversationDestination({
748
+ conversationId: args.conversationId,
749
+ current: existing.destination,
750
+ next: args.destination
751
+ });
752
+ }
753
+ const current = existing ?? emptyConversation({
754
+ conversationId: args.conversationId,
755
+ destination: args.destination,
756
+ nowMs,
757
+ source: args.source
758
+ });
759
+ await writeConversation(
760
+ state,
761
+ withExecutionUpdate(
762
+ {
763
+ ...current,
764
+ ...current.destination ?? args.destination ? { destination: current.destination ?? args.destination } : {},
765
+ ...current.source ?? args.source ? { source: current.source ?? args.source } : {},
766
+ ...current.channelName ?? args.channelName ? { channelName: current.channelName ?? args.channelName } : {},
767
+ ...current.requester ?? args.requester ? { requester: current.requester ?? args.requester } : {},
768
+ ...current.title ?? args.title ? { title: current.title ?? args.title } : {},
769
+ createdAtMs: Math.min(current.createdAtMs, args.createdAtMs),
770
+ lastActivityAtMs: Math.max(
771
+ current.lastActivityAtMs,
772
+ args.lastActivityAtMs
773
+ )
774
+ },
775
+ {
776
+ ...current.execution,
777
+ ...args.execution
778
+ },
779
+ nowMs
780
+ )
781
+ );
782
+ });
783
+ }
739
784
  async function markConversationWorkEnqueued(args) {
740
785
  const nowMs = args.nowMs ?? now();
741
786
  await withConversationMutation(args, async (state) => {
@@ -1061,6 +1106,7 @@ export {
1061
1106
  appendInboundMessage,
1062
1107
  requestConversationWork,
1063
1108
  recordConversationActivity,
1109
+ recordConversationExecution,
1064
1110
  markConversationWorkEnqueued,
1065
1111
  startConversationWork,
1066
1112
  checkInConversationWork,
@@ -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-TO3UAY2M.js";
34
+ } from "./chunk-R5T7QY3P.js";
32
35
  import {
33
36
  createPluginEmbedder,
34
37
  createPluginHookRunner,
@@ -39,14 +42,14 @@ import {
39
42
  getPlugins,
40
43
  getSlackToolContext,
41
44
  resolveChannelCapabilities
42
- } from "./chunk-WBSGTHNO.js";
45
+ } from "./chunk-SSWBYEFH.js";
43
46
  import {
44
47
  createPluginLogger,
45
48
  createPluginState
46
49
  } from "./chunk-RARSKPVT.js";
47
50
  import {
48
51
  getDb
49
- } from "./chunk-NYKJ3KON.js";
52
+ } from "./chunk-237T7XAN.js";
50
53
  import {
51
54
  SANDBOX_DATA_ROOT,
52
55
  SANDBOX_SKILLS_ROOT,
@@ -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
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getConversationStore
3
- } from "./chunk-NYKJ3KON.js";
3
+ } from "./chunk-237T7XAN.js";
4
4
  import {
5
5
  SANDBOX_DATA_ROOT,
6
6
  SANDBOX_WORKSPACE_ROOT,
@@ -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);
@@ -1567,6 +1712,14 @@ function parseAgentTurnSessionStatus(parsed) {
1567
1712
  function parseAgentTurnSurface(value) {
1568
1713
  return value === "slack" || value === "api" || value === "scheduler" || value === "internal" ? value : void 0;
1569
1714
  }
1715
+ function conversationExecutionFromSummary(summary) {
1716
+ const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
1717
+ return {
1718
+ status,
1719
+ runId: summary.sessionId,
1720
+ updatedAtMs: summary.updatedAtMs
1721
+ };
1722
+ }
1570
1723
  function parseSource(value) {
1571
1724
  const result = sourceSchema.safeParse(value);
1572
1725
  return result.success ? result.data : void 0;
@@ -1690,6 +1843,17 @@ async function recordConversationActivityMetadata(args) {
1690
1843
  requester: sessionLogRequester(args.summary.requester),
1691
1844
  source
1692
1845
  });
1846
+ await conversationStore.recordExecution({
1847
+ channelName: args.summary.channelName,
1848
+ conversationId: args.summary.conversationId,
1849
+ createdAtMs: args.summary.startedAtMs,
1850
+ destination: args.summary.destination,
1851
+ execution: conversationExecutionFromSummary(args.summary),
1852
+ lastActivityAtMs: args.summary.updatedAtMs,
1853
+ requester: sessionLogRequester(args.summary.requester),
1854
+ source,
1855
+ updatedAtMs: args.nowMs
1856
+ });
1693
1857
  } catch (error) {
1694
1858
  logWarn(
1695
1859
  "conversation_activity_metadata_update_failed",
@@ -2020,11 +2184,15 @@ export {
2020
2184
  buildPluginSystemPromptContributions,
2021
2185
  buildSystemPrompt,
2022
2186
  buildTurnContextPrompt,
2187
+ loadActivityEntries,
2023
2188
  loadProjection,
2024
2189
  loadConnectedMcpProviders,
2025
2190
  recordMcpProviderConnected,
2026
2191
  recordAuthorizationRequested,
2027
2192
  recordAuthorizationCompleted,
2193
+ recordToolExecutionStarted,
2194
+ recordSubagentStarted,
2195
+ recordSubagentEnded,
2028
2196
  commitMessages,
2029
2197
  getAgentTurnSessionRecord,
2030
2198
  upsertAgentTurnSessionRecord,
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-RARSKPVT.js";
5
5
  import {
6
6
  getDb
7
- } from "./chunk-NYKJ3KON.js";
7
+ } from "./chunk-237T7XAN.js";
8
8
  import {
9
9
  SANDBOX_WORKSPACE_ROOT
10
10
  } from "./chunk-G3E7SCME.js";
@@ -606,6 +606,29 @@ function getPluginRoutes() {
606
606
  }
607
607
  return routes;
608
608
  }
609
+ function getPluginDashboardRoutes() {
610
+ const routes = [];
611
+ for (const plugin of getPlugins()) {
612
+ const pluginName = plugin.manifest.name;
613
+ const hook = plugin.hooks?.dashboardRoutes;
614
+ if (!hook) {
615
+ continue;
616
+ }
617
+ const app = hook({
618
+ ...basePluginContext(plugin)
619
+ });
620
+ if (app === void 0) {
621
+ continue;
622
+ }
623
+ if (!isRecord(app) || typeof app.fetch !== "function") {
624
+ throw new Error(
625
+ `Plugin dashboardRoutes hook from plugin "${pluginName}" must return a fetch-compatible app`
626
+ );
627
+ }
628
+ routes.push({ app, pluginName });
629
+ }
630
+ return routes;
631
+ }
609
632
  function trustedSlackConversationUrl(pluginName, link) {
610
633
  const url = typeof link?.url === "string" ? link.url.trim() : "";
611
634
  if (!url) {
@@ -958,6 +981,7 @@ export {
958
981
  getPluginUserPromptContributions,
959
982
  getPluginTools,
960
983
  getPluginRoutes,
984
+ getPluginDashboardRoutes,
961
985
  getPluginSlackConversationLink,
962
986
  getPluginOperationalReports,
963
987
  createPluginHookRunner
package/dist/cli/chat.js CHANGED
@@ -130,10 +130,10 @@ async function configureLocalChatPlugins(pluginSet) {
130
130
  databaseModule
131
131
  ] = await Promise.all([
132
132
  import("../plugins-PZMDS7AT.js"),
133
- import("../agent-hooks-7P2WSR4R.js"),
133
+ import("../agent-hooks-OFDNZJB2.js"),
134
134
  import("../registry-RRIDPJBT.js"),
135
135
  import("../validation-MDMYBRFB.js"),
136
- import("../db-7A7PFRGL.js")
136
+ import("../db-NGQ3JCMF.js")
137
137
  ]);
138
138
  const resolvedPluginSet = pluginSet === void 0 ? await loadLocalPluginSet() : pluginSet ?? void 0;
139
139
  const plugins = pluginsModule.pluginRuntimeRegistrationsFromPluginSet(resolvedPluginSet);
@@ -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-JWLZI3EX.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-JWLZI3EX.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,
@@ -10,13 +10,13 @@ import {
10
10
  import {
11
11
  setPlugins,
12
12
  validatePlugins
13
- } from "../chunk-WBSGTHNO.js";
13
+ } from "../chunk-SSWBYEFH.js";
14
14
  import {
15
15
  createPluginLogger
16
16
  } from "../chunk-RARSKPVT.js";
17
17
  import {
18
18
  getDb
19
- } from "../chunk-NYKJ3KON.js";
19
+ } from "../chunk-237T7XAN.js";
20
20
  import "../chunk-G3E7SCME.js";
21
21
  import "../chunk-LXTPBU4K.js";
22
22
  import "../chunk-Q6XFTRV5.js";