@sentry/junior 0.104.1 → 0.105.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.
@@ -673,12 +673,24 @@ export declare const conversationStatsItemSchema: z.ZodObject<{
673
673
  costUsd: z.ZodOptional<z.ZodNumber>;
674
674
  tokens: z.ZodOptional<z.ZodNumber>;
675
675
  }, z.core.$strict>;
676
+ export declare const conversationMetricDaySchema: z.ZodObject<{
677
+ costUsd: z.ZodOptional<z.ZodNumber>;
678
+ date: z.ZodString;
679
+ durationMs: z.ZodNumber;
680
+ tokens: z.ZodOptional<z.ZodNumber>;
681
+ }, z.core.$strict>;
676
682
  export declare const conversationStatsReportSchema: z.ZodObject<{
677
683
  active: z.ZodNumber;
678
684
  conversations: z.ZodNumber;
679
685
  durationMs: z.ZodNumber;
680
686
  failed: z.ZodNumber;
681
687
  generatedAt: z.ZodString;
688
+ metricDays: z.ZodArray<z.ZodObject<{
689
+ costUsd: z.ZodOptional<z.ZodNumber>;
690
+ date: z.ZodString;
691
+ durationMs: z.ZodNumber;
692
+ tokens: z.ZodOptional<z.ZodNumber>;
693
+ }, z.core.$strict>>;
682
694
  locations: z.ZodArray<z.ZodObject<{
683
695
  active: z.ZodNumber;
684
696
  conversations: z.ZodNumber;
@@ -722,4 +734,5 @@ export type ConversationDetailReport = z.infer<typeof conversationDetailReportSc
722
734
  export type ConversationSubagentTranscriptReport = z.infer<typeof conversationSubagentTranscriptReportSchema>;
723
735
  export type ConversationFeed = z.infer<typeof conversationFeedSchema>;
724
736
  export type ConversationStatsItem = z.infer<typeof conversationStatsItemSchema>;
737
+ export type ConversationMetricDay = z.infer<typeof conversationMetricDaySchema>;
725
738
  export type ConversationStatsReport = z.infer<typeof conversationStatsReportSchema>;
@@ -1,7 +1,7 @@
1
1
  export { dailyConversationActivitySchema } from "./activity";
2
2
  export type { DailyConversationActivity } from "./activity";
3
3
  export { conversationDetailReportSchema, conversationFeedSchema, conversationStatsReportSchema, conversationSubagentTranscriptReportSchema, } from "./conversations/schema";
4
- export type { ActorIdentity, ConversationActivityReport, ConversationActivityStatus, ConversationContextEvent, ConversationCost, ConversationDetailReport, ConversationFeed, ConversationReportStatus, ConversationStatsItem, ConversationStatsReport, ConversationSubagentActivityReport, ConversationSubagentTranscriptReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./conversations/schema";
4
+ export type { ActorIdentity, ConversationActivityReport, ConversationActivityStatus, ConversationContextEvent, ConversationCost, ConversationDetailReport, ConversationFeed, ConversationReportStatus, ConversationMetricDay, ConversationStatsItem, ConversationStatsReport, ConversationSubagentActivityReport, ConversationSubagentTranscriptReport, ConversationSummaryReport, ConversationSurface, ConversationToolActivityReport, ConversationUsage, TranscriptMessage, TranscriptPart, TranscriptPartType, TranscriptRole, } from "./conversations/schema";
5
5
  export { actorDirectoryReportSchema, actorProfileReportSchema, } from "./people/schema";
6
6
  export { locationActivityDayReportSchema, locationDetailReportSchema, locationDirectoryReportSchema, } from "./locations/schema";
7
7
  export type { LocationActorSummaryReport, LocationActivityDayReport, LocationDetailReport, LocationDirectoryReport, LocationSummaryReport, } from "./locations/schema";
@@ -14,7 +14,7 @@ import {
14
14
  locationParamsSchema,
15
15
  personParamsSchema,
16
16
  subagentParamsSchema
17
- } from "../chunk-AIRE7Q2W.js";
17
+ } from "../chunk-CCFROYAV.js";
18
18
  import {
19
19
  healthReportSchema,
20
20
  pluginOperationalReportFeedSchema,
package/dist/api.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  locationParamsSchema,
14
14
  personParamsSchema,
15
15
  subagentParamsSchema
16
- } from "./chunk-AIRE7Q2W.js";
16
+ } from "./chunk-CCFROYAV.js";
17
17
  import {
18
18
  buildSentryConversationUrl,
19
19
  buildSentryTraceUrl,
@@ -1116,7 +1116,7 @@ var detail_default = {
1116
1116
  };
1117
1117
 
1118
1118
  // src/api/conversations/stats.query.ts
1119
- import { and as and3, eq as eq3, gte, isNull as isNull2, lte } from "drizzle-orm";
1119
+ import { and as and3, eq as eq3, gte, isNull as isNull2, lte, sql as sql2 } from "drizzle-orm";
1120
1120
 
1121
1121
  // src/api/conversations/aggregate.ts
1122
1122
  import { sql } from "drizzle-orm";
@@ -1187,7 +1187,7 @@ function conversationActiveDaysColumn() {
1187
1187
  }
1188
1188
 
1189
1189
  // src/api/conversations/stats.query.ts
1190
- var WINDOW_MS = 90 * 24 * 60 * 60 * 1e3;
1190
+ var WINDOW_DAYS = 90;
1191
1191
  function emptyStatsItem(label) {
1192
1192
  return {
1193
1193
  active: 0,
@@ -1248,9 +1248,39 @@ function statsWhere(start, end) {
1248
1248
  lte(juniorConversations.lastActivityAt, end)
1249
1249
  );
1250
1250
  }
1251
+ function statsWindow(nowMs) {
1252
+ const end = new Date(nowMs);
1253
+ const start = new Date(nowMs);
1254
+ start.setUTCHours(0, 0, 0, 0);
1255
+ start.setUTCDate(start.getUTCDate() - (WINDOW_DAYS - 1));
1256
+ return { end, start };
1257
+ }
1258
+ function metricDays(rows, endMs) {
1259
+ const byDate = new Map(rows.map((row) => [row.date, row]));
1260
+ const end = new Date(endMs);
1261
+ end.setUTCHours(0, 0, 0, 0);
1262
+ const start = new Date(end);
1263
+ start.setUTCDate(start.getUTCDate() - (WINDOW_DAYS - 1));
1264
+ const days = [];
1265
+ for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
1266
+ const date = cursor.toISOString().slice(0, 10);
1267
+ const row = byDate.get(date);
1268
+ days.push({
1269
+ date,
1270
+ durationMs: row?.durationMs ?? 0,
1271
+ ...row?.costUsd !== null && row?.costUsd !== void 0 ? { costUsd: row.costUsd } : {},
1272
+ ...row?.tokens !== null && row?.tokens !== void 0 ? { tokens: row.tokens } : {}
1273
+ });
1274
+ }
1275
+ return days;
1276
+ }
1251
1277
  async function aggregateStats(db, start, end) {
1252
1278
  const where = statsWhere(start, end);
1253
- const [totalsRows, actorRows, locationRows] = await Promise.all([
1279
+ const activityDate = sql2`TO_CHAR(
1280
+ ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
1281
+ 'YYYY-MM-DD'
1282
+ )`;
1283
+ const [totalsRows, actorRows, locationRows, metricRows] = await Promise.all([
1254
1284
  db.select(conversationAggregateColumns()).from(juniorConversations).where(where),
1255
1285
  db.select({
1256
1286
  identityDisplayName: juniorIdentities.displayName,
@@ -1289,17 +1319,23 @@ async function aggregateStats(db, start, end) {
1289
1319
  juniorDestinations.kind,
1290
1320
  juniorDestinations.provider,
1291
1321
  juniorDestinations.visibility
1292
- )
1322
+ ),
1323
+ db.select({
1324
+ costUsd: conversationAggregateColumns().costUsd,
1325
+ date: activityDate,
1326
+ durationMs: conversationAggregateColumns().durationMs,
1327
+ tokens: conversationAggregateColumns().tokens
1328
+ }).from(juniorConversations).where(where).groupBy(activityDate)
1293
1329
  ]);
1294
- return { actorRows, locationRows, totals: totalsRows[0] };
1330
+ return { actorRows, locationRows, metricRows, totals: totalsRows[0] };
1295
1331
  }
1296
1332
  async function readConversationStatsFromSql() {
1297
1333
  const nowMs = Date.now();
1298
- const windowStartMs = nowMs - WINDOW_MS;
1299
- const { actorRows, locationRows, totals } = await aggregateStats(
1334
+ const { end, start } = statsWindow(nowMs);
1335
+ const { actorRows, locationRows, metricRows, totals } = await aggregateStats(
1300
1336
  getDb(),
1301
- new Date(windowStartMs),
1302
- new Date(nowMs)
1337
+ start,
1338
+ end
1303
1339
  );
1304
1340
  const actors = /* @__PURE__ */ new Map();
1305
1341
  const locations = /* @__PURE__ */ new Map();
@@ -1315,13 +1351,14 @@ async function readConversationStatsFromSql() {
1315
1351
  durationMs: totals?.durationMs ?? 0,
1316
1352
  failed: totals?.failed ?? 0,
1317
1353
  generatedAt: new Date(nowMs).toISOString(),
1354
+ metricDays: metricDays(metricRows, nowMs),
1318
1355
  locations: statsItems(locations),
1319
1356
  actors: statsItems(actors),
1320
1357
  source: "conversation_index",
1321
1358
  ...totals?.costUsd !== null && totals?.costUsd !== void 0 ? { costUsd: addUsd(void 0, totals.costUsd) } : {},
1322
1359
  ...totals?.tokens !== null && totals?.tokens !== void 0 ? { tokens: totals.tokens } : {},
1323
- windowEnd: new Date(nowMs).toISOString(),
1324
- windowStart: new Date(windowStartMs).toISOString()
1360
+ windowEnd: end.toISOString(),
1361
+ windowStart: start.toISOString()
1325
1362
  };
1326
1363
  }
1327
1364
 
@@ -1387,7 +1424,7 @@ function createConversationRoutes() {
1387
1424
  import { Hono as Hono2 } from "hono";
1388
1425
 
1389
1426
  // src/api/locations/query.ts
1390
- import { and as and4, asc as asc2, desc as desc2, eq as eq4, gte as gte2, isNull as isNull3, sql as sql2 } from "drizzle-orm";
1427
+ import { and as and4, asc as asc2, desc as desc2, eq as eq4, gte as gte2, isNull as isNull3, sql as sql3 } from "drizzle-orm";
1391
1428
 
1392
1429
  // src/api/conversations/reporting.ts
1393
1430
  var PRIVATE_CONVERSATION_LABEL2 = "Private Conversation";
@@ -1579,12 +1616,12 @@ async function directoryRows(db) {
1579
1616
  ).where(topLevelWhere()).groupBy(...locationGroupBy());
1580
1617
  }
1581
1618
  async function directoryActivityRows(db, start) {
1582
- const date = sql2`TO_CHAR(
1619
+ const date = sql3`TO_CHAR(
1583
1620
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
1584
1621
  'YYYY-MM-DD'
1585
1622
  )`;
1586
1623
  return db.select({
1587
- conversations: sql2`COUNT(*)::integer`,
1624
+ conversations: sql3`COUNT(*)::integer`,
1588
1625
  date,
1589
1626
  visibility: juniorDestinations.visibility
1590
1627
  }).from(juniorConversations).leftJoin(
@@ -1664,7 +1701,7 @@ async function recentLocationRows(db, locationId) {
1664
1701
  destinationId: juniorDestinations.id,
1665
1702
  destinationVisibility: juniorDestinations.visibility,
1666
1703
  durationMs: juniorConversations.durationMs,
1667
- email: sql2`COALESCE(
1704
+ email: sql3`COALESCE(
1668
1705
  ${juniorUsers.primaryEmailNormalized},
1669
1706
  ${juniorIdentities.email}
1670
1707
  )`,
@@ -1673,7 +1710,7 @@ async function recentLocationRows(db, locationId) {
1673
1710
  fullName: juniorUsers.displayName,
1674
1711
  handle: juniorIdentities.handle,
1675
1712
  lastActivityAt: juniorConversations.lastActivityAt,
1676
- providerSubjectId: sql2`CASE
1713
+ providerSubjectId: sql3`CASE
1677
1714
  WHEN ${juniorIdentities.provider} = 'slack'
1678
1715
  THEN ${juniorIdentities.providerSubjectId}
1679
1716
  ELSE NULL
@@ -1705,7 +1742,7 @@ async function readLocationDetailFromSql(locationId) {
1705
1742
  const start = new Date(end);
1706
1743
  start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
1707
1744
  const where = publicLocationWhere(locationId);
1708
- const activityDate = sql2`TO_CHAR(
1745
+ const activityDate = sql3`TO_CHAR(
1709
1746
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
1710
1747
  'YYYY-MM-DD'
1711
1748
  )`;
@@ -1830,10 +1867,10 @@ function createLocationRoutes() {
1830
1867
  import { Hono as Hono3 } from "hono";
1831
1868
 
1832
1869
  // src/api/people/list.query.ts
1833
- import { and as and6, eq as eq6, gte as gte3, sql as sql4 } from "drizzle-orm";
1870
+ import { and as and6, eq as eq6, gte as gte3, sql as sql5 } from "drizzle-orm";
1834
1871
 
1835
1872
  // src/api/people/shared.ts
1836
- import { and as and5, asc as asc3, desc as desc3, eq as eq5, isNull as isNull4, sql as sql3 } from "drizzle-orm";
1873
+ import { and as and5, asc as asc3, desc as desc3, eq as eq5, isNull as isNull4, sql as sql4 } from "drizzle-orm";
1837
1874
  var RECENT_LIMIT2 = 25;
1838
1875
  var ACTIVITY_DAYS2 = 365;
1839
1876
  function normalizeEmail(email) {
@@ -1880,7 +1917,7 @@ function verifiedActorWhere(email) {
1880
1917
  return and5(
1881
1918
  eq5(juniorIdentities.provider, "slack"),
1882
1919
  eq5(juniorIdentities.emailVerified, true),
1883
- sql3`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
1920
+ sql4`${juniorUsers.primaryEmailNormalized} IS NOT NULL`,
1884
1921
  normalizedEmail ? eq5(juniorUsers.primaryEmailNormalized, normalizedEmail) : void 0
1885
1922
  );
1886
1923
  }
@@ -1939,7 +1976,7 @@ function directoryActivityDays2(rows, nowMs) {
1939
1976
  async function readPeopleListFromSql() {
1940
1977
  const nowMs = Date.now();
1941
1978
  const { end, start } = activityWindow(nowMs);
1942
- const activityDate = sql4`TO_CHAR(
1979
+ const activityDate = sql5`TO_CHAR(
1943
1980
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
1944
1981
  'YYYY-MM-DD'
1945
1982
  )`;
@@ -1947,8 +1984,8 @@ async function readPeopleListFromSql() {
1947
1984
  getDb().select({
1948
1985
  email: juniorUsers.primaryEmailNormalized,
1949
1986
  fullName: juniorUsers.displayName,
1950
- slackUserId: sql4`MAX(${juniorIdentities.providerSubjectId})`,
1951
- slackUserName: sql4`MAX(${juniorIdentities.handle})`,
1987
+ slackUserId: sql5`MAX(${juniorIdentities.providerSubjectId})`,
1988
+ slackUserName: sql5`MAX(${juniorIdentities.handle})`,
1952
1989
  activeDays: conversationActiveDaysColumn(),
1953
1990
  ...conversationAggregateColumns(),
1954
1991
  ...conversationRangeColumns()
@@ -1957,8 +1994,8 @@ async function readPeopleListFromSql() {
1957
1994
  eq6(juniorIdentities.id, juniorConversations.actorIdentityId)
1958
1995
  ).innerJoin(juniorUsers, eq6(juniorUsers.id, juniorIdentities.userId)).where(verifiedActorWhere()).groupBy(juniorUsers.primaryEmailNormalized, juniorUsers.displayName),
1959
1996
  getDb().select({
1960
- activePeople: sql4`COUNT(DISTINCT ${juniorUsers.id})::int`,
1961
- conversations: sql4`COUNT(*)::int`,
1997
+ activePeople: sql5`COUNT(DISTINCT ${juniorUsers.id})::int`,
1998
+ conversations: sql5`COUNT(*)::int`,
1962
1999
  date: activityDate
1963
2000
  }).from(juniorConversations).innerJoin(
1964
2001
  juniorIdentities,
@@ -2012,7 +2049,7 @@ var list_default3 = {
2012
2049
  };
2013
2050
 
2014
2051
  // src/api/people/profile.query.ts
2015
- import { and as and7, eq as eq7, gte as gte4, sql as sql5 } from "drizzle-orm";
2052
+ import { and as and7, eq as eq7, gte as gte4, sql as sql6 } from "drizzle-orm";
2016
2053
  function emptyProfile(email, nowMs) {
2017
2054
  const end = new Date(nowMs);
2018
2055
  end.setUTCHours(0, 0, 0, 0);
@@ -2047,7 +2084,7 @@ function addAggregate2(map, label, row) {
2047
2084
  map.set(label, item);
2048
2085
  }
2049
2086
  function surfaceExpression() {
2050
- return sql5`CASE
2087
+ return sql6`CASE
2051
2088
  WHEN ${juniorConversations.source} IN ('api', 'scheduler', 'slack')
2052
2089
  THEN ${juniorConversations.source}
2053
2090
  WHEN ${juniorConversations.conversationId} LIKE 'slack:%' THEN 'slack'
@@ -2075,17 +2112,17 @@ async function readPeopleProfileFromSql(email) {
2075
2112
  start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
2076
2113
  const where = verifiedActorWhere(normalizedEmail);
2077
2114
  const surface = surfaceExpression();
2078
- const activityDate = sql5`TO_CHAR(
2115
+ const activityDate = sql6`TO_CHAR(
2079
2116
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
2080
2117
  'YYYY-MM-DD'
2081
2118
  )`;
2082
- const channel = sql5`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
2119
+ const channel = sql6`SPLIT_PART(${juniorConversations.conversationId}, ':', 2)`;
2083
2120
  const [totalsRows, dayRows, locationRows, surfaceRows, recentRows] = await Promise.all([
2084
2121
  getDb().select({
2085
2122
  email: juniorUsers.primaryEmailNormalized,
2086
2123
  fullName: juniorUsers.displayName,
2087
- slackUserId: sql5`MAX(${juniorIdentities.providerSubjectId})`,
2088
- slackUserName: sql5`MAX(${juniorIdentities.handle})`,
2124
+ slackUserId: sql6`MAX(${juniorIdentities.providerSubjectId})`,
2125
+ slackUserName: sql6`MAX(${juniorIdentities.handle})`,
2089
2126
  activeDays: conversationActiveDaysColumn(),
2090
2127
  ...conversationAggregateColumns()
2091
2128
  }).from(juniorConversations).innerJoin(
package/dist/app.js CHANGED
@@ -102,7 +102,7 @@ import {
102
102
  splitSlackReplyText,
103
103
  startOAuthFlow,
104
104
  truncateStatusText
105
- } from "./chunk-TMWUGGSA.js";
105
+ } from "./chunk-NJJFU6CP.js";
106
106
  import {
107
107
  CooperativeTurnYieldError,
108
108
  TurnInputCommitLostError,
@@ -520,6 +520,7 @@ async function verifyDispatchCallbackRequest(request) {
520
520
  import { createHash } from "crypto";
521
521
  import {
522
522
  destinationSchema,
523
+ destinationVisibilitySchema,
523
524
  isSlackDestination,
524
525
  sourceSchema
525
526
  } from "@sentry/junior-plugin-api";
@@ -550,6 +551,7 @@ var dispatchRecordSchema = z.object({
550
551
  createdAtMs: z.number().finite(),
551
552
  credentialSubject: credentialSubjectSchema.optional(),
552
553
  destination: destinationSchema,
554
+ destinationVisibility: destinationVisibilitySchema,
553
555
  errorMessage: z.string().optional(),
554
556
  id: nonEmptyExactStringSchema,
555
557
  idempotencyKey: z.string().min(1),
@@ -735,6 +737,7 @@ async function createOrGetDispatch(args) {
735
737
  createdAtMs: args.nowMs,
736
738
  ...args.options.credentialSubject ? { credentialSubject: args.options.credentialSubject } : {},
737
739
  destination: args.options.destination,
740
+ destinationVisibility: args.options.destinationVisibility,
738
741
  id,
739
742
  idempotencyKey: args.options.idempotencyKey,
740
743
  input: args.options.input,
@@ -964,6 +967,7 @@ async function runAgentDispatchSlice(callback, deps) {
964
967
  ...dispatch.credentialSubject ? { subject: dispatch.credentialSubject } : {}
965
968
  },
966
969
  destination: dispatch.destination,
970
+ destinationVisibility: dispatch.destinationVisibility,
967
971
  source: dispatch.source,
968
972
  dispatch: {
969
973
  actor: dispatch.actor,
@@ -1106,6 +1110,7 @@ async function runAgentDispatchSlice(callback, deps) {
1106
1110
  usage: reply.diagnostics.usage,
1107
1111
  reasoningLevel: reply.diagnostics.reasoningLevel,
1108
1112
  destination: dispatch.destination,
1113
+ destinationVisibility: dispatch.destinationVisibility,
1109
1114
  source: dispatch.source,
1110
1115
  actor: dispatch.actor,
1111
1116
  surface: "api",
@@ -9197,6 +9202,25 @@ function getSlackMessageTs(message) {
9197
9202
  return void 0;
9198
9203
  }
9199
9204
 
9205
+ // src/chat/slack/action-token.ts
9206
+ import { z as z10 } from "zod";
9207
+ var slackActionTokenSchema = z10.string().trim().min(1).brand();
9208
+ var slackMessageEnvelopeSchema = z10.object({
9209
+ raw: z10.object({
9210
+ action_token: z10.unknown().optional()
9211
+ }).optional()
9212
+ });
9213
+ function readSlackActionToken(message) {
9214
+ const envelope = slackMessageEnvelopeSchema.safeParse(message);
9215
+ if (!envelope.success) {
9216
+ return void 0;
9217
+ }
9218
+ const token = slackActionTokenSchema.safeParse(
9219
+ envelope.data.raw?.action_token
9220
+ );
9221
+ return token.success ? token.data : void 0;
9222
+ }
9223
+
9200
9224
  // src/chat/slack/assistant-thread/title.ts
9201
9225
  function maybeUpdateAssistantTitle(args) {
9202
9226
  const assistantThreadContext = args.assistantThreadContext;
@@ -9490,6 +9514,7 @@ function createReplyToThread(deps) {
9490
9514
  threadTs,
9491
9515
  type: destinationVisibility === "public" ? "pub" : "priv"
9492
9516
  });
9517
+ const slackActionToken = readSlackActionToken(message);
9493
9518
  const runId = getRunId(thread, message);
9494
9519
  const conversationId = threadId ?? runId;
9495
9520
  await withSpan(
@@ -10082,6 +10107,7 @@ function createReplyToThread(deps) {
10082
10107
  slackConversation,
10083
10108
  source,
10084
10109
  destination,
10110
+ destinationVisibility,
10085
10111
  surface: "slack",
10086
10112
  correlation: {
10087
10113
  conversationId,
@@ -10095,7 +10121,8 @@ function createReplyToThread(deps) {
10095
10121
  channelName,
10096
10122
  actorId: slackActorId
10097
10123
  },
10098
- toolChannelId
10124
+ toolChannelId,
10125
+ slackActionToken
10099
10126
  },
10100
10127
  policy: {
10101
10128
  configuration: preparedState.configuration,
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import type { Destination, Source, SystemActor } from "@sentry/junior-plugin-api";
11
11
  import type { ChannelConfigurationService } from "@/chat/configuration/types";
12
+ import type { ConversationPrivacy } from "@/chat/conversation-privacy";
12
13
  import type { CredentialContext } from "@/chat/credentials/context";
13
14
  import type { PiMessage } from "@/chat/pi/messages";
14
15
  import { type Actor } from "@/chat/actor";
@@ -22,6 +23,7 @@ import type { ConversationPendingAuthState } from "@/chat/state/conversation";
22
23
  import type { PiMessageProvenance } from "@/chat/state/session-log";
23
24
  import type { AgentTurnSurface } from "@/chat/state/turn-session";
24
25
  import type { ToolExecutionReport } from "@/chat/tool-support/tool-execution-report";
26
+ import type { SlackActionToken } from "@/chat/slack/action-token";
25
27
  import type { TurnReasoningLevel } from "@/chat/reasoning-level";
26
28
  import type { ImageGenerateToolDeps, WebFetchToolDeps, WebSearchToolDeps } from "@/chat/tools/types";
27
29
  export interface AgentRunAttachment {
@@ -62,7 +64,14 @@ export interface AgentRunRouting {
62
64
  actor?: Actor;
63
65
  source: Source;
64
66
  slackConversation?: SlackConversationContext;
67
+ /**
68
+ * TODO: Move ephemeral Slack credentials into provider-owned turn context so
69
+ * the Slack tool registry can consume them without extending core routing.
70
+ */
71
+ slackActionToken?: SlackActionToken;
65
72
  destination: Destination;
73
+ /** Confirmed visibility of the destination where this run is delivered. */
74
+ destinationVisibility?: ConversationPrivacy;
66
75
  surface?: AgentTurnSurface;
67
76
  dispatch?: {
68
77
  actor?: SystemActor;
@@ -1,4 +1,4 @@
1
- import type { DispatchOptions, Source, SlackDestination } from "@sentry/junior-plugin-api";
1
+ import type { DispatchOptions, DestinationVisibility, Source, SlackDestination } from "@sentry/junior-plugin-api";
2
2
  import type { CredentialSubject, CredentialSystemActor } from "@/chat/credentials/context";
3
3
  export type DispatchStatus = "pending" | "running" | "awaiting_resume" | "completed" | "failed" | "blocked";
4
4
  export type SlackDispatchOptions = Omit<DispatchOptions, "destination"> & {
@@ -13,6 +13,7 @@ export interface DispatchRecord {
13
13
  createdAtMs: number;
14
14
  credentialSubject?: CredentialSubject;
15
15
  destination: SlackDestination;
16
+ destinationVisibility: DestinationVisibility;
16
17
  errorMessage?: string;
17
18
  id: string;
18
19
  idempotencyKey: string;
@@ -3,5 +3,3 @@ export declare const NO_REPLY_MARKER = "[[NO_REPLY]]";
3
3
  export declare function isNoReplyMarker(text: string): boolean;
4
4
  /** Detect marker leaks before publication strips or rejects them. */
5
5
  export declare function containsNoReplyMarker(text: string): boolean;
6
- /** Remove the reserved marker from mixed assistant text so it is never shown to users. */
7
- export declare function stripNoReplyMarker(text: string): string;
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ declare const slackActionTokenSchema: z.core.$ZodBranded<z.ZodString, "SlackActionToken", "out">;
3
+ export type SlackActionToken = z.output<typeof slackActionTokenSchema>;
4
+ /** Parse the ephemeral search token from an untrusted Slack message envelope. */
5
+ export declare function readSlackActionToken(message: unknown): SlackActionToken | undefined;
6
+ export {};
@@ -0,0 +1,74 @@
1
+ import type { SlackActionToken } from "@/chat/slack/action-token";
2
+ /** Create an interactive, public-channel-only Slack search tool. */
3
+ export declare function createSlackPublicSearchTool(actionToken: SlackActionToken): Omit<import("../../tools/definition").AnyToolDefinition, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
4
+ inputSchema: import("../../tools/definition").JsonSchemaObject;
5
+ outputSchema: import("../../tools/definition").JsonSchemaObject;
6
+ prepareArguments(args: unknown): {
7
+ query: string;
8
+ after?: number | undefined;
9
+ before?: number | undefined;
10
+ cursor?: string | undefined;
11
+ limit?: number | undefined;
12
+ sort?: "timestamp" | "score" | undefined;
13
+ sort_dir?: "asc" | "desc" | 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
+ messages: {
22
+ channel_id: string;
23
+ message_ts: string;
24
+ content: string;
25
+ permalink: string;
26
+ author_name?: string | undefined;
27
+ author_user_id?: string | undefined;
28
+ channel_name?: string | undefined;
29
+ is_author_bot?: boolean | undefined;
30
+ }[];
31
+ target?: string | undefined;
32
+ data?: unknown;
33
+ truncated?: boolean | undefined;
34
+ continuation?: {
35
+ arguments: Record<string, unknown>;
36
+ reason?: string | undefined;
37
+ } | undefined;
38
+ error?: string | {
39
+ kind: string;
40
+ message: string;
41
+ retryable?: boolean | undefined;
42
+ } | undefined;
43
+ next_cursor?: string | undefined;
44
+ } | Promise<{
45
+ [x: string]: unknown;
46
+ ok: boolean;
47
+ status: "error" | "success";
48
+ query: string;
49
+ count: number;
50
+ messages: {
51
+ channel_id: string;
52
+ message_ts: string;
53
+ content: string;
54
+ permalink: string;
55
+ author_name?: string | undefined;
56
+ author_user_id?: string | undefined;
57
+ channel_name?: string | undefined;
58
+ is_author_bot?: boolean | undefined;
59
+ }[];
60
+ target?: string | undefined;
61
+ data?: unknown;
62
+ truncated?: boolean | undefined;
63
+ continuation?: {
64
+ arguments: Record<string, unknown>;
65
+ reason?: string | undefined;
66
+ } | undefined;
67
+ error?: string | {
68
+ kind: string;
69
+ message: string;
70
+ retryable?: boolean | undefined;
71
+ } | undefined;
72
+ next_cursor?: string | undefined;
73
+ }>) | undefined;
74
+ };
@@ -89,6 +89,8 @@ export interface AnyToolDefinition {
89
89
  execute?(input: unknown, options: ToolExecuteOptions): Promise<unknown> | unknown;
90
90
  prepareArguments?(args: unknown): unknown;
91
91
  }
92
+ /** Name-indexed heterogeneous tool definitions accepted by the agent runtime. */
93
+ export type ToolRegistry = Record<string, AnyToolDefinition>;
92
94
  /** Distinguish legacy TypeBox schemas from JSON Schema projected from Zod. */
93
95
  export declare function isTypeBoxInputSchema(schema: ToolInputSchema): schema is TSchema;
94
96
  /** Infer execute parameter types from the inputSchema via generic binding. */
@@ -1,6 +1,6 @@
1
1
  import type { SkillMetadata } from "@/chat/skills";
2
- import type { AnyToolDefinition } from "@/chat/tools/definition";
2
+ import type { ToolRegistry } from "@/chat/tools/definition";
3
3
  import type { ToolHooks, ToolRuntimeContext } from "@/chat/tools/types";
4
4
  export type { ToolHooks, ToolRuntimeContext };
5
5
  /** Build the model-facing tool registry from runtime-owned context and capabilities. */
6
- export declare function createTools(availableSkills: SkillMetadata[], hooks: ToolHooks | undefined, context: ToolRuntimeContext): Record<string, AnyToolDefinition>;
6
+ export declare function createTools(availableSkills: SkillMetadata[], hooks: ToolHooks | undefined, context: ToolRuntimeContext): ToolRegistry;
@@ -8,6 +8,7 @@ import type { Skill } from "@/chat/skills";
8
8
  import type { LoadSkillMetadata } from "@/chat/tools/skill/load-skill";
9
9
  import type { JuniorToolResult } from "@/chat/tool-support/structured-result";
10
10
  import type { LocalActor, Actor, SlackActor } from "@/chat/actor";
11
+ import type { SlackActionToken } from "@/chat/slack/action-token";
11
12
  import type { ModelProfile } from "@/chat/model-profile";
12
13
  interface HandoffControl {
13
14
  /** Non-empty catalog with the default target first. */
@@ -77,12 +78,14 @@ interface SlackToolRuntimeContext extends BaseToolRuntimeContext {
77
78
  destination: SlackDestination;
78
79
  actor?: SlackActor;
79
80
  source: SlackSource;
81
+ slackActionToken?: SlackActionToken;
80
82
  }
81
83
  interface LocalToolRuntimeContext extends BaseToolRuntimeContext {
82
84
  destination: LocalDestination;
83
85
  actor?: LocalActor;
84
86
  source: LocalSource;
85
87
  slack?: never;
88
+ slackActionToken?: never;
86
89
  }
87
90
  export type ToolRuntimeContext = LocalToolRuntimeContext | SlackToolRuntimeContext;
88
91
  export interface ToolState {
@@ -225,12 +225,19 @@ var conversationStatsItemSchema = z2.object({
225
225
  costUsd: z2.number().optional(),
226
226
  tokens: z2.number().optional()
227
227
  }).strict();
228
+ var conversationMetricDaySchema = z2.object({
229
+ costUsd: z2.number().optional(),
230
+ date: z2.string(),
231
+ durationMs: z2.number(),
232
+ tokens: z2.number().optional()
233
+ }).strict();
228
234
  var conversationStatsReportSchema = z2.object({
229
235
  active: z2.number(),
230
236
  conversations: z2.number(),
231
237
  durationMs: z2.number(),
232
238
  failed: z2.number(),
233
239
  generatedAt: z2.string(),
240
+ metricDays: z2.array(conversationMetricDaySchema),
234
241
  locations: z2.array(conversationStatsItemSchema),
235
242
  actors: z2.array(conversationStatsItemSchema),
236
243
  source: z2.literal("conversation_index"),