@sentry/junior 0.204.0 → 0.206.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/{agent-CMyTD4DD.js → agent-C9yuiZUF.js} +277 -85
  2. package/dist/{agent-hooks-Bbcp9e33.js → agent-hooks-DNjlz2A-.js} +1084 -16
  3. package/dist/{agent-runner-DNb1A49M.js → agent-runner-E67lFrDG.js} +5 -5
  4. package/dist/api/schema.d.ts +85 -0
  5. package/dist/api/schema.js +2 -2
  6. package/dist/api.js +44 -511
  7. package/dist/{app-B1TOqBI8.d.ts → app-AxGmJHwK.d.ts} +8 -0
  8. package/dist/app.d.ts +1 -1
  9. package/dist/app.js +56 -84
  10. package/dist/{auth-pause-state-DosEalIG.js → auth-pause-state-MVZ6xvEK.js} +6 -6
  11. package/dist/briefs-CvJFQvhJ.js +398 -0
  12. package/dist/{catalog-runtime-BReos-D4.js → catalog-runtime-TBR8n4e1.js} +1 -1
  13. package/dist/{checkpoint-CNrPyRJN.js → checkpoint-BY47jJCB.js} +7 -6
  14. package/dist/cli/chat.js +14 -12
  15. package/dist/cli/check.js +3 -3
  16. package/dist/cli/init.js +1 -0
  17. package/dist/cli/main.js +5 -1
  18. package/dist/cli/plugins.js +3 -3
  19. package/dist/cli/run.d.ts +2 -1
  20. package/dist/cli/run.js +2 -1
  21. package/dist/cli/snapshot-create.js +3 -3
  22. package/dist/cli/upgrade.js +3 -3
  23. package/dist/{config-nGCW1OTG.js → client-ceE7NtvC.js} +4 -367
  24. package/dist/config-CIE83sR2.js +386 -0
  25. package/dist/{schema-Rn8-XEaN.js → conversation-AA4R1VUn.js} +580 -558
  26. package/dist/{conversation-privacy-B4voFRSm.js → conversation-privacy-Auyq26XQ.js} +1 -1
  27. package/dist/{credential-sync-kXr1hvO1.js → credential-sync-BO33aNQd.js} +22 -20
  28. package/dist/{execution-stats-DWfSihcH.js → execution-stats-SiS2FLcv.js} +4 -4
  29. package/dist/{executor-DcMSlI5c.js → executor-BfbYPW63.js} +31 -3
  30. package/dist/instrumentation.js +2 -2
  31. package/dist/{job-queue-Drn_LXlM.js → job-queue-CyTc24Es.js} +1 -1
  32. package/dist/{logging-BzAk1S_s.js → logging-D37bayDc.js} +1 -1
  33. package/dist/nitro.d.ts +1 -1
  34. package/dist/nitro.js +3 -3
  35. package/dist/{oauth-callback-server-JiftokvA.js → oauth-callback-server-BtfOeHTZ.js} +2 -2
  36. package/dist/{pending-auth-CWmSdZVg.js → pending-auth-BPUzcs7n.js} +2 -2
  37. package/dist/{profile-CrT5nXbw.js → profile-CSixooTi.js} +4 -3
  38. package/dist/prompt-3gqNY1Ka.js +552 -0
  39. package/dist/{registry-DH1xUo2N.js → registry-DiR0-xRg.js} +1 -1
  40. package/dist/{resolve-CZHnfxaS.js → resolve-Bn5q__9b.js} +2 -2
  41. package/dist/{runner-CZV2sC46.js → runner-DFx3dIJH.js} +9 -9
  42. package/dist/{sandbox-egress-signals-bHZfmhbd.js → sandbox-egress-signals-bwvjihxA.js} +1 -1
  43. package/dist/schema-8GXFfclK.js +559 -0
  44. package/dist/{skills-CIcDQSgM.js → skills-BhM5qyCF.js} +2 -2
  45. package/dist/{spawn-DdkUFRmq.js → spawn-Dk0PqdH6.js} +2 -2
  46. package/dist/{stats-DTUbAb88.js → stats-DKRP4rsY.js} +26 -12
  47. package/dist/{task-queue-CJIR4RF7.js → task-queue-CdpTvu4R.js} +3 -3
  48. package/dist/{task-runner-D-O3A-Cp.js → task-runner-alpjVSxQ.js} +10 -10
  49. package/dist/{thread-state-CRc9jTJS.js → thread-state-CRK7aA41.js} +5 -5
  50. package/dist/{turn-execution-BkinIdJF.js → turn-execution-XtyzIHog.js} +3 -3
  51. package/dist/{turn-lifecycle-DQV19VTW.js → turn-lifecycle-VbwZLfBv.js} +1 -1
  52. package/dist/{usage-D3EbSO7w.js → usage-D4A48Bdl.js} +4 -125
  53. package/dist/{validation-C2CR-OX3.js → validation-jn_46o4U.js} +1 -1
  54. package/dist/{version-DxcWdoKa.js → version-DcfJxB4z.js} +1 -1
  55. package/dist/version.js +1 -1
  56. package/dist/web-authorization-CsfLSfEs.js +38 -0
  57. package/dist/{web-input-3EPfpf55.js → web-input-BPp_sc7t.js} +6 -6
  58. package/dist/{work-fGofI7Op.js → work-Di-ubSGg.js} +9 -9
  59. package/dist/{worker-CsOLq-mR.js → worker-Cu_D5Mr8.js} +5 -4
  60. package/migrations/0040_conversation_briefs.sql +17 -0
  61. package/migrations/meta/0040_snapshot.json +3516 -0
  62. package/migrations/meta/_journal.json +7 -0
  63. package/package.json +4 -4
  64. package/dist/history-CqTuSAdI.js +0 -441
  65. package/dist/usage-schema-CKaDkH_j.js +0 -20
  66. package/dist/web-authorization-RMBxAR_t.js +0 -76
@@ -1,17 +1,21 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
- import { _ as completeObject, i as getChatConfig, n as botConfig, o as getSlackBotToken, y as embedTexts } from "./config-nGCW1OTG.js";
3
- import { C as withSpan, P as normalizeIdentityEmail, _ as setSpanAttributes, d as logInfo, f as logWarn, u as logException, v as setSpanStatus } from "./logging-BzAk1S_s.js";
2
+ import { a as embedTexts, r as completeObject } from "./client-ceE7NtvC.js";
3
+ import { C as withSpan, P as normalizeIdentityEmail, _ as setSpanAttributes, d as logInfo, f as logWarn, u as logException, v as setSpanStatus } from "./logging-D37bayDc.js";
4
+ import { a as getClient$1 } from "./sentry-CD3SdJl-.js";
5
+ import { a as getChatConfig, n as botConfig, s as getSlackBotToken } from "./config-CIE83sR2.js";
4
6
  import { n as defaultModelId } from "./model-profile-CZmVoGdX.js";
5
7
  import { _ as parseSlackMessageTs, c as isActorUserId, h as parseSlackTeamId, m as parseSlackChannelReferenceId, u as parseActorUserId } from "./context-B45_WqVX.js";
6
8
  import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
7
- import { C as juniorConversations, S as conversationReadColumns, T as juniorUsers, b as juniorCodeRepositories, g as juniorConversationEvents, h as juniorConversationBindings, i as juniorWorkspaces, m as juniorConversationParticipants, n as juniorSnapshots, r as juniorWorkspaceRepos, t as createJuniorSqlExecutor, v as juniorConversationAnnotations, w as juniorIdentities, y as juniorCodeChanges } from "./executor-DcMSlI5c.js";
8
- import { c as newConversationEventSchema, o as decodeStoredConversationEvent, r as conversationEventDataSchema, s as historyReplacementSchema } from "./history-CqTuSAdI.js";
9
- import { A as sleep$1, P as SANDBOX_WORKSPACE_ROOT, l as isSandboxMissingError, n as hash, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-CrT5nXbw.js";
9
+ import { C as conversationReadColumns, E as juniorUsers, T as juniorIdentities, _ as juniorConversationEvents, b as juniorCodeChanges, g as juniorConversationBriefs, h as juniorConversationBindings, i as juniorWorkspaces, m as juniorConversationParticipants, n as juniorSnapshots, r as juniorWorkspaceRepos, t as createJuniorSqlExecutor, w as juniorConversations, x as juniorCodeRepositories, y as juniorConversationAnnotations } from "./executor-BfbYPW63.js";
10
+ import { B as decodeStoredConversationEvent, C as conversationReportEventSchema, H as newConversationEventSchema, L as conversationEventDataSchema, M as conversationBriefSchema, N as parseBriefInput, V as historyReplacementSchema } from "./conversation-AA4R1VUn.js";
11
+ import { A as sleep$1, P as SANDBOX_WORKSPACE_ROOT, l as isSandboxMissingError, n as hash, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand } from "./profile-CSixooTi.js";
12
+ import { n as briefEntriesFromReportEvents, s as generateBrief, t as BRIEF_PROMPT } from "./prompt-3gqNY1Ka.js";
10
13
  import { AsyncLocalStorage } from "node:async_hooks";
11
14
  import { z } from "zod";
12
15
  import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
13
- import { and, asc, desc, eq, gt, gte, inArray, isNull, lt, lte, ne, or, sql } from "drizzle-orm";
14
- import { codeChangeInputSchema, conversationAnnotationInputSchema, destinationSchema, identitySchema, locationSchema, missingToolAnnotationKeys, normalizeResourceEventIdentifier, pluginResourceEventsSchema, promptContextSchema, promptMessageSchema, resourceEventInputSchema, resourceEventMatchSchema, resourceEventMatches, resourceEventTypeSchema, sourceSchema, stableResourceEventMatchKey, userSchema } from "@sentry/junior-plugin-api";
16
+ import { and, arrayContains, asc, desc, eq, getTableColumns, gt, gte, inArray, isNotNull, isNull, lt, lte, max, ne, or, sql } from "drizzle-orm";
17
+ import { codeChangeInputSchema, conversationAnnotationInputSchema, conversationEventPresentationSchema, defineConversationEvent, destinationSchema, identitySchema, locationSchema, missingToolAnnotationKeys, normalizeResourceEventIdentifier, pluginResourceEventsSchema, promptContextSchema, promptMessageSchema, resourceEventInputSchema, resourceEventMatchSchema, resourceEventMatches, resourceEventTypeSchema, sourceSchema, stableResourceEventMatchKey, userSchema } from "@sentry/junior-plugin-api";
18
+ import { alias } from "drizzle-orm/pg-core";
15
19
  import { WebClient } from "@slack/web-api";
16
20
  import { Sandbox } from "@vercel/sandbox";
17
21
  //#region src/chat/destination.ts
@@ -330,19 +334,21 @@ function locationForWrite(args) {
330
334
  const source = args.sessionSource?.kind === "slack" ? args.sessionSource : void 0;
331
335
  if (location) {
332
336
  if (destination && (location.id !== args.destinationId || location.teamId !== destination.teamId || location.channelId !== destination.channelId) || source && (location.teamId !== source.teamId || location.channelId !== source.channelId)) throw new Error("Conversation Location changed");
333
- return source?.threadTs && !location.threadTs ? {
337
+ const threadTs = location.threadTs ?? destination?.threadTs ?? source?.threadTs;
338
+ return threadTs === location.threadTs ? location : {
334
339
  ...location,
335
- threadTs: source.threadTs
336
- } : location;
340
+ threadTs
341
+ };
337
342
  }
338
343
  if (destination && source && (destination.teamId !== source.teamId || destination.channelId !== source.channelId)) throw new Error("Conversation Location does not match its session Source");
339
344
  if (!destination || !args.destinationId) return;
345
+ const threadTs = destination.threadTs ?? source?.threadTs;
340
346
  return locationSchema.parse({
341
347
  id: args.destinationId,
342
348
  provider: "slack",
343
349
  teamId: destination.teamId,
344
350
  channelId: destination.channelId,
345
- ...source?.threadTs ? { threadTs: source.threadTs } : void 0
351
+ ...threadTs ? { threadTs } : void 0
346
352
  });
347
353
  }
348
354
  /** Read one complete Location from SQL. */
@@ -1356,6 +1362,126 @@ async function closeDb() {
1356
1362
  await previous?.db.close();
1357
1363
  }
1358
1364
  //#endregion
1365
+ //#region src/chat/conversations/structured-events.ts
1366
+ /** Reserved namespace for Junior-owned conversation metadata events. */
1367
+ const JUNIOR_NATIVE_EVENT_NAMESPACE = "junior";
1368
+ const authenticationEventContentSchema = z.object({
1369
+ accountLabel: z.string().trim().min(1).max(120).optional(),
1370
+ actorId: z.string().min(1),
1371
+ authorizationId: z.string().min(1).optional(),
1372
+ provider: z.string().regex(/^[a-z][a-z0-9-]*$/),
1373
+ providerLabel: z.string().trim().min(1).max(80).optional()
1374
+ }).strict();
1375
+ const agentsInstructionsUpdatedContentSchema = z.object({
1376
+ action: z.enum([
1377
+ "loaded",
1378
+ "replaced",
1379
+ "cleared"
1380
+ ]),
1381
+ directory: z.string().trim().min(1).max(500).optional(),
1382
+ fingerprint: z.string().trim().min(1).max(128),
1383
+ sources: z.array(z.object({
1384
+ content: z.string().min(1).max(32768),
1385
+ path: z.string().trim().min(1).max(500)
1386
+ }).strict()).max(32),
1387
+ textBytes: z.number().int().nonnegative().optional()
1388
+ }).strict();
1389
+ function providerTitle(content) {
1390
+ return content.providerLabel?.trim() || content.provider;
1391
+ }
1392
+ function agentsInstructionsTitle(action) {
1393
+ if (action === "loaded") return "Loaded AGENTS.md";
1394
+ if (action === "replaced") return "Updated AGENTS.md";
1395
+ return "Cleared AGENTS.md";
1396
+ }
1397
+ function sourceFilename(sourcePath) {
1398
+ return sourcePath.split("/").at(-1) || sourcePath;
1399
+ }
1400
+ function formatBytes(bytes) {
1401
+ if (bytes < 1024) return `${bytes} bytes`;
1402
+ const kilobytes = bytes / 1024;
1403
+ return `${Number.isInteger(kilobytes) ? kilobytes : kilobytes.toFixed(1)} KB`;
1404
+ }
1405
+ function agentsInstructionsPreview(content) {
1406
+ const source = content.sources[0];
1407
+ if (!source) return void 0;
1408
+ const filename = sourceFilename(source.path);
1409
+ const label = content.sources.length === 1 ? filename : `${content.sources.length} AGENTS.md files`;
1410
+ return typeof content.textBytes === "number" ? `${label} · ${formatBytes(content.textBytes)}` : label;
1411
+ }
1412
+ /** Junior-owned account-link transcript event. */
1413
+ const authenticationLinkedEvent = defineConversationEvent({
1414
+ name: "authentication_linked",
1415
+ version: 1,
1416
+ schema: authenticationEventContentSchema,
1417
+ renderEvent(event) {
1418
+ const provider = providerTitle(event);
1419
+ return {
1420
+ icon: "link",
1421
+ title: `${provider} connected`,
1422
+ ...event.accountLabel ? { preview: `Connected as \`${event.accountLabel}\`` } : void 0,
1423
+ details: [{
1424
+ title: `${provider} connected`,
1425
+ ...event.accountLabel ? { description: `Connected as \`${event.accountLabel}\`` } : void 0,
1426
+ metadata: [event.provider]
1427
+ }]
1428
+ };
1429
+ }
1430
+ });
1431
+ /** Junior-owned account-unlink transcript event. */
1432
+ const authenticationUnlinkedEvent = defineConversationEvent({
1433
+ name: "authentication_unlinked",
1434
+ version: 1,
1435
+ schema: authenticationEventContentSchema,
1436
+ renderEvent(event) {
1437
+ const provider = providerTitle(event);
1438
+ return {
1439
+ icon: "key",
1440
+ title: `${provider} disconnected`,
1441
+ details: [{
1442
+ title: `${provider} disconnected`,
1443
+ metadata: [event.provider]
1444
+ }]
1445
+ };
1446
+ }
1447
+ });
1448
+ /** Junior-owned AGENTS.md bootstrap transition for the dashboard timeline. */
1449
+ const agentsInstructionsUpdatedEvent = defineConversationEvent({
1450
+ name: "agents_instructions_updated",
1451
+ version: 1,
1452
+ schema: agentsInstructionsUpdatedContentSchema,
1453
+ renderEvent(event) {
1454
+ const title = agentsInstructionsTitle(event.action);
1455
+ const preview = agentsInstructionsPreview(event);
1456
+ return {
1457
+ icon: "brain",
1458
+ title,
1459
+ ...preview ? { preview } : void 0,
1460
+ ...event.sources.length > 0 ? { details: event.sources.map((source) => ({
1461
+ title: sourceFilename(source.path),
1462
+ content: source.content
1463
+ })) } : void 0
1464
+ };
1465
+ }
1466
+ });
1467
+ const NATIVE_EVENT_DEFINITIONS = [
1468
+ authenticationLinkedEvent,
1469
+ authenticationUnlinkedEvent,
1470
+ agentsInstructionsUpdatedEvent
1471
+ ];
1472
+ /** Resolve a registered Junior-native conversation event definition. */
1473
+ function getJuniorNativeEventDefinition(args) {
1474
+ return NATIVE_EVENT_DEFINITIONS.find((definition) => definition.eventName === args.name && definition.version === args.version);
1475
+ }
1476
+ /** Render one Junior-native event through its active definition. */
1477
+ function renderJuniorNativeConversationEvent(args) {
1478
+ if (args.namespace !== "junior") return void 0;
1479
+ const definition = getJuniorNativeEventDefinition(args);
1480
+ if (!definition) return void 0;
1481
+ const presentation = definition.renderEvent(definition.parse(args.content));
1482
+ return presentation === void 0 ? void 0 : conversationEventPresentationSchema.parse(presentation);
1483
+ }
1484
+ //#endregion
1359
1485
  //#region src/chat/plugins/annotations.ts
1360
1486
  function annotationFromRow(row) {
1361
1487
  const parsed = conversationAnnotationInputSchema.safeParse(row.annotation);
@@ -1420,6 +1546,926 @@ async function listConversationAnnotationsById(db, conversationIds) {
1420
1546
  return byConversation;
1421
1547
  }
1422
1548
  //#endregion
1549
+ //#region src/chat/conversations/sql/privacy.ts
1550
+ const privacyRoot = alias(juniorConversations, "privacy_root");
1551
+ /** Accept only a structurally valid persisted root as privacy authority. */
1552
+ async function readRootCandidate(executor, conversationId) {
1553
+ const row = (await executor.db().select({
1554
+ requestedConversationId: juniorConversations.conversationId,
1555
+ requestedParentConversationId: juniorConversations.parentConversationId,
1556
+ requestedRootConversationId: juniorConversations.rootConversationId,
1557
+ rootConversationId: privacyRoot.conversationId,
1558
+ rootDestinationId: privacyRoot.destinationId,
1559
+ rootParentConversationId: privacyRoot.parentConversationId,
1560
+ rootRootConversationId: privacyRoot.rootConversationId
1561
+ }).from(juniorConversations).leftJoin(privacyRoot, eq(privacyRoot.conversationId, juniorConversations.rootConversationId)).where(eq(juniorConversations.conversationId, conversationId)))[0];
1562
+ return row?.requestedRootConversationId !== null && (row?.requestedParentConversationId !== null || row?.requestedRootConversationId === row?.requestedConversationId) && row?.rootConversationId === row.requestedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && row.rootDestinationId !== null ? {
1563
+ destinationId: row.rootDestinationId,
1564
+ rootConversationId: row.rootConversationId
1565
+ } : void 0;
1566
+ }
1567
+ async function readCandidateVisibility(executor, candidate) {
1568
+ const destinations = await executor.db().select({ visibility: juniorDestinations.visibility }).from(juniorDestinations).where(eq(juniorDestinations.id, candidate.destinationId)).for("share");
1569
+ return {
1570
+ rootConversationId: candidate.rootConversationId,
1571
+ visibility: destinations[0]?.visibility ?? null
1572
+ };
1573
+ }
1574
+ /**
1575
+ * Resolve a conversation's privacy authority from its persisted root.
1576
+ *
1577
+ * Missing or structurally invalid root metadata fails closed. The root
1578
+ * destination is locked so callers that keep the transaction open receive a
1579
+ * stable visibility decision.
1580
+ */
1581
+ async function resolveRootVisibility(executor, conversationId) {
1582
+ const candidate = await readRootCandidate(executor, conversationId);
1583
+ if (!candidate) return {
1584
+ rootConversationId: conversationId,
1585
+ visibility: null
1586
+ };
1587
+ return readCandidateVisibility(executor, candidate);
1588
+ }
1589
+ //#endregion
1590
+ //#region src/chat/briefs/events.ts
1591
+ /** Durable cost and size record for one stored Brief version. */
1592
+ const briefUpdatedEvent = defineConversationEvent({
1593
+ name: "brief_updated",
1594
+ version: 1,
1595
+ schema: z.object({
1596
+ version: z.number().int().positive(),
1597
+ modelId: z.string().min(1),
1598
+ costUsd: z.number().finite().nonnegative().optional(),
1599
+ decisions: z.number().int().nonnegative(),
1600
+ openDecisions: z.number().int().nonnegative(),
1601
+ links: z.number().int().nonnegative()
1602
+ }).strict(),
1603
+ renderEvent(event) {
1604
+ return {
1605
+ icon: "activity",
1606
+ title: `Brief updated (v${event.version})`
1607
+ };
1608
+ }
1609
+ });
1610
+ //#endregion
1611
+ //#region src/chat/sentry-links.ts
1612
+ function getSentryOrgSlug() {
1613
+ return process.env.SENTRY_ORG_SLUG?.trim() || void 0;
1614
+ }
1615
+ function isSentrySaasDsnHost(host) {
1616
+ return host === "sentry.io" || host.endsWith(".sentry.io");
1617
+ }
1618
+ function buildSentryWebBaseUrl(dsn) {
1619
+ if (isSentrySaasDsnHost(dsn.host)) return "https://sentry.io";
1620
+ const port = dsn.port ? `:${dsn.port}` : "";
1621
+ const path = dsn.path ? `/${dsn.path}` : "";
1622
+ return `${dsn.protocol}://${dsn.host}${port}${path}`;
1623
+ }
1624
+ function buildSentryOrgPath(args) {
1625
+ const dsn = getClient$1()?.getDsn();
1626
+ if (!dsn?.host || !dsn.projectId) return;
1627
+ const orgSlug = getSentryOrgSlug();
1628
+ if (!orgSlug) return;
1629
+ const params = new URLSearchParams();
1630
+ params.set("project", dsn.projectId);
1631
+ if (args.query) for (const [key, value] of Object.entries(args.query)) params.set(key, value);
1632
+ const path = `${args.path}?${params.toString()}`;
1633
+ if (isSentrySaasDsnHost(dsn.host)) return `https://${orgSlug}.sentry.io/${path}`;
1634
+ return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path}`;
1635
+ }
1636
+ /** Build a Sentry conversation URL only when the runtime has enough Sentry config. */
1637
+ function buildSentryConversationUrl(conversationId) {
1638
+ return buildSentryOrgPath({ path: `explore/conversations/${encodeURIComponent(conversationId)}/` });
1639
+ }
1640
+ /** Build a Sentry event URL only when the runtime has enough Sentry config. */
1641
+ function buildSentryEventUrl(eventId) {
1642
+ return buildSentryOrgPath({
1643
+ path: "issues/",
1644
+ query: { query: eventId }
1645
+ });
1646
+ }
1647
+ //#endregion
1648
+ //#region src/api/conversations/events.ts
1649
+ /** Canonical event types that can contribute to the reporting projection. */
1650
+ const conversationReportSourceEventTypes = [
1651
+ "message",
1652
+ "message_handled",
1653
+ "assistant_message",
1654
+ "tool_result",
1655
+ "tool_execution_started",
1656
+ "guardian_action_reviewed",
1657
+ "turn_started",
1658
+ "turn_context",
1659
+ "structured_event",
1660
+ "attachments_delivered",
1661
+ "turn_routed",
1662
+ "turn_completed",
1663
+ "turn_failed",
1664
+ "compaction",
1665
+ "handoff",
1666
+ "subagent_started",
1667
+ "subagent_ended"
1668
+ ];
1669
+ const reportingAssistantMessageSchema = z.object({
1670
+ type: z.literal("assistant_message"),
1671
+ content: z.array(z.unknown())
1672
+ }).passthrough();
1673
+ const reportingToolCallPartSchema = z.object({
1674
+ type: z.literal("toolCall"),
1675
+ id: z.string().min(1),
1676
+ name: z.string().min(1),
1677
+ arguments: z.unknown()
1678
+ }).passthrough();
1679
+ const reportingToolResultMessageSchema = z.object({
1680
+ type: z.literal("tool_result"),
1681
+ toolCallId: z.string().min(1),
1682
+ toolName: z.string().min(1).optional(),
1683
+ name: z.string().min(1).optional(),
1684
+ content: z.array(z.unknown()),
1685
+ details: z.unknown().optional(),
1686
+ isError: z.boolean().optional()
1687
+ }).passthrough();
1688
+ const reportingToolResultDetailsSchema = z.object({
1689
+ ok: z.boolean().optional(),
1690
+ status: z.string().optional()
1691
+ }).passthrough();
1692
+ const reportingTextPartSchema = z.object({
1693
+ type: z.literal("text"),
1694
+ text: z.string()
1695
+ }).passthrough();
1696
+ const reportingReasoningPartSchema = z.object({
1697
+ type: z.literal("thinking"),
1698
+ thinking: z.string(),
1699
+ redacted: z.literal(true).optional()
1700
+ }).passthrough();
1701
+ const reportingMediaPartSchema = z.object({
1702
+ type: z.enum(["image", "audio"]),
1703
+ mimeType: z.string().min(1).optional()
1704
+ }).passthrough();
1705
+ const reportingMessageAuthorSchema = z.object({
1706
+ fullName: z.string().min(1).optional(),
1707
+ userId: z.string().min(1).optional(),
1708
+ userName: z.string().min(1).optional()
1709
+ }).passthrough();
1710
+ /** Retain model-visible text and media descriptors, dropping all opaque fields. */
1711
+ function sanitizeModelContentPart(value) {
1712
+ const text = reportingTextPartSchema.safeParse(value);
1713
+ if (text.success) return {
1714
+ type: "text",
1715
+ text: text.data.text
1716
+ };
1717
+ const media = reportingMediaPartSchema.safeParse(value);
1718
+ if (!media.success) return void 0;
1719
+ return {
1720
+ type: media.data.type,
1721
+ ...media.data.mimeType ? { mimeType: media.data.mimeType } : void 0
1722
+ };
1723
+ }
1724
+ /** Convert allowlisted Pi content parts into the reporting result value. */
1725
+ function modelVisibleToolOutput(content) {
1726
+ const sanitized = content.map(sanitizeModelContentPart).filter((part) => part !== void 0);
1727
+ if (sanitized.length === 0) return void 0;
1728
+ if (sanitized.length !== 1) return sanitized;
1729
+ const only = sanitized[0];
1730
+ return only.type === "text" ? only.text : only;
1731
+ }
1732
+ function reportMessageActorIdentity(data) {
1733
+ if (data.role !== "user") return void 0;
1734
+ const author = reportingMessageAuthorSchema.safeParse(data.meta?.author);
1735
+ if (!author.success) return void 0;
1736
+ const actorIdentity = {
1737
+ ...author.data.fullName ? { fullName: author.data.fullName } : void 0,
1738
+ ...author.data.userId ? { slackUserId: author.data.userId } : void 0,
1739
+ ...author.data.userName ? { slackUserName: author.data.userName } : void 0
1740
+ };
1741
+ return Object.keys(actorIdentity).length > 0 ? actorIdentity : void 0;
1742
+ }
1743
+ /** Project native assistant tool calls through the existing reporting shape. */
1744
+ function reportToolCalls(args) {
1745
+ const message = reportingAssistantMessageSchema.safeParse(args.message);
1746
+ if (!message.success) return void 0;
1747
+ const calls = message.data.content.flatMap((part) => {
1748
+ const call = reportingToolCallPartSchema.safeParse(part);
1749
+ if (!call.success) return [];
1750
+ return [{
1751
+ toolCallId: call.data.id,
1752
+ name: call.data.name,
1753
+ status: "running",
1754
+ startedAt: new Date(args.createdAtMs).toISOString(),
1755
+ startedSeq: args.seq,
1756
+ ...args.canExposePayload && call.data.arguments !== void 0 ? { input: call.data.arguments } : void 0
1757
+ }];
1758
+ });
1759
+ return calls.length > 0 ? {
1760
+ type: "tool_calls",
1761
+ calls
1762
+ } : void 0;
1763
+ }
1764
+ /** Project assistant reasoning with tool ids retained only for display order. */
1765
+ function reportAssistantMessage(args) {
1766
+ const message = reportingAssistantMessageSchema.safeParse(args.message);
1767
+ if (!message.success) return void 0;
1768
+ if (!message.data.content.some((part) => {
1769
+ const reasoning = reportingReasoningPartSchema.safeParse(part);
1770
+ return reasoning.success && reasoning.data.redacted !== true && reasoning.data.thinking.trim().length > 0;
1771
+ })) return reportToolCalls(args);
1772
+ const reasoningParts = [];
1773
+ const assistantParts = [];
1774
+ for (const part of message.data.content) {
1775
+ const reasoning = reportingReasoningPartSchema.safeParse(part);
1776
+ if (reasoning.success) {
1777
+ if (reasoning.data.redacted !== true && reasoning.data.thinking.trim().length > 0) {
1778
+ const projected = {
1779
+ type: "reasoning",
1780
+ ...args.canExposePayload ? { text: reasoning.data.thinking } : { redacted: true }
1781
+ };
1782
+ reasoningParts.push(projected);
1783
+ assistantParts.push(projected);
1784
+ }
1785
+ continue;
1786
+ }
1787
+ const call = reportingToolCallPartSchema.safeParse(part);
1788
+ if (call.success) assistantParts.push({
1789
+ type: "tool_call",
1790
+ toolCallId: call.data.id
1791
+ });
1792
+ }
1793
+ const toolCalls = reportToolCalls(args);
1794
+ if (toolCalls?.type === "tool_calls") return {
1795
+ ...toolCalls,
1796
+ assistant: { parts: assistantParts }
1797
+ };
1798
+ return reasoningParts.length > 0 ? {
1799
+ type: "assistant_message",
1800
+ parts: reasoningParts
1801
+ } : void 0;
1802
+ }
1803
+ /** Project a native tool result without exposing host-only result details. */
1804
+ function reportToolResult(args) {
1805
+ const message = reportingToolResultMessageSchema.safeParse(args.message);
1806
+ if (!message.success) return void 0;
1807
+ const details = reportingToolResultDetailsSchema.safeParse(message.data.details);
1808
+ const outcome = message.data.isError === true || details.success && (details.data.ok === false || details.data.status === "error") ? "error" : "completed";
1809
+ const output = modelVisibleToolOutput(message.data.content);
1810
+ const name = message.data.toolName ?? message.data.name ?? args.start?.name;
1811
+ if (!name) return void 0;
1812
+ return {
1813
+ type: "tool_calls",
1814
+ calls: [{
1815
+ toolCallId: message.data.toolCallId,
1816
+ name,
1817
+ status: outcome,
1818
+ ...args.start ? {
1819
+ startedAt: new Date(args.start.createdAtMs).toISOString(),
1820
+ startedSeq: args.start.seq
1821
+ } : void 0,
1822
+ ...args.canExposePayload && output !== void 0 ? { output } : void 0
1823
+ }]
1824
+ };
1825
+ }
1826
+ /** Return tool result ids that may need start metadata from outside a page. */
1827
+ function conversationReportToolResultIds(events) {
1828
+ return [...new Set(events.flatMap((event) => {
1829
+ const result = reportingToolResultMessageSchema.safeParse(event.data);
1830
+ return result.success ? [result.data.toolCallId] : [];
1831
+ }))];
1832
+ }
1833
+ function reportEventData(args) {
1834
+ const { data } = args;
1835
+ if (data.type === "structured_event") {
1836
+ if (!args.canExposePayload) return void 0;
1837
+ const presentation = data.namespace === "junior" ? renderJuniorNativeConversationEvent(data) : renderPluginConversationEvent(data);
1838
+ if (!presentation) return void 0;
1839
+ return {
1840
+ type: "structured_event",
1841
+ namespace: data.namespace,
1842
+ name: data.name,
1843
+ version: data.version,
1844
+ ...data.turnId ? { turnId: data.turnId } : void 0,
1845
+ presentation
1846
+ };
1847
+ }
1848
+ switch (data.type) {
1849
+ case "message": {
1850
+ const actorIdentity = args.canExposePayload ? reportMessageActorIdentity(data) : void 0;
1851
+ return {
1852
+ type: "message",
1853
+ messageId: data.messageId,
1854
+ role: data.role,
1855
+ ...data.meta?.source === "web" || data.meta?.source === "slack" ? { source: data.meta.source } : void 0,
1856
+ ...actorIdentity ? { actorIdentity } : void 0,
1857
+ ...typeof data.meta?.eventType === "string" ? { eventType: data.meta.eventType } : void 0,
1858
+ ...typeof data.meta?.explicitMention === "boolean" ? { explicitMention: data.meta.explicitMention } : void 0,
1859
+ ...args.canExposePayload && typeof data.meta?.trustedSummary === "string" ? { trustedSummary: data.meta.trustedSummary } : void 0,
1860
+ ...args.canExposePayload ? { text: data.text } : { redacted: true }
1861
+ };
1862
+ }
1863
+ case "message_handled": return {
1864
+ type: "message_handled",
1865
+ messageId: data.messageId
1866
+ };
1867
+ case "turn_started": return {
1868
+ type: "turn_lifecycle",
1869
+ turnId: data.turnId,
1870
+ state: "started",
1871
+ ...data.inputMessageIds.length > 0 ? { inputMessageIds: data.inputMessageIds } : void 0
1872
+ };
1873
+ case "turn_context":
1874
+ if (!args.canExposePayload) return;
1875
+ return {
1876
+ type: "turn_context",
1877
+ turnId: data.turnId,
1878
+ pluginName: data.pluginName,
1879
+ kind: data.kind,
1880
+ version: data.version,
1881
+ content: data.content
1882
+ };
1883
+ case "attachments_delivered":
1884
+ if (!args.canExposePayload) return;
1885
+ return {
1886
+ type: "attachments_delivered",
1887
+ attachments: data.attachments
1888
+ };
1889
+ case "turn_routed": return {
1890
+ type: "turn_routed",
1891
+ turnId: data.turnId,
1892
+ modelProfile: data.modelProfile,
1893
+ modelId: data.modelId,
1894
+ ...data.costUsd !== void 0 ? { costUsd: data.costUsd } : void 0,
1895
+ reasoningLevel: data.reasoningLevel,
1896
+ ...data.confidence !== void 0 ? { confidence: data.confidence } : void 0,
1897
+ source: data.source
1898
+ };
1899
+ case "guardian_action_reviewed": return {
1900
+ type: "guardian_action_reviewed",
1901
+ turnId: data.turnId,
1902
+ toolCallId: data.toolCallId,
1903
+ toolName: data.toolName,
1904
+ decision: data.decision,
1905
+ riskLevel: data.riskLevel,
1906
+ userAuthorization: data.userAuthorization
1907
+ };
1908
+ case "turn_completed": return {
1909
+ type: "turn_lifecycle",
1910
+ turnId: data.turnId,
1911
+ state: data.outcome === "success" ? "succeeded" : "no_reply"
1912
+ };
1913
+ case "turn_failed": {
1914
+ const eventId = data.eventId;
1915
+ const sentryEventUrl = eventId ? buildSentryEventUrl(eventId) : void 0;
1916
+ return {
1917
+ type: "turn_lifecycle",
1918
+ turnId: data.turnId,
1919
+ state: "failed",
1920
+ failureCode: data.failureCode,
1921
+ ...data.failureReason ? { failureReason: data.failureReason } : void 0,
1922
+ ...eventId ? { eventId } : void 0,
1923
+ ...sentryEventUrl ? { sentryEventUrl } : void 0
1924
+ };
1925
+ }
1926
+ case "compaction": return {
1927
+ type: "compaction",
1928
+ modelProfile: data.modelProfile,
1929
+ modelId: data.modelId,
1930
+ ...args.canExposePayload && data.summary ? { summary: data.summary } : void 0,
1931
+ ...data.details ? { details: data.details } : void 0
1932
+ };
1933
+ default: return;
1934
+ }
1935
+ }
1936
+ /**
1937
+ * Project canonical events into the ordered reporting boundary.
1938
+ *
1939
+ * `canExposePayload` must come from the authorized conversation-detail policy;
1940
+ * this projection never derives visibility or authorization from event data.
1941
+ */
1942
+ function projectConversationReportEventPage(args) {
1943
+ const subagentStarts = /* @__PURE__ */ new Map();
1944
+ for (const event of args.subagentStartEvents ?? []) if (event.data.type === "subagent_started") subagentStarts.set(event.data.subagentInvocationId, {
1945
+ createdAtMs: event.createdAtMs,
1946
+ data: event.data,
1947
+ seq: event.seq
1948
+ });
1949
+ const toolStarts = /* @__PURE__ */ new Map();
1950
+ for (const event of args.toolStartEvents ?? []) if (event.data.type === "tool_execution_started") {
1951
+ const current = toolStarts.get(event.data.toolCallId);
1952
+ if (!current || event.seq < current.seq) toolStarts.set(event.data.toolCallId, {
1953
+ createdAtMs: event.createdAtMs,
1954
+ name: event.data.toolName,
1955
+ seq: event.seq
1956
+ });
1957
+ }
1958
+ const projected = [];
1959
+ for (const event of args.events) {
1960
+ let data;
1961
+ if (event.data.type === "assistant_message" || event.data.type === "tool_result") {
1962
+ const reportArgs = {
1963
+ canExposePayload: args.canExposePayload,
1964
+ createdAtMs: event.createdAtMs,
1965
+ message: event.data,
1966
+ seq: event.seq
1967
+ };
1968
+ const result = reportingToolResultMessageSchema.safeParse(event.data);
1969
+ const start = result.success ? toolStarts.get(result.data.toolCallId) : void 0;
1970
+ data = reportAssistantMessage(reportArgs) ?? reportToolResult({
1971
+ canExposePayload: args.canExposePayload,
1972
+ message: event.data,
1973
+ ...start && start.seq < event.seq ? { start } : void 0
1974
+ });
1975
+ } else if (event.data.type === "tool_execution_started") {
1976
+ toolStarts.set(event.data.toolCallId, {
1977
+ createdAtMs: event.createdAtMs,
1978
+ name: event.data.toolName,
1979
+ seq: event.seq
1980
+ });
1981
+ data = {
1982
+ type: "tool_calls",
1983
+ calls: [{
1984
+ toolCallId: event.data.toolCallId,
1985
+ name: event.data.toolName,
1986
+ status: "running",
1987
+ startedAt: new Date(event.createdAtMs).toISOString(),
1988
+ startedSeq: event.seq
1989
+ }]
1990
+ };
1991
+ } else if (event.data.type === "subagent_started") {
1992
+ subagentStarts.set(event.data.subagentInvocationId, {
1993
+ createdAtMs: event.createdAtMs,
1994
+ data: event.data,
1995
+ seq: event.seq
1996
+ });
1997
+ data = {
1998
+ type: "subagent",
1999
+ startedSeq: event.seq,
2000
+ startedAt: new Date(event.createdAtMs).toISOString(),
2001
+ childConversationId: event.data.childConversationId,
2002
+ subagentKind: event.data.subagentKind,
2003
+ status: "running",
2004
+ ...event.data.parentToolCallId ? { parentToolCallId: event.data.parentToolCallId } : void 0
2005
+ };
2006
+ } else if (event.data.type === "subagent_ended") {
2007
+ const started = subagentStarts.get(event.data.subagentInvocationId);
2008
+ if (started && started.seq < event.seq) {
2009
+ data = {
2010
+ type: "subagent",
2011
+ startedSeq: started.seq,
2012
+ startedAt: new Date(started.createdAtMs).toISOString(),
2013
+ childConversationId: started.data.childConversationId,
2014
+ subagentKind: started.data.subagentKind,
2015
+ ...started.data.parentToolCallId ? { parentToolCallId: started.data.parentToolCallId } : void 0,
2016
+ status: event.data.outcome === "success" ? "completed" : event.data.outcome
2017
+ };
2018
+ subagentStarts.delete(event.data.subagentInvocationId);
2019
+ }
2020
+ } else if (event.data.type === "handoff") data = {
2021
+ type: "handoff",
2022
+ modelProfile: event.data.modelProfile,
2023
+ modelId: event.data.modelId,
2024
+ ...event.data.reasoningLevel ? { reasoningLevel: event.data.reasoningLevel } : void 0,
2025
+ ...event.data.triggeringToolCallId ? { triggeringToolCallId: event.data.triggeringToolCallId } : void 0,
2026
+ ...args.canExposePayload && event.data.summary ? { summary: event.data.summary } : void 0
2027
+ };
2028
+ else data = reportEventData({
2029
+ canExposePayload: args.canExposePayload,
2030
+ data: event.data
2031
+ });
2032
+ if (!data) continue;
2033
+ projected.push(conversationReportEventSchema.parse({
2034
+ seq: event.seq,
2035
+ createdAt: new Date(event.createdAtMs).toISOString(),
2036
+ data
2037
+ }));
2038
+ }
2039
+ return projected;
2040
+ }
2041
+ //#endregion
2042
+ //#region src/api/conversations/event-page.ts
2043
+ const conversationEventColumns = getTableColumns(juniorConversationEvents);
2044
+ const MIN_SCAN_SIZE = 64;
2045
+ /** Read bounded canonical rows while excluding model-only replacement history. */
2046
+ async function readConversationEventRows(executor, args) {
2047
+ return executor.db().select({
2048
+ ...conversationEventColumns,
2049
+ payload: sql`case
2050
+ when ${juniorConversationEvents.type} in ('compaction', 'handoff')
2051
+ then jsonb_set(
2052
+ ${juniorConversationEvents.payload},
2053
+ '{replacementHistory}',
2054
+ '[]'::jsonb
2055
+ )
2056
+ else ${juniorConversationEvents.payload}
2057
+ end`
2058
+ }).from(juniorConversationEvents).where(and(eq(juniorConversationEvents.conversationId, args.conversationId), args.beforeSeq === void 0 ? void 0 : lt(juniorConversationEvents.seq, args.beforeSeq), inArray(juniorConversationEvents.type, args.types ?? [...conversationReportSourceEventTypes]), args.subagentInvocationIds === void 0 ? void 0 : inArray(sql`${juniorConversationEvents.payload}->>'subagentInvocationId'`, args.subagentInvocationIds), args.toolCallIds === void 0 ? void 0 : inArray(sql`${juniorConversationEvents.payload}->>'toolCallId'`, args.toolCallIds))).orderBy(args.direction === "forward" ? asc(juniorConversationEvents.seq) : desc(juniorConversationEvents.seq)).limit(args.limit);
2059
+ }
2060
+ function decodeConversationEventRow(row) {
2061
+ return decodeStoredConversationEvent({
2062
+ schemaVersion: row.schemaVersion,
2063
+ seq: row.seq,
2064
+ historyVersion: row.historyVersion,
2065
+ ...row.idempotencyKey ? { idempotencyKey: row.idempotencyKey } : void 0,
2066
+ createdAtMs: row.createdAt.getTime(),
2067
+ type: row.type,
2068
+ payload: row.payload
2069
+ });
2070
+ }
2071
+ /** Project stored rows and resolve entity starts that precede the page. */
2072
+ async function projectConversationEventRows(executor, args) {
2073
+ const events = args.rows.map(decodeConversationEventRow).sort((left, right) => left.seq - right.seq);
2074
+ const endedInvocationIds = [...new Set(events.flatMap((event) => event.data.type === "subagent_ended" ? [event.data.subagentInvocationId] : []))];
2075
+ const subagentStartRows = endedInvocationIds.length === 0 ? [] : await readConversationEventRows(executor, {
2076
+ conversationId: args.conversationId,
2077
+ direction: "forward",
2078
+ limit: endedInvocationIds.length,
2079
+ subagentInvocationIds: endedInvocationIds,
2080
+ types: ["subagent_started"]
2081
+ });
2082
+ const toolResultIds = conversationReportToolResultIds(events);
2083
+ const toolStartRows = toolResultIds.length === 0 ? [] : await readConversationEventRows(executor, {
2084
+ conversationId: args.conversationId,
2085
+ direction: "forward",
2086
+ limit: toolResultIds.length,
2087
+ toolCallIds: toolResultIds,
2088
+ types: ["tool_execution_started"]
2089
+ });
2090
+ return projectConversationReportEventPage({
2091
+ canExposePayload: args.canExposePayload,
2092
+ events,
2093
+ subagentStartEvents: subagentStartRows.map(decodeConversationEventRow),
2094
+ toolStartEvents: toolStartRows.map(decodeConversationEventRow)
2095
+ });
2096
+ }
2097
+ /**
2098
+ * Read the latest reporting events before an exclusive boundary.
2099
+ *
2100
+ * Source rows are scanned in bounded chunks until the requested projected page
2101
+ * and one older event are known. This keeps ordinary pages bounded without
2102
+ * assuming every agent history event produces a reporting event.
2103
+ */
2104
+ async function readConversationEventPage(executor, args) {
2105
+ const scanSize = Math.max(args.limit + 1, MIN_SCAN_SIZE);
2106
+ const rows = [];
2107
+ let beforeSeq = args.beforeSeq;
2108
+ let projected = [];
2109
+ while (projected.length <= args.limit) {
2110
+ const batch = await readConversationEventRows(executor, {
2111
+ beforeSeq,
2112
+ conversationId: args.conversationId,
2113
+ direction: "backward",
2114
+ limit: scanSize
2115
+ });
2116
+ if (batch.length === 0) break;
2117
+ rows.push(...batch);
2118
+ projected = await projectConversationEventRows(executor, {
2119
+ canExposePayload: args.canExposePayload,
2120
+ conversationId: args.conversationId,
2121
+ rows
2122
+ });
2123
+ if (projected.length > args.limit || batch.length < scanSize) break;
2124
+ beforeSeq = batch.at(-1).seq;
2125
+ }
2126
+ const events = projected.slice(-args.limit);
2127
+ return {
2128
+ events,
2129
+ ...projected.length > events.length && events[0] ? { previousSeq: events[0].seq } : void 0
2130
+ };
2131
+ }
2132
+ //#endregion
2133
+ //#region src/chat/briefs/input.ts
2134
+ const EVENT_PAGE_SIZE = 500;
2135
+ async function readReportEventsThrough(executor, conversationId, throughSeq) {
2136
+ const pages = [];
2137
+ let beforeSeq = throughSeq + 1;
2138
+ do {
2139
+ const page = await readConversationEventPage(executor, {
2140
+ beforeSeq,
2141
+ canExposePayload: true,
2142
+ conversationId,
2143
+ limit: EVENT_PAGE_SIZE
2144
+ });
2145
+ pages.unshift(page.events);
2146
+ beforeSeq = page.previousSeq;
2147
+ } while (beforeSeq !== void 0);
2148
+ const bySequence = /* @__PURE__ */ new Map();
2149
+ for (const event of pages.flat()) bySequence.set(event.seq, event);
2150
+ return [...bySequence.values()].sort((left, right) => left.seq - right.seq);
2151
+ }
2152
+ /**
2153
+ * Build generator input from SQL through one durable event. The caller owns
2154
+ * the boundary, so a Turn that completes during the read cannot be covered by
2155
+ * a Brief that never saw it.
2156
+ */
2157
+ async function briefInputFromSql(executor, conversationId, throughSeq) {
2158
+ const db = executor.db();
2159
+ const conversation = await createSqlStore(executor).get({ conversationId });
2160
+ if (!conversation) throw new Error(`Conversation ${conversationId} is unavailable`);
2161
+ const [events, annotations, codeChanges] = await Promise.all([
2162
+ readReportEventsThrough(executor, conversationId, throughSeq),
2163
+ listConversationAnnotations(db, conversationId),
2164
+ db.select({
2165
+ closedAt: juniorCodeChanges.closedAt,
2166
+ mergedAt: juniorCodeChanges.mergedAt,
2167
+ number: juniorCodeChanges.number,
2168
+ openedAt: juniorCodeChanges.openedAt,
2169
+ repository: juniorCodeRepositories.name,
2170
+ state: juniorCodeChanges.state,
2171
+ title: juniorCodeChanges.title,
2172
+ url: juniorCodeChanges.url
2173
+ }).from(juniorCodeChanges).innerJoin(juniorCodeRepositories, eq(juniorCodeRepositories.id, juniorCodeChanges.repositoryId)).where(and(arrayContains(juniorCodeChanges.conversationIds, [conversationId]), isNotNull(juniorCodeChanges.url)))
2174
+ ]);
2175
+ const location = conversation.location;
2176
+ return parseBriefInput({
2177
+ conversationId,
2178
+ ...conversation.title ? { title: conversation.title } : void 0,
2179
+ visibility: conversation.visibility ?? "private",
2180
+ ...location ? { location: {
2181
+ provider: location.provider,
2182
+ ...conversation.channelName ? { channelName: conversation.channelName } : void 0
2183
+ } } : void 0,
2184
+ entries: briefEntriesFromReportEvents(events),
2185
+ codeChanges: codeChanges.map((change) => ({
2186
+ repository: change.repository,
2187
+ number: change.number,
2188
+ ...change.title ? { title: change.title } : void 0,
2189
+ url: change.url,
2190
+ state: change.state,
2191
+ openedAt: change.openedAt.toISOString(),
2192
+ ...change.mergedAt ? { mergedAt: change.mergedAt.toISOString() } : void 0,
2193
+ ...change.closedAt ? { closedAt: change.closedAt.toISOString() } : void 0
2194
+ })),
2195
+ resources: annotations.map((annotation) => ({
2196
+ label: annotation.label,
2197
+ url: annotation.url,
2198
+ ...annotation.status ? { status: annotation.status } : void 0
2199
+ }))
2200
+ });
2201
+ }
2202
+ //#endregion
2203
+ //#region src/chat/briefs/operational-report.ts
2204
+ const DAY_MS$1 = 864e5;
2205
+ const WINDOWS = [
2206
+ 7,
2207
+ 30,
2208
+ 90
2209
+ ];
2210
+ function startOfUtcDay$1(value) {
2211
+ const date = new Date(value);
2212
+ date.setUTCHours(0, 0, 0, 0);
2213
+ return date;
2214
+ }
2215
+ function formatCount(value) {
2216
+ return new Intl.NumberFormat("en-US").format(value);
2217
+ }
2218
+ function formatUsd(value) {
2219
+ const maximumFractionDigits = value > 0 && value < .01 ? 4 : 2;
2220
+ return new Intl.NumberFormat("en-US", {
2221
+ currency: "USD",
2222
+ maximumFractionDigits,
2223
+ minimumFractionDigits: 2,
2224
+ style: "currency"
2225
+ }).format(value);
2226
+ }
2227
+ /**
2228
+ * Build Brief storage and model-cost diagnostics for the System page. Cost
2229
+ * comes from retained Conversation events, so spend on purged private
2230
+ * Conversations leaves the report with them; the labels say so.
2231
+ */
2232
+ async function buildBriefsOperationalReport(args) {
2233
+ const newestDay = startOfUtcDay$1(args.nowMs);
2234
+ const windowStart = /* @__PURE__ */ new Date(newestDay.getTime() - 29 * DAY_MS$1);
2235
+ const windowEnd = new Date(newestDay.getTime() + DAY_MS$1);
2236
+ const [[counts], recentCounts] = await Promise.all([args.db.select({
2237
+ conversations: sql`count(distinct ${juniorConversationBriefs.conversationId})`.mapWith(Number),
2238
+ stored: sql`count(*)`.mapWith(Number)
2239
+ }).from(juniorConversationBriefs), args.db.select({ stored: sql`count(*)`.mapWith(Number) }).from(juniorConversationBriefs).where(and(gte(juniorConversationBriefs.createdAt, windowStart), lt(juniorConversationBriefs.createdAt, windowEnd)))]);
2240
+ const recentDays = args.briefDays.filter((day) => {
2241
+ const dayStart = Date.parse(`${day.date}T00:00:00.000Z`);
2242
+ return dayStart >= windowStart.getTime() && dayStart < windowEnd.getTime();
2243
+ });
2244
+ const recentCost = recentDays.reduce((total, day) => total + day.costUsd, 0);
2245
+ const recentBriefs = recentDays.reduce((total, day) => total + day.events, 0);
2246
+ return {
2247
+ generatedAt: new Date(args.nowMs).toISOString(),
2248
+ title: "Briefs",
2249
+ metrics: [
2250
+ {
2251
+ label: "briefs stored",
2252
+ tone: counts?.stored ? "good" : "neutral",
2253
+ value: formatCount(counts?.stored ?? 0)
2254
+ },
2255
+ {
2256
+ label: "conversations with a brief",
2257
+ value: formatCount(counts?.conversations ?? 0)
2258
+ },
2259
+ {
2260
+ label: "briefs · 30d",
2261
+ value: formatCount(recentCounts[0]?.stored ?? 0)
2262
+ },
2263
+ {
2264
+ label: "cost · 30d · retained",
2265
+ value: formatUsd(recentCost)
2266
+ },
2267
+ {
2268
+ label: "average cost per brief · 30d · retained",
2269
+ value: formatUsd(recentBriefs === 0 ? 0 : recentCost / recentBriefs)
2270
+ }
2271
+ ],
2272
+ widgets: [{
2273
+ categories: args.briefDays.map((day) => ({
2274
+ id: day.date,
2275
+ label: day.date,
2276
+ values: {
2277
+ briefs: day.events,
2278
+ costUsd: day.costUsd
2279
+ }
2280
+ })),
2281
+ description: "Stored Brief versions and estimated model cost",
2282
+ id: "briefs-created",
2283
+ series: [{
2284
+ key: "briefs",
2285
+ label: "Briefs"
2286
+ }, {
2287
+ format: "usd",
2288
+ key: "costUsd",
2289
+ label: "Cost"
2290
+ }],
2291
+ timeRangeDays: [...WINDOWS],
2292
+ title: "Briefs created",
2293
+ type: "bar_chart"
2294
+ }]
2295
+ };
2296
+ }
2297
+ //#endregion
2298
+ //#region src/chat/briefs/store.ts
2299
+ function briefVersionFromRow(row) {
2300
+ return {
2301
+ conversationId: row.conversationId,
2302
+ version: row.version,
2303
+ turnId: row.turnId,
2304
+ throughSeq: row.throughSeq,
2305
+ content: conversationBriefSchema.parse(row.content),
2306
+ searchText: row.searchText,
2307
+ modelId: row.modelId,
2308
+ ...row.costUsd !== null ? { costUsd: row.costUsd } : void 0,
2309
+ createdAt: row.createdAt
2310
+ };
2311
+ }
2312
+ /** Append the next Brief version, or return the row for an existing Turn. */
2313
+ async function appendConversationBrief(db, input) {
2314
+ return await db.transaction(async (tx) => {
2315
+ const conversation = (await tx.select({
2316
+ conversationId: juniorConversations.conversationId,
2317
+ transcriptPurgedAt: juniorConversations.transcriptPurgedAt
2318
+ }).from(juniorConversations).where(eq(juniorConversations.conversationId, input.conversationId)).for("update"))[0];
2319
+ if (!conversation) throw new Error(`Conversation ${input.conversationId} is unavailable`);
2320
+ if (conversation.transcriptPurgedAt) {
2321
+ if ((await resolveRootVisibility({ db: () => tx }, input.conversationId)).visibility !== "public") throw new Error(`Cannot append a Brief to purged non-public Conversation ${input.conversationId}`);
2322
+ }
2323
+ const existing = await tx.select().from(juniorConversationBriefs).where(and(eq(juniorConversationBriefs.conversationId, input.conversationId), eq(juniorConversationBriefs.turnId, input.turnId))).limit(1);
2324
+ if (existing[0]) return {
2325
+ inserted: false,
2326
+ value: briefVersionFromRow(existing[0])
2327
+ };
2328
+ const version = ((await tx.select({ version: max(juniorConversationBriefs.version) }).from(juniorConversationBriefs).where(eq(juniorConversationBriefs.conversationId, input.conversationId)))[0]?.version ?? 0) + 1;
2329
+ const row = (await tx.insert(juniorConversationBriefs).values({
2330
+ conversationId: input.conversationId,
2331
+ version,
2332
+ turnId: input.turnId,
2333
+ throughSeq: input.throughSeq,
2334
+ content: conversationBriefSchema.parse(input.content),
2335
+ searchText: input.searchText,
2336
+ modelId: input.modelId,
2337
+ costUsd: input.costUsd ?? null
2338
+ }).onConflictDoNothing({ target: [juniorConversationBriefs.conversationId, juniorConversationBriefs.turnId] }).returning())[0];
2339
+ if (!row) throw new Error("Brief insert conflicted after the Turn check");
2340
+ return {
2341
+ inserted: true,
2342
+ value: briefVersionFromRow(row)
2343
+ };
2344
+ });
2345
+ }
2346
+ /** Read the Brief version produced for one completed Turn. */
2347
+ async function readConversationBriefForTurn(db, conversationId, turnId) {
2348
+ const rows = await db.select().from(juniorConversationBriefs).where(and(eq(juniorConversationBriefs.conversationId, conversationId), eq(juniorConversationBriefs.turnId, turnId))).limit(1);
2349
+ return rows[0] ? briefVersionFromRow(rows[0]) : void 0;
2350
+ }
2351
+ /** Read the latest Brief version for one Conversation. */
2352
+ async function readLatestConversationBrief(db, conversationId) {
2353
+ const rows = await db.select().from(juniorConversationBriefs).where(eq(juniorConversationBriefs.conversationId, conversationId)).orderBy(desc(juniorConversationBriefs.version)).limit(1);
2354
+ return rows[0] ? briefVersionFromRow(rows[0]) : void 0;
2355
+ }
2356
+ //#endregion
2357
+ //#region src/chat/briefs/task.ts
2358
+ const BRIEF_LOCK_TTL_MS = 6e5;
2359
+ function hasUserInstruction(run) {
2360
+ return run.transcript.some((entry) => entry.type === "message" && entry.role === "user" && entry.provenance?.authority === "instruction");
2361
+ }
2362
+ function logSkip(context, conversationId, reason) {
2363
+ context.log.info("Brief update skipped", {
2364
+ conversationId,
2365
+ reason
2366
+ });
2367
+ }
2368
+ async function readTurnCompletedSeq(db, conversationId, turnId) {
2369
+ const seq = (await db.select({ seq: juniorConversationEvents.seq }).from(juniorConversationEvents).where(and(eq(juniorConversationEvents.conversationId, conversationId), eq(juniorConversationEvents.type, "turn_completed"), sql`${juniorConversationEvents.payload}->>'turnId' = ${turnId}`)).orderBy(desc(juniorConversationEvents.seq)).limit(1))[0]?.seq;
2370
+ if (seq === void 0) throw new Error(`Completed Turn ${turnId} has no terminal event in Conversation ${conversationId}`);
2371
+ return seq;
2372
+ }
2373
+ async function emitBriefUpdated(context, stored) {
2374
+ await context.events.emit(briefUpdatedEvent({
2375
+ version: stored.version,
2376
+ modelId: stored.modelId,
2377
+ ...stored.costUsd !== void 0 ? { costUsd: stored.costUsd } : void 0,
2378
+ decisions: stored.content.decisions.length,
2379
+ openDecisions: stored.content.openDecisions.length,
2380
+ links: stored.content.links.length
2381
+ }));
2382
+ }
2383
+ /** Generate and store the next Brief after one completed Turn. */
2384
+ async function updateConversationBrief(context) {
2385
+ const run = await context.run.load();
2386
+ if (run.source.kind !== "slack" && run.source.kind !== "web" && run.source.kind !== "local") {
2387
+ logSkip(context, run.conversationId, "unsupported_source");
2388
+ return;
2389
+ }
2390
+ const executor = getSqlExecutor();
2391
+ const conversation = await createSqlStore(executor).get({ conversationId: run.conversationId });
2392
+ if (conversation?.parentConversationId) {
2393
+ logSkip(context, run.conversationId, "child_conversation");
2394
+ return;
2395
+ }
2396
+ if (conversation?.transcriptPurgedAtMs !== void 0) {
2397
+ if ((await resolveRootVisibility(executor, run.conversationId)).visibility !== "public") {
2398
+ logSkip(context, run.conversationId, "purged_private");
2399
+ return;
2400
+ }
2401
+ }
2402
+ if (!hasUserInstruction(run)) {
2403
+ logSkip(context, run.conversationId, "no_user_instruction");
2404
+ return;
2405
+ }
2406
+ await context.state.withLock(`brief:${run.conversationId}`, BRIEF_LOCK_TTL_MS, async () => {
2407
+ const db = context.db;
2408
+ const existing = await readConversationBriefForTurn(db, run.conversationId, run.runId);
2409
+ if (existing) {
2410
+ await emitBriefUpdated(context, existing);
2411
+ logSkip(context, run.conversationId, "turn_already_stored");
2412
+ return;
2413
+ }
2414
+ const terminalSeq = await readTurnCompletedSeq(db, run.conversationId, run.runId);
2415
+ const previous = await readLatestConversationBrief(db, run.conversationId);
2416
+ if (previous && previous.throughSeq >= terminalSeq) {
2417
+ logSkip(context, run.conversationId, "turn_already_covered");
2418
+ return;
2419
+ }
2420
+ const input = await briefInputFromSql(executor, run.conversationId, terminalSeq);
2421
+ const modelId = defaultModelId(botConfig);
2422
+ const generation = await generateBrief({
2423
+ completeObject: (request) => completeObject({
2424
+ ...request,
2425
+ modelId,
2426
+ promptName: "junior.brief_update"
2427
+ }),
2428
+ input,
2429
+ previous: previous?.content,
2430
+ prompt: BRIEF_PROMPT,
2431
+ throughIndex: terminalSeq
2432
+ });
2433
+ const stored = await appendConversationBrief(db, {
2434
+ conversationId: run.conversationId,
2435
+ turnId: run.runId,
2436
+ throughSeq: terminalSeq,
2437
+ content: generation.brief,
2438
+ searchText: generation.searchText,
2439
+ modelId,
2440
+ ...generation.costUsd !== void 0 ? { costUsd: generation.costUsd } : void 0
2441
+ });
2442
+ await emitBriefUpdated(context, stored.value);
2443
+ if (!stored.inserted) logSkip(context, run.conversationId, "turn_already_stored");
2444
+ });
2445
+ }
2446
+ /** Core task registration kept outside the installed plugin catalog. */
2447
+ const briefsTaskRegistration = {
2448
+ manifest: {
2449
+ name: "briefs",
2450
+ displayName: "Briefs",
2451
+ description: "Durable Conversation Brief generation"
2452
+ },
2453
+ conversationEvents: [briefUpdatedEvent],
2454
+ hooks: { async operationalReport(context) {
2455
+ return await buildBriefsOperationalReport({
2456
+ briefDays: await context.eventStats.costsByDay({
2457
+ days: 90,
2458
+ eventName: "brief_updated"
2459
+ }),
2460
+ db: context.db,
2461
+ nowMs: context.nowMs
2462
+ });
2463
+ } },
2464
+ tasks: { updateBrief: { async run(context) {
2465
+ await updateConversationBrief(context);
2466
+ } } }
2467
+ };
2468
+ //#endregion
1423
2469
  //#region src/chat/plugins/conversation-events.ts
1424
2470
  function registeredDefinition(plugin, value) {
1425
2471
  return plugin.conversationEvents?.find((definition) => definition === value.definition);
@@ -1453,7 +2499,7 @@ function createPluginConversationEvents(args) {
1453
2499
  }
1454
2500
  /** Render a stored plugin event through its currently registered definition. */
1455
2501
  function renderPluginConversationEvent(args) {
1456
- const definition = getPlugins().find((candidate) => candidate.manifest.name === args.namespace)?.conversationEvents?.find((candidate) => candidate.eventName === args.name && candidate.version === args.version);
2502
+ const definition = [briefsTaskRegistration, ...getPlugins()].find((candidate) => candidate.manifest.name === args.namespace)?.conversationEvents?.find((candidate) => candidate.eventName === args.name && candidate.version === args.version);
1457
2503
  if (!definition) return void 0;
1458
2504
  return definition.renderEvent(definition.parse(args.content));
1459
2505
  }
@@ -1664,7 +2710,7 @@ function createPluginState(plugin, adapter) {
1664
2710
  };
1665
2711
  }
1666
2712
  //#endregion
1667
- //#region src/chat/slack/dashboard-link.ts
2713
+ //#region src/chat/dashboard-link.ts
1668
2714
  let dashboardConversationLinkOptions;
1669
2715
  function withHttps(host) {
1670
2716
  return /^https?:\/\//.test(host) ? host : `https://${host}`;
@@ -1687,7 +2733,7 @@ function resolveDashboardBaseURL(config) {
1687
2733
  if (vercelUrl) return stripTrailingSlashes(withHttps(vercelUrl));
1688
2734
  return "http://localhost:3000";
1689
2735
  }
1690
- /** Configure core dashboard links used in Slack footers. */
2736
+ /** Configure core dashboard links. */
1691
2737
  function setDashboardConversationLinkOptions(options) {
1692
2738
  const previous = dashboardConversationLinkOptions;
1693
2739
  dashboardConversationLinkOptions = options?.disabled ? void 0 : options;
@@ -3091,6 +4137,28 @@ function createCodeChangePublisher(provider) {
3091
4137
  };
3092
4138
  }
3093
4139
  //#endregion
4140
+ //#region src/chat/briefs/registration.ts
4141
+ var registration_exports = /* @__PURE__ */ __exportAll({
4142
+ coreTaskRegistrations: () => coreTaskRegistrations,
4143
+ isBriefsEnabled: () => isBriefsEnabled,
4144
+ setBriefsConfig: () => setBriefsConfig
4145
+ });
4146
+ let configuredBriefs = {};
4147
+ /** Replace app-level Brief settings and return the previous setting. */
4148
+ function setBriefsConfig(config) {
4149
+ const previous = { ...configuredBriefs };
4150
+ configuredBriefs = config ? { ...config } : {};
4151
+ return previous;
4152
+ }
4153
+ /** Return whether automatic Brief generation is enabled. */
4154
+ function isBriefsEnabled() {
4155
+ return configuredBriefs.enabled === true;
4156
+ }
4157
+ /** Return enabled core task registrations. */
4158
+ function coreTaskRegistrations() {
4159
+ return isBriefsEnabled() ? [briefsTaskRegistration] : [];
4160
+ }
4161
+ //#endregion
3094
4162
  //#region src/chat/plugins/agent-hooks.ts
3095
4163
  var agent_hooks_exports = /* @__PURE__ */ __exportAll({
3096
4164
  PluginHookDeniedError: () => PluginHookDeniedError,
@@ -3815,7 +4883,7 @@ function failedOperationalReport(args) {
3815
4883
  /** Collect read-only operational summaries exposed by plugins. */
3816
4884
  async function getPluginOperationalReports(nowMs) {
3817
4885
  const reports = [];
3818
- for (const plugin of getPlugins()) {
4886
+ for (const plugin of [...coreTaskRegistrations(), ...getPlugins()]) {
3819
4887
  const pluginName = plugin.manifest.name;
3820
4888
  const hook = plugin.hooks?.operationalReport;
3821
4889
  if (!hook) continue;
@@ -4037,4 +5105,4 @@ function createPluginHookRunner(input = {}) {
4037
5105
  };
4038
5106
  }
4039
5107
  //#endregion
4040
- export { RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS as $, bindEventTaskCredentialSubject as A, sanitizePostgresJson as At, getSlackClient as B, parseDestination as Bt, setWorkspaceSnapshotBuild as C, getConversationEventStore as Ct, tryWorkspaceRepoCheckoutPath as D, getSqlExecutor as Dt, getWorkspaceSnapshotBuilder as E, getDb as Et, verifySlackDirectCredentialSubject as F, resolveRootConversationId as Ft, withSlackRetries as G, isDmChannel as H, sameDestination as Ht, SlackActionError as I, deleteProviderIdentityForSlackUser as It, findUserByEmail as J, getWorkspaceTeamId as K, downloadPrivateSlackFile as L, upsertIdentity as Lt, bindSlackDirectCredentialSubject as M, locationFromRow as Mt, verifyEventTaskCredentialSubject as N, bindProviderConversation as Nt, workspaceRepoCheckoutPath as O, createSqlConversationEventStore as Ot, verifyScheduledTaskCredentialSubject as P, parseSessionSource as Pt, getSlackToolContext as Q, getFilePermalink as R, upsertLinkedIdentity as Rt, setWorkspaceSnapshot as S, db_exports as St, deleteWorkspaceSnapshotBuilders as T, getConversationStore as Tt, normalizeSlackConversationId as U, isConversationScopedChannel as V, requireSlackDestination as Vt, runWithSlackInstallationToken as W, resolveViewerUser as X, readActorIdentity as Y, updateViewerDisplayName as Z, listWorkspaces as _, createPluginLogger as _t, getPluginRoutes as a, collectResourceEventMatchKeys as at, invalidateReadySnapshot as b, listConversationAnnotations as bt, getPluginTools as c, findMatchingResourceEventSubscriptions as ct, setPlugins as d, getDashboardConversationLink as dt, RESOURCE_SUBSCRIPTION_MAX_TTL_MS as et, validatePlugins as f, getDashboardTaskLink as ft, getWorkspaceByName as g, createPluginModel as gt, getWorkspace as h, createPluginEmbedder as ht, getPluginApiRoutes as i, cancelSubscriptions as it, bindScheduledTaskCredentialSubject as j, withConversationMutationLock as jt, resolveChannelCapabilities as k, withConversationEventLock as kt, getPluginUserPromptContributions as l, listResourceEventSubscriptions as lt, deleteWorkspace as m, createPluginState as mt, applyPluginFormatMarkdown as n, STOP_WATCHING_TOOL_NAME as nt, getPluginSlackConversationLink as o, createResourceEventSubscription as ot, createWorkspace as p, setDashboardConversationLinkOptions as pt, runWithWorkspaceTeamId as q, createPluginHookRunner as r, cancelResourceEventSubscription as rt, getPluginSystemPromptContributions as s, deliverResourceEventSubscription as st, agent_hooks_exports as t, RESOURCE_WATCH_TOOL_SOURCE as tt, getPlugins as u, JUNIOR_THREAD_STATE_TTL_MS as ut, updateWorkspace as v, createPluginConversationEvents as vt, SANDBOX_RUNTIME as w, getConversationMessageSearchStore as wt, loadSnapshotsForProfile as x, listConversationAnnotationsById as xt, WorkspaceValidationError as y, renderPluginConversationEvent as yt, getHeaderString as z, createSlackDestination as zt };
5108
+ export { resolveViewerUser as $, tryWorkspaceRepoCheckoutPath as A, authenticationLinkedEvent as At, downloadPrivateSlackFile as B, sanitizePostgresJson as Bt, invalidateReadySnapshot as C, readConversationEventPage as Ct, SANDBOX_RUNTIME as D, listConversationAnnotationsById as Dt, setWorkspaceSnapshotBuild as E, listConversationAnnotations as Et, bindSlackDirectCredentialSubject as F, getConversationStore as Ft, isDmChannel as G, resolveRootConversationId as Gt, getHeaderString as H, locationFromRow as Ht, verifyEventTaskCredentialSubject as I, getDb as It, withSlackRetries as J, upsertLinkedIdentity as Jt, normalizeSlackConversationId as K, deleteProviderIdentityForSlackUser as Kt, verifyScheduledTaskCredentialSubject as L, getSqlExecutor as Lt, resolveChannelCapabilities as M, db_exports as Mt, bindEventTaskCredentialSubject as N, getConversationEventStore as Nt, deleteWorkspaceSnapshotBuilders as O, JUNIOR_NATIVE_EVENT_NAMESPACE as Ot, bindScheduledTaskCredentialSubject as P, getConversationMessageSearchStore as Pt, readActorIdentity as Q, sameDestination as Qt, verifySlackDirectCredentialSubject as R, createSqlConversationEventStore as Rt, WorkspaceValidationError as S, readLatestConversationBrief as St, setWorkspaceSnapshot as T, resolveRootVisibility as Tt, getSlackClient as U, bindProviderConversation as Ut, getFilePermalink as V, withConversationMutationLock as Vt, isConversationScopedChannel as W, parseSessionSource as Wt, runWithWorkspaceTeamId as X, parseDestination as Xt, getWorkspaceTeamId as Y, createSlackDestination as Yt, findUserByEmail as Z, requireSlackDestination as Zt, deleteWorkspace as _, createPluginState as _t, getPluginRoutes as a, STOP_WATCHING_TOOL_NAME as at, listWorkspaces as b, createPluginLogger as bt, getPluginTools as c, collectResourceEventMatchKeys as ct, setPlugins as d, findMatchingResourceEventSubscriptions as dt, updateViewerDisplayName as et, validatePlugins as f, listResourceEventSubscriptions as ft, createWorkspace as g, setDashboardConversationLinkOptions as gt, setBriefsConfig as h, getDashboardTaskLink as ht, getPluginApiRoutes as i, RESOURCE_WATCH_TOOL_SOURCE as it, workspaceRepoCheckoutPath as j, authenticationUnlinkedEvent as jt, getWorkspaceSnapshotBuilder as k, agentsInstructionsUpdatedEvent as kt, getPluginUserPromptContributions as l, createResourceEventSubscription as lt, registration_exports as m, getDashboardConversationLink as mt, applyPluginFormatMarkdown as n, RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS as nt, getPluginSlackConversationLink as o, cancelResourceEventSubscription as ot, coreTaskRegistrations as p, JUNIOR_THREAD_STATE_TTL_MS as pt, runWithSlackInstallationToken as q, upsertIdentity as qt, createPluginHookRunner as r, RESOURCE_SUBSCRIPTION_MAX_TTL_MS as rt, getPluginSystemPromptContributions as s, cancelSubscriptions as st, agent_hooks_exports as t, getSlackToolContext as tt, getPlugins as u, deliverResourceEventSubscription as ut, getWorkspace as v, createPluginEmbedder as vt, loadSnapshotsForProfile as w, buildSentryConversationUrl as wt, updateWorkspace as x, createPluginConversationEvents as xt, getWorkspaceByName as y, createPluginModel as yt, SlackActionError as z, withConversationEventLock as zt };