@sentry/junior 0.271.0 → 0.273.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 (75) hide show
  1. package/dist/{adapter-Bqbtr1RV.js → adapter-umap1cth.js} +1 -1
  2. package/dist/{admin-CWBjzyN0.js → admin-0o6E_3lo.js} +1 -1
  3. package/dist/{agent-runner-BiHsH8O_.js → agent-runner-D7Qz-lGO.js} +899 -631
  4. package/dist/api/schema.d.ts +2 -2
  5. package/dist/api/schema.js +5 -5
  6. package/dist/api.d.ts +1 -1
  7. package/dist/api.js +62 -537
  8. package/dist/{app-C81xfpNa.d.ts → app-BcyhJq8x.d.ts} +4 -9
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +673 -134
  11. package/dist/{assistant-reply-DgtX9DaJ.js → assistant-reply-wZ7i4OFa.js} +1 -1
  12. package/dist/{automation-Dwh_xcqM.js → automation-yffXcZt9.js} +1 -1
  13. package/dist/{briefs-0FeSJHQ3.js → briefs-CgMnlpIp.js} +4 -4
  14. package/dist/{catalog-runtime-Bw9kjGVu.js → catalog-runtime-TygDsPOU.js} +1 -1
  15. package/dist/cli/chat.js +15 -15
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/init.js +1 -1
  18. package/dist/cli/main.js +3 -3
  19. package/dist/cli/plugins.js +4 -4
  20. package/dist/cli/snapshot-create.js +4 -4
  21. package/dist/cli/upgrade.d.ts +1 -1
  22. package/dist/cli/upgrade.js +3 -3
  23. package/dist/{client-B0jlQSgO.js → client-DhqbV0Fe.js} +63 -5
  24. package/dist/{reporting-schema-CZREhYc-.js → common-Eu6sx09c.js} +4 -1
  25. package/dist/{config-DrAeD8sr.js → config-C6FTd7PX.js} +7 -15
  26. package/dist/{connections-BzN2K0G9.js → connections-Dq72KajR.js} +11 -11
  27. package/dist/{conversation-AhOVzN_k.js → conversation-T4oabbBi.js} +22 -1
  28. package/dist/{conversation-privacy-D1NhUxyo.js → conversation-privacy-BeRgrsph.js} +1 -1
  29. package/dist/{credential-sync-DpNWl4yy.js → credential-sync-BmCbHAp4.js} +15 -16
  30. package/dist/{db-DSabI7jJ.d.ts → db-DOzBGLn5.d.ts} +11 -1
  31. package/dist/{executor-C2cRw0hZ.js → executor-BafgvQcc.js} +25 -7
  32. package/dist/instrumentation.js +114 -3
  33. package/dist/{job-queue-CEeLYkSj.js → job-queue-C4lphW9Y.js} +3 -3
  34. package/dist/{logging-DuwXUuIY.js → logging-CNs1e04n.js} +6 -1
  35. package/dist/message-CETC5lsl.js +186 -0
  36. package/dist/nitro.d.ts +1 -1
  37. package/dist/nitro.js +3 -3
  38. package/dist/{oauth-callback-server-oM5TJt34.js → oauth-callback-server-DcQZsKMm.js} +2 -2
  39. package/dist/{credentialed-mh7HBqey.js → projection-BOpi891B.js} +4576 -49
  40. package/dist/{prompt-BPSdXYXV.js → prompt-B1ySjUyf.js} +1 -1
  41. package/dist/{registry-BPtbAVFo.js → registry-B-yUrv3h.js} +2 -2
  42. package/dist/{resolve-D44G1jJm.js → resolve-DqVtd4Q9.js} +3 -3
  43. package/dist/{result-CvP8c5bb.js → result-DQwTiLTw.js} +16 -12
  44. package/dist/{runner-FIGIKvk2.js → runner-Dco5qMq6.js} +11 -11
  45. package/dist/{sandbox-egress-signals-C_JzucwP.js → sandbox-egress-signals-DFc_9Mcv.js} +1 -1
  46. package/dist/{schema-DKZgO-xf.js → schema-5xHDGlBW.js} +10 -9
  47. package/dist/{schema-DPCvePop.d.ts → schema-9OGdsaxF.d.ts} +28 -3
  48. package/dist/{service-account-1mJGRAvx.js → service-account-CWfk9Cs-.js} +3 -3
  49. package/dist/{service-account-CHO3YXpt.js → service-account-smdPFWNy.js} +3 -3
  50. package/dist/{session-stop-DuUB1rwQ.js → session-stop-DaT_7tYa.js} +50 -24
  51. package/dist/{skills-DHK7L9Eo.js → skills-BbtJSF2-.js} +2 -2
  52. package/dist/{spawn-CKl_ySIq.js → spawn-Ceikdjkp.js} +2 -2
  53. package/dist/{list-DjJJ5K0x.js → stop-dKUc-x2K.js} +1017 -469
  54. package/dist/{store-Di0E4EvU.js → store-C1xuZcLd.js} +48 -31
  55. package/dist/{store-BF2XEeQz.js → store-DaxUuOmD.js} +3 -3
  56. package/dist/{task-queue-CC7IFtbT.js → task-queue-ymAh_-P1.js} +4 -4
  57. package/dist/{task-runner-DKZtMFwa.js → task-runner-vlH6pEm3.js} +6 -6
  58. package/dist/{thread-state-BQ1T3eeF.js → thread-state-BkUpE9Ae.js} +4 -5
  59. package/dist/{turn-failure-response-B5eSFoHn.js → turn-failure-response-DIsD7ip_.js} +7 -8
  60. package/dist/{turn-lifecycle-CTLEIEOn.js → turn-lifecycle-BdPKdPS8.js} +1 -1
  61. package/dist/turn-session-routing-BdCTDq9m.js +1625 -0
  62. package/dist/{validation-Cjo-3yt3.js → validation-BQjCfh84.js} +1 -1
  63. package/dist/{version-D30MBLLw.js → version-BpiFGZZW.js} +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/{viewer-D3jhuP66.js → viewer-C6Y1aXjZ.js} +49 -21
  66. package/dist/{work-CL9QwQcl.js → work-ChPh_YEh.js} +27 -39
  67. package/dist/{worker-S2P7IJDI.js → worker-Be_sK_ou.js} +6 -6
  68. package/migrations/0072_conversation_read_positions.sql +10 -0
  69. package/migrations/meta/0072_snapshot.json +4064 -0
  70. package/migrations/meta/_journal.json +7 -0
  71. package/package.json +4 -4
  72. package/dist/message-CRe3WBKc.js +0 -59
  73. package/dist/turn-session-routing-BFVp8TbU.js +0 -6072
  74. package/migrations/AGENTS.md +0 -8
  75. package/dist/{oauth-relay-CTygMkNx.js → oauth-relay-C6ZAyJgf.js} +1 -1
package/dist/api.js CHANGED
@@ -1,68 +1,39 @@
1
- import { F as conversationRangeColumns, N as conversationActiveDaysColumn, P as conversationAggregateColumns, Wr as isExperimentalFeatureEnabled, ct as AutomationConflictError, ft as automationEventCatalog, j as readAutomationTitlesByConversationId } from "./turn-session-routing-BFVp8TbU.js";
2
- import { p as escapeXml } from "./client-B0jlQSgO.js";
3
- import { m as logInfo, p as logException } from "./logging-DuwXUuIY.js";
4
- import { C as getSqlExecutor, E as loadConversationSlackTenantIds, J as resolveRootConversationId, K as agentTurnUsageSchema, S as getDb, c as readViewerAvatarUrl, d as updateViewerDisplayName, l as resolveViewerUser, m as readAvatarImage, q as hasAgentTurnUsage, t as findUserByEmail, x as getConversationStore } from "./viewer-D3jhuP66.js";
1
+ import { Ca as createPluginLogger, Cr as hash, Fa as readConversationEventPage, Jn as searchReviewItems, Nn as getPlugins, Qn as canActOnReviewItem, Sn as createUserTokenStore, Sr as create, Vn as countReviewItemsNeedingUser, Xa as lookupSlackUser, Za as createPluginCache, _i as workspaceRepoCheckoutPath, _r as WorkspaceValidationError, ar as reviewItemReaderForUser, bn as isExperimentalFeatureEnabled, dr as deleteWorkspace, fr as getWorkspace, gr as updateWorkspace, hi as SANDBOX_RUNTIME, hr as listWorkspacesByRepository, mr as listWorkspaces, qn as listWaitingConversationIds, tr as isOnlyAssignee, ur as createWorkspace, wa as readConversationAccessFromSql } from "./projection-BOpi891B.js";
2
+ import { m as escapeXml } from "./client-DhqbV0Fe.js";
3
+ import { m as logInfo, p as logException } from "./logging-CNs1e04n.js";
4
+ import { C as getSqlExecutor, D as loadConversationSlackTenantIds, S as getDb, c as readViewerAvatarUrl, d as updateViewerDisplayName, l as resolveViewerUser, m as readAvatarImage, t as findUserByEmail, x as getConversationStore } from "./viewer-C6Y1aXjZ.js";
5
5
  import { c as juniorIdentities, d as juniorDestinations, l as juniorUsers, s as juniorConversations } from "./agent-invocations-C1RNOCs8.js";
6
- import { C as juniorCodeRepositories, S as juniorCodeChanges, b as juniorApiTokens, m as juniorConversationParticipants, v as juniorConversationMetrics, y as juniorConversationEvents } from "./executor-C2cRw0hZ.js";
6
+ import { C as juniorCodeChanges, b as juniorConversationEvents, w as juniorCodeRepositories, x as juniorApiTokens, y as juniorConversationMetrics } from "./executor-BafgvQcc.js";
7
7
  import { t as INPUT_IMAGE_TYPES } from "./media-U6Tuz6Mi.js";
8
- import { n as pluginCatalogRuntime } from "./catalog-runtime-Bw9kjGVu.js";
9
- import { B as activateViewerAutomationVersion, Ct as conversationSubtreeColumns, G as ViewerTaskNotFoundError, H as deleteViewerTask, J as readViewerAutomationRuns, K as readConversationSourceTask, Ot as readUserCustomization, Q as AutomationEditError, St as conversationSubtree, U as readViewerAutomationEdit, V as changeViewerAutomationLifecycle, W as updateViewerAutomation, X as readViewerAutomationVersions, Y as readViewerAutomationSummary, Z as readViewerAutomations, bt as listChildAgents, dt as decodeInputImages, et as listOwnedWatches, it as appendAndEnqueueWebMessage, kt as saveUserCustomization, lt as webActorFromEmail, m as readStats, ot as createAndEnqueueConversation, pt as readLiveAttachment, q as readViewerAutomationExecutions, rt as acceptsWebMessages, x as createSlackReviewItemShow } from "./agent-runner-BiHsH8O_.js";
10
- import { $t as workspaceRepoCheckoutPath, Ar as lookupSlackUser, D as listWaitingConversationIds, Er as listConversationAnnotations, F as isOnlyAssignee, G as deleteWorkspace, J as listWorkspaces, K as getWorkspace, M as canActOnReviewItem, O as searchReviewItems, W as createWorkspace, X as updateWorkspace, Y as listWorkspacesByRepository, Z as WorkspaceValidationError, Zt as SANDBOX_RUNTIME, _r as readConversationEventPage, ar as createPluginLogger, gr as readLatestConversationBrief, jr as createPluginCache, n as createUserTokenStore, nt as create, or as readConversationAccessFromSql, p as getPlugins, rt as hash, vr as buildSentryConversationUrl, x as countReviewItemsNeedingUser, z as reviewItemReaderForUser } from "./credentialed-mh7HBqey.js";
11
- import { r as getStateAdapter } from "./adapter-Bqbtr1RV.js";
12
- import { A as createConversationMessageBodySchema, M as forkConversationResponseSchema, N as steerConversationPendingMessagesBodySchema, P as steerConversationPendingMessagesResponseSchema, W as readMessageAttachments, _ as conversationParamsSchema, a as cancelConversationPendingMessagesBodySchema, c as conversationAttachmentParamsSchema, d as conversationDetailReportSchema, g as conversationFeedSchema, h as conversationFeedQuerySchema, i as archiveConversationResponseSchema, j as forkConversationBodySchema, k as createConversationBodySchema, m as conversationEventsQuerySchema, o as cancelConversationPendingMessagesResponseSchema, p as conversationEventPageSchema, r as archiveConversationBodySchema, t as acceptedConversationMessageSchema, u as conversationDetailQuerySchema, x as conversationPendingMessagesReportSchema } from "./conversation-AhOVzN_k.js";
13
- import { h as getChildAgentLabel } from "./store-Di0E4EvU.js";
14
- import { d as getEventCatalog } from "./session-stop-DuUB1rwQ.js";
8
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-TygDsPOU.js";
9
+ import { $ as AutomationEditError, At as saveUserCustomization, G as updateViewerAutomation, H as changeViewerAutomationLifecycle, J as readViewerAutomationExecutions, K as ViewerTaskNotFoundError, Q as readViewerAutomations, U as deleteViewerTask, V as activateViewerAutomationVersion, W as readViewerAutomationEdit, X as readViewerAutomationSummary, Y as readViewerAutomationRuns, Z as readViewerAutomationVersions, at as appendAndEnqueueWebMessage, ft as decodeInputImages, it as acceptsWebMessages, kt as readUserCustomization, m as readStats, mt as readLiveAttachment, st as createAndEnqueueConversation, tt as listOwnedWatches, ut as webActorFromEmail, x as createSlackReviewItemShow, xt as listChildAgents } from "./agent-runner-D7Qz-lGO.js";
10
+ import { r as getStateAdapter } from "./adapter-umap1cth.js";
11
+ import { A as createConversationMessageBodySchema, G as readMessageAttachments, I as conversationReadPositionSchema, M as forkConversationResponseSchema, N as steerConversationPendingMessagesBodySchema, P as steerConversationPendingMessagesResponseSchema, _ as conversationParamsSchema, a as cancelConversationPendingMessagesBodySchema, c as conversationAttachmentParamsSchema, d as conversationDetailReportSchema, g as conversationFeedSchema, h as conversationFeedQuerySchema, i as archiveConversationResponseSchema, j as forkConversationBodySchema, k as createConversationBodySchema, m as conversationEventsQuerySchema, o as cancelConversationPendingMessagesResponseSchema, p as conversationEventPageSchema, r as archiveConversationBodySchema, t as acceptedConversationMessageSchema, u as conversationDetailQuerySchema, x as conversationPendingMessagesReportSchema } from "./conversation-T4oabbBi.js";
12
+ import { d as getEventCatalog } from "./session-stop-DaT_7tYa.js";
15
13
  import { S as userCustomizationSchema, a as fillUtcSixHours, b as webMessageId, c as trailingUtcDayWindow, d as utcDayKey, f as utcHourKey, i as fillUtcHours, l as trailingUtcHourWindow, n as HOUR_MS$1, o as startOfUtcHour, r as fillUtcDays, s as sumUtcHoursIntoSixHours, t as DAY_MS$1, u as trailingUtcSixHourWindow } from "./reporting-window-DgHgwQB3.js";
16
- import { s as getSandboxResources, t as getCachedSnapshot } from "./resolve-D44G1jJm.js";
17
- import { a as getVercelConversationWorkQueue } from "./job-queue-CEeLYkSj.js";
18
- import { E as steerWebPendingMessages, a as cancelHumanFacingPendingMessages, g as getConversationWorkState, h as getConversation } from "./store-BF2XEeQz.js";
19
- import { n as finishServiceAccountSignIn, o as serviceAccountPlugins, r as getServiceAccountStatus, s as startServiceAccountSignIn, t as disconnectServiceAccount } from "./service-account-CHO3YXpt.js";
20
- import { N as ScheduleIntentError, P as compileScheduleIntent, _ as automationSummarySchema, b as automationVersionListSchema, c as automationLifecycleSchema, d as automationParamsSchema, f as automationRunListSchema, g as automationSchedulePreviewSchema, h as automationScheduleIntentSchema, i as automationExecutionListSchema, l as automationListQuerySchema, n as automationEditSchema, r as automationEventCatalogSchema, t as automationEditErrorSchema, u as automationListSchema, v as automationUpdateSchema, x as automationVersionParamsSchema, y as automationVersionActivateSchema } from "./automation-Dwh_xcqM.js";
21
- import { c as runtimeInfoReportSchema, n as pluginOperationalReportFeedSchema, s as pluginsSchema, t as healthReportSchema, u as skillReportsSchema } from "./reporting-schema-CZREhYc-.js";
22
- import { B as actOnReviewItem, C as isAllowedSignInEmail, H as readReviewItemForUser, J as readHealthReport, N as stopConversationTurn, O as clearSlackPendingReactions, R as parseContent, V as closeReviewItemsAnsweredByReply, W as getAttachmentStorage, a as resolveMentions, c as readConversationAuxiliaryCostsFromSql, d as notCopiedByFork, f as childAgentTotalsReport, g as resolveSlackTeamDomains, h as conversationSummaryFromStoredConversation, i as attachConversationMentions, l as ConversationForkError, m as conversationEventHistory, n as readConversationRecordFromSql, o as readFirstHumanMessageTexts, p as readConversationTreeMetricsFromSql, r as readConversationParticipants, s as listConversationSidebarAnnotations, t as readConversationFeed, u as forkConversation, y as getProductionSlackAdapter, z as ReviewItemActionError } from "./list-DjJJ5K0x.js";
23
- import { d as legacyWebMailboxMetadataSchema } from "./result-CvP8c5bb.js";
24
- import { a as startConnection, l as getWebAuthorization, n as isConnectableProvider, r as listConnections, t as disconnectConnection, u as htmlCallbackResponse } from "./connections-BzN2K0G9.js";
25
- import { A as avatarParamsSchema, C as createdPersonalTokenSchema, D as revokePersonalTokenResponseSchema, E as personalTokenParamsSchema, F as locationDirectoryReportSchema, G as startConnectionResponseSchema, H as connectionParamsSchema, I as locationParamsSchema, L as activityDays$1, M as personalSpendReportSchema, O as actorDirectoryReportSchema, P as locationDetailReportSchema, Q as codeRepositoryReportSchema, R as activityHours$1, S as createPersonalTokenBodySchema, V as connectionListSchema, W as disconnectConnectionResponseSchema, X as codePersonReportSchema, Y as codeOverviewReportSchema, Z as codeRepositoryParamsSchema, _ as disconnectServiceAccountResponseSchema, a as workspaceBodySchema, at as conversationStatsReportSchema, d as reviewItemCountSchema, f as reviewItemListQuerySchema, g as reviewItemWaitingConversationsSchema, h as reviewItemSchema, i as deleteWorkspaceResponseSchema, it as stopConversationTurnResponseSchema, j as personParamsSchema, k as actorProfileReportSchema, l as workspaceSchema, m as reviewItemParamsSchema, n as statsReportSchema, o as workspaceListSchema, ot as apiErrorSchema, p as reviewItemListSchema, rt as conversationChildAgentsReportSchema, s as workspaceParamsSchema, tt as conversationWatchesReportSchema, u as reviewItemActionSchema, v as serviceAccountListSchema, w as personalTokenListSchema, x as startServiceAccountSignInResponseSchema, y as serviceAccountParamsSchema, z as activitySixHours$1 } from "./schema-DKZgO-xf.js";
14
+ import { s as getSandboxResources, t as getCachedSnapshot } from "./resolve-DqVtd4Q9.js";
15
+ import { a as getVercelConversationWorkQueue } from "./job-queue-C4lphW9Y.js";
16
+ import { E as steerWebPendingMessages, a as cancelHumanFacingPendingMessages, g as getConversationWorkState, h as getConversation } from "./store-DaxUuOmD.js";
17
+ import { F as conversationRangeColumns, N as conversationActiveDaysColumn, P as conversationAggregateColumns, ct as AutomationConflictError, ft as automationEventCatalog, j as readAutomationTitlesByConversationId } from "./turn-session-routing-BdCTDq9m.js";
18
+ import { n as finishServiceAccountSignIn, o as serviceAccountPlugins, r as getServiceAccountStatus, s as startServiceAccountSignIn, t as disconnectServiceAccount } from "./service-account-smdPFWNy.js";
19
+ import { N as ScheduleIntentError, P as compileScheduleIntent, _ as automationSummarySchema, b as automationVersionListSchema, c as automationLifecycleSchema, d as automationParamsSchema, f as automationRunListSchema, g as automationSchedulePreviewSchema, h as automationScheduleIntentSchema, i as automationExecutionListSchema, l as automationListQuerySchema, n as automationEditSchema, r as automationEventCatalogSchema, t as automationEditErrorSchema, u as automationListSchema, v as automationUpdateSchema, x as automationVersionParamsSchema, y as automationVersionActivateSchema } from "./automation-yffXcZt9.js";
20
+ import { c as pluginsSchema, d as skillReportsSchema, l as runtimeInfoReportSchema, n as healthReportSchema, r as pluginOperationalReportFeedSchema, t as apiErrorSchema } from "./common-Eu6sx09c.js";
21
+ import { C as getProductionSlackAdapter, D as isAllowedSignInEmail, G as closeReviewItemsAnsweredByReply, H as parseContent, J as getAttachmentStorage, K as readReviewItemForUser, M as clearSlackPendingReactions, Q as readHealthReport, U as ReviewItemActionError, W as actOnReviewItem, _ as archiveConversation, a as readConversationFeed, b as throwApiError, c as childAgentTotalsReport, d as conversationSummaryFromStoredConversation, f as attachConversationMentions, g as encodeConversationCursor, h as decodeConversationCursor, i as forkConversationForViewer, l as readConversationTreeMetricsFromSql, m as readFirstHumanMessageTexts, n as readConversationDetail, o as readConversationRecordFromSql, p as resolveMentions, r as markConversationRead, s as readConversationAuxiliaryCostsFromSql, t as stopConversationTurnForViewer, u as conversationEventHistory, v as emptyResponse, y as jsonResponse } from "./stop-dKUc-x2K.js";
22
+ import { f as legacyWebMailboxMetadataSchema } from "./result-DQwTiLTw.js";
23
+ import { a as startConnection, l as getWebAuthorization, n as isConnectableProvider, r as listConnections, t as disconnectConnection, u as htmlCallbackResponse } from "./connections-Dq72KajR.js";
24
+ import { A as avatarParamsSchema, C as createdPersonalTokenSchema, D as revokePersonalTokenResponseSchema, E as personalTokenParamsSchema, F as locationDirectoryReportSchema, G as startConnectionResponseSchema, H as connectionParamsSchema, I as locationParamsSchema, L as activityDays$1, M as personalSpendReportSchema, O as actorDirectoryReportSchema, P as locationDetailReportSchema, Q as codeRepositoryReportSchema, R as activityHours$1, S as createPersonalTokenBodySchema, V as connectionListSchema, W as disconnectConnectionResponseSchema, X as codePersonReportSchema, Y as codeOverviewReportSchema, Z as codeRepositoryParamsSchema, _ as disconnectServiceAccountResponseSchema, a as workspaceBodySchema, at as conversationStatsReportSchema, d as reviewItemCountSchema, f as reviewItemListQuerySchema, g as reviewItemWaitingConversationsSchema, h as reviewItemSchema, i as deleteWorkspaceResponseSchema, it as stopConversationTurnResponseSchema, j as personParamsSchema, k as actorProfileReportSchema, l as workspaceSchema, m as reviewItemParamsSchema, n as statsReportSchema, o as workspaceListSchema, p as reviewItemListSchema, rt as conversationChildAgentsReportSchema, s as workspaceParamsSchema, tt as conversationWatchesReportSchema, u as reviewItemActionSchema, v as serviceAccountListSchema, w as personalTokenListSchema, x as startServiceAccountSignInResponseSchema, y as serviceAccountParamsSchema, z as activitySixHours$1 } from "./schema-5xHDGlBW.js";
26
25
  import { Hono } from "hono";
27
26
  import path from "node:path";
27
+ import { createHash, randomBytes, randomUUID } from "node:crypto";
28
28
  import { z } from "zod";
29
- import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
30
- import { and, asc, desc, eq, gt, gte, isNotNull, isNull, lte, notExists, or, sql } from "drizzle-orm";
29
+ import { and, asc, desc, eq, gt, gte, isNull, lte, notExists, or, sql } from "drizzle-orm";
31
30
  import { pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema } from "@sentry/junior-plugin-api";
32
31
  import { alias } from "drizzle-orm/pg-core";
33
32
  import { readFileSync } from "node:fs";
34
- import { bodyLimit } from "hono/body-limit";
35
33
  import { HTTPException } from "hono/http-exception";
34
+ import { bodyLimit } from "hono/body-limit";
36
35
  import { zValidator } from "@hono/zod-validator";
37
36
  import { createMiddleware } from "hono/factory";
38
- // src/api/http.ts
39
- const NO_STORE_HEADERS = { "cache-control": "no-store" };
40
- /** Build response init that keeps personalized API payloads out of shared caches. */
41
- function noStoreInit(init) {
42
- if (!init) return { headers: NO_STORE_HEADERS };
43
- const headers = new Headers(init.headers);
44
- if (!headers.has("cache-control")) headers.set("cache-control", "no-store");
45
- return {
46
- ...init,
47
- headers
48
- };
49
- }
50
- /** Serialize a REST response only after it satisfies its declared schema. */
51
- function jsonResponse(schema, value, init) {
52
- return Response.json(schema.parse(value), noStoreInit(init));
53
- }
54
- /** Return an empty personalized API response with no-store caching. */
55
- function emptyResponse(status = 204) {
56
- return new Response(null, noStoreInit({ status }));
57
- }
58
- /** Stop a request with Junior's stable JSON error contract. */
59
- function throwApiError(status, message, cause) {
60
- throw new HTTPException(status, {
61
- ...cause === void 0 ? void 0 : { cause },
62
- message,
63
- res: jsonResponse(apiErrorSchema, { error: message }, { status })
64
- });
65
- }
66
37
  // src/api/validation.ts
67
38
  /** Validate one request input and use Junior's stable JSON error contract. */
68
39
  function validateRequest(target, schema, message) {
@@ -83,61 +54,6 @@ const requireAdmin = createMiddleware(async (context, next) => {
83
54
  if (viewer.isAdmin !== true) throwApiError(403, "Only Junior admins can do this.");
84
55
  await next();
85
56
  });
86
- // src/api/conversations/archive.ts
87
- async function setViewerArchive(args) {
88
- const executor = getSqlExecutor();
89
- return await executor.transaction(async () => {
90
- const db = executor.db();
91
- const [root] = await db.select({ lastActivityAt: juniorConversations.lastActivityAt }).from(juniorConversations).where(eq(juniorConversations.conversationId, args.rootConversationId)).limit(1).for("update");
92
- if (!root) return { status: "missing" };
93
- if (args.archived) {
94
- if (root.lastActivityAt.getTime() !== Date.parse(args.lastSeenAt)) return { status: "conflict" };
95
- const archivedAt = /* @__PURE__ */ new Date();
96
- const lastMessageAt = new Date(args.lastSeenAt);
97
- await db.insert(juniorConversationParticipants).values({
98
- archivedAt,
99
- lastMessageAt,
100
- rootConversationId: args.rootConversationId,
101
- userId: args.userId
102
- }).onConflictDoUpdate({
103
- target: [juniorConversationParticipants.userId, juniorConversationParticipants.rootConversationId],
104
- set: {
105
- archivedAt,
106
- lastMessageAt: sql`greatest(${juniorConversationParticipants.lastMessageAt}, excluded.last_message_at)`
107
- }
108
- });
109
- return {
110
- archivedAt: archivedAt.toISOString(),
111
- status: "updated"
112
- };
113
- }
114
- await db.update(juniorConversationParticipants).set({ archivedAt: null }).where(and(eq(juniorConversationParticipants.userId, args.userId), eq(juniorConversationParticipants.rootConversationId, args.rootConversationId), isNotNull(juniorConversationParticipants.archivedAt)));
115
- return {
116
- archivedAt: null,
117
- status: "updated"
118
- };
119
- });
120
- }
121
- /** Read one viewer's archive timestamp for a conversation root. */
122
- async function readConversationArchivedAt(viewer, conversationId) {
123
- const rootConversationId = await resolveRootConversationId(getSqlExecutor(), conversationId);
124
- const [row] = await getDb().select({ archivedAt: juniorConversationParticipants.archivedAt }).from(juniorConversationParticipants).where(and(eq(juniorConversationParticipants.userId, viewer.id), eq(juniorConversationParticipants.rootConversationId, rootConversationId))).limit(1);
125
- return row?.archivedAt?.getTime();
126
- }
127
- /** Archive or restore one conversation for the signed-in viewer. */
128
- async function archiveConversation(viewer, conversationId, body) {
129
- if (!(await readConversationAccessFromSql(getDb(), [conversationId], viewer)).get(conversationId)?.isParticipant) throwApiError(404, "Conversation not found.");
130
- const rootConversationId = await resolveRootConversationId(getSqlExecutor(), conversationId);
131
- const result = await setViewerArchive({
132
- archived: body.archived,
133
- lastSeenAt: body.lastSeenAt,
134
- rootConversationId,
135
- userId: viewer.id
136
- });
137
- if (result.status === "updated") return { archivedAt: result.archivedAt };
138
- if (result.status === "missing") throwApiError(404, "Conversation not found.");
139
- throwApiError(409, "Conversation received new activity.");
140
- }
141
57
  // src/api/conversations/child-agents.ts
142
58
  /**
143
59
  * Load the direct child agents of one Conversation, or throw a standard API
@@ -303,370 +219,6 @@ async function appendConversationMessageForViewer(viewer, conversationId, body,
303
219
  throwApiError(500, "Unable to append message.", error);
304
220
  }
305
221
  }
306
- // src/chat/pi/sql-model-usage.ts
307
- /** SQL reporting adapter for usage stored on assistant history events. */
308
- const message = sql`${juniorConversationEvents.payload}`;
309
- const usage = sql`${message}->'usage'`;
310
- const cost = sql`${usage}->'cost'`;
311
- function nonnegativeNumber(source, field, fallbackField, floor = false) {
312
- const value = (name) => sql`case
313
- when jsonb_typeof(${source}->${name}) = 'number'
314
- and (${source}->>${name})::numeric >= 0
315
- then ${floor ? sql`floor((${source}->>${name})::numeric)` : sql`(${source}->>${name})::numeric`}
316
- end`;
317
- const primary = value(field);
318
- return fallbackField ? sql`coalesce(${primary}, ${value(fallbackField)})` : sql`${primary}`;
319
- }
320
- function token(field, fallbackField) {
321
- return nonnegativeNumber(usage, field, fallbackField, true);
322
- }
323
- function summed(value) {
324
- return sql`sum(${value})::double precision`;
325
- }
326
- function summedCost(value) {
327
- return sql`round(sum(${value}), 12)::double precision`;
328
- }
329
- function messageTotalTokens(args) {
330
- const { input, output, cacheRead, cacheWrite } = args;
331
- return sql`case
332
- when ${input} is not null
333
- or ${output} is not null
334
- or ${cacheRead} is not null
335
- or ${cacheWrite} is not null
336
- then coalesce(${input}, 0)
337
- + coalesce(${output}, 0)
338
- + coalesce(${cacheRead}, 0)
339
- + coalesce(${cacheWrite}, 0)
340
- else ${token("totalTokens")}
341
- end`;
342
- }
343
- function definedNumber(value) {
344
- return value === null ? void 0 : value;
345
- }
346
- function usageFromRow(row) {
347
- const components = {
348
- inputTokens: definedNumber(row.inputTokens),
349
- outputTokens: definedNumber(row.outputTokens),
350
- cachedInputTokens: definedNumber(row.cachedInputTokens),
351
- cacheCreationTokens: definedNumber(row.cacheCreationTokens)
352
- };
353
- const costFields = {
354
- input: definedNumber(row.costInput),
355
- output: definedNumber(row.costOutput),
356
- cacheRead: definedNumber(row.costCacheRead),
357
- cacheWrite: definedNumber(row.costCacheWrite),
358
- total: definedNumber(row.costTotal)
359
- };
360
- const definedCost = Object.fromEntries(Object.entries(costFields).filter(([, value]) => value !== void 0));
361
- const result = {
362
- ...Object.fromEntries(Object.entries(components).filter(([, value]) => value !== void 0)),
363
- ...row.reasoningTokens === null ? void 0 : { reasoningTokens: row.reasoningTokens },
364
- ...row.totalTokens === null ? void 0 : { totalTokens: row.totalTokens },
365
- ...Object.keys(definedCost).length > 0 ? { cost: definedCost } : void 0
366
- };
367
- return hasAgentTurnUsage(result) ? agentTurnUsageSchema.parse(result) : void 0;
368
- }
369
- /**
370
- * Aggregate real assistant calls by provider/model in SQL, for one
371
- * Conversation and all its child agents at any depth. Replayed history is not
372
- * another call; its tokens appear in the next call's input usage.
373
- */
374
- async function readConversationModelUsageFromSql(executor, options) {
375
- const modelId = sql`case
376
- when position('/' in coalesce(${message}->>'model', '')) > 0
377
- then ${message}->>'model'
378
- else concat(${message}->>'provider', '/', ${message}->>'model')
379
- end`;
380
- const inputTokens = token("input", "inputTokens");
381
- const outputTokens = token("output", "outputTokens");
382
- const cachedInputTokens = token("cacheRead", "cachedInputTokens");
383
- const cacheCreationTokens = token("cacheWrite", "cacheCreationTokens");
384
- return (await executor.db().select({
385
- modelId,
386
- inputTokens: summed(inputTokens),
387
- outputTokens: summed(outputTokens),
388
- cachedInputTokens: summed(cachedInputTokens),
389
- cacheCreationTokens: summed(cacheCreationTokens),
390
- reasoningTokens: summed(token("reasoning", "reasoningTokens")),
391
- totalTokens: summed(messageTotalTokens({
392
- input: inputTokens,
393
- output: outputTokens,
394
- cacheRead: cachedInputTokens,
395
- cacheWrite: cacheCreationTokens
396
- })),
397
- costInput: summedCost(nonnegativeNumber(cost, "input")),
398
- costOutput: summedCost(nonnegativeNumber(cost, "output")),
399
- costCacheRead: summedCost(nonnegativeNumber(cost, "cacheRead")),
400
- costCacheWrite: summedCost(nonnegativeNumber(cost, "cacheWrite")),
401
- costTotal: summedCost(nonnegativeNumber(cost, "total"))
402
- }).from(juniorConversationEvents).innerJoin(conversationSubtree([options.conversationId]), eq(conversationSubtreeColumns.conversationId, juniorConversationEvents.conversationId)).where(and(eq(juniorConversationEvents.type, "assistant_message"), notCopiedByFork(), sql`jsonb_typeof(${message}->'provider') = 'string'`, sql`jsonb_typeof(${message}->'model') = 'string'`, sql`coalesce(${message}->>'provider', '') <> ''`, sql`coalesce(${message}->>'model', '') <> ''`)).groupBy(modelId).orderBy(modelId)).flatMap((row) => {
403
- const modelUsage = usageFromRow(row);
404
- return modelUsage ? [{
405
- modelId: row.modelId,
406
- usage: modelUsage
407
- }] : [];
408
- });
409
- }
410
- // src/api/conversations/cursor.ts
411
- const CURSOR_CONTEXT = "junior-conversation-report-cursor";
412
- const conversationCursorPayloadSchema = z.object({
413
- conversationId: z.string().min(1),
414
- seq: z.number().int().min(-1),
415
- version: z.literal(1)
416
- }).strict();
417
- function cursorSecret() {
418
- const secret = process.env.JUNIOR_SECRET?.trim();
419
- if (!secret) throw new Error("Cannot sign conversation cursor without JUNIOR_SECRET");
420
- return secret;
421
- }
422
- /** Sign one encoded cursor with the domain-separated server secret. */
423
- function signature(payload) {
424
- return createHmac("sha256", cursorSecret()).update(`${CURSOR_CONTEXT}:${payload}`).digest("base64url");
425
- }
426
- /** Encode an authenticated, conversation-bound event position for an API cursor. */
427
- function encodeConversationCursor(payload) {
428
- const encoded = Buffer.from(JSON.stringify({
429
- ...payload,
430
- version: 1
431
- }), "utf8").toString("base64url");
432
- return `${encoded}.${signature(encoded)}`;
433
- }
434
- /** Decode an authenticated reporting cursor for the expected conversation. */
435
- function decodeConversationCursor(args) {
436
- const [encoded, actualSignature, extra] = args.cursor.split(".");
437
- if (!encoded || !actualSignature || extra !== void 0) return void 0;
438
- const expected = Buffer.from(signature(encoded));
439
- const actual = Buffer.from(actualSignature);
440
- if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) return;
441
- try {
442
- const parsed = conversationCursorPayloadSchema.parse(JSON.parse(Buffer.from(encoded, "base64url").toString("utf8")));
443
- if (parsed.conversationId !== args.conversationId) return;
444
- return { seq: parsed.seq };
445
- } catch {
446
- return;
447
- }
448
- }
449
- // src/api/conversations/fork.ts
450
- /** Fork a Conversation that the viewer can read into a new root they own. */
451
- async function forkConversationForViewer(viewer, sourceConversationId, body) {
452
- if (!(await readConversationAccessFromSql(getDb(), [sourceConversationId], viewer)).get(sourceConversationId)?.canViewPrivateContent) throwApiError(403, "You do not have access to this conversation.");
453
- try {
454
- return await forkConversation({
455
- sourceConversationId,
456
- ...body,
457
- actorEmail: viewer.email,
458
- ...viewer.displayName ? { actorName: viewer.displayName } : void 0
459
- });
460
- } catch (error) {
461
- if (error instanceof ConversationForkError) throwApiError(409, error.message);
462
- throw error;
463
- }
464
- }
465
- /**
466
- * Read fork state for the viewer: whether they can fork, and the source and
467
- * fork links that they can open.
468
- */
469
- async function readConversationForks(conversationId, viewer) {
470
- const db = getDb();
471
- const [row] = await db.select({
472
- forkedFrom: juniorConversations.forkedFromConversationId,
473
- parent: juniorConversations.parentConversationId
474
- }).from(juniorConversations).where(eq(juniorConversations.conversationId, conversationId));
475
- const forks = await db.select({ conversationId: juniorConversations.conversationId }).from(juniorConversations).where(eq(juniorConversations.forkedFromConversationId, conversationId)).orderBy(asc(juniorConversations.createdAt)).limit(50);
476
- const forkedFrom = row?.forkedFrom ?? void 0;
477
- const ids = forks.map((fork) => fork.conversationId);
478
- const access = await readConversationAccessFromSql(db, [
479
- conversationId,
480
- ...ids,
481
- ...forkedFrom ? [forkedFrom] : []
482
- ], viewer);
483
- const canOpen = (id) => access.get(id)?.canViewPrivateContent;
484
- if (!canOpen(conversationId)) return {
485
- canFork: false,
486
- forks: []
487
- };
488
- const result = {
489
- canFork: !row?.parent,
490
- forks: ids.filter(canOpen)
491
- };
492
- if (forkedFrom && canOpen(forkedFrom)) {
493
- const [source] = await db.select({ title: juniorConversations.title }).from(juniorConversations).where(eq(juniorConversations.conversationId, forkedFrom));
494
- if (source) {
495
- result.forkedFromConversationId = forkedFrom;
496
- if (source.title) result.forkedFromTitle = source.title;
497
- }
498
- }
499
- return result;
500
- }
501
- // src/api/conversations/agent-tree.ts
502
- /** Bound the parent chain that one agent tree reads. */
503
- const MAX_AGENT_TREE_DEPTH = 16;
504
- const CHILD_LABEL = "Subagent";
505
- async function readParentConversationId(conversationId) {
506
- const [row] = await getDb().select({ parentId: juniorConversations.parentConversationId }).from(juniorConversations).where(eq(juniorConversations.conversationId, conversationId));
507
- return row?.parentId ?? void 0;
508
- }
509
- /**
510
- * Read the agent tree of a child Conversation: its parent chain, root first,
511
- * with the child last. Returns undefined for a root Conversation. Each
512
- * Conversation has its own access check, so a viewer sees a label only where
513
- * they can see private content.
514
- */
515
- async function readConversationAgentTree(conversationId, viewer) {
516
- const ids = [conversationId];
517
- let parentId = await readParentConversationId(conversationId);
518
- while (parentId && !ids.includes(parentId) && ids.length < MAX_AGENT_TREE_DEPTH) {
519
- ids.unshift(parentId);
520
- parentId = await readParentConversationId(parentId);
521
- }
522
- if (ids.length < 2) return;
523
- const root = parentId ? void 0 : await getConversationStore().get({ conversationId: ids[0] });
524
- const [access, automationTitleByConversation, firstHumanMessageTextByConversation] = await Promise.all([
525
- readConversationAccessFromSql(getDb(), ids, viewer),
526
- readAutomationTitlesByConversationId(root ? [root] : []),
527
- readFirstHumanMessageTexts(getDb(), root ? [root] : [])
528
- ]);
529
- return await Promise.all(ids.map(async (id) => {
530
- const itemAccess = access.get(id);
531
- if (root?.conversationId === id) return {
532
- conversationId: id,
533
- label: conversationSummaryFromStoredConversation({
534
- access: itemAccess,
535
- automationTitle: automationTitleByConversation.get(id),
536
- conversation: root,
537
- durationMs: 0,
538
- firstHumanMessageText: firstHumanMessageTextByConversation.get(id)
539
- }).displayTitle
540
- };
541
- if (!itemAccess?.canViewPrivateContent) return {
542
- conversationId: id,
543
- label: CHILD_LABEL
544
- };
545
- const agent = await getChildAgentLabel(id);
546
- return {
547
- conversationId: id,
548
- label: agent?.title ?? agent?.agentName ?? CHILD_LABEL
549
- };
550
- }));
551
- }
552
- // src/api/conversations/detail.ts
553
- /** Project stored metadata and a bounded event page into a signed history cursor. */
554
- function projectConversationDetail(args) {
555
- const { conversation } = args;
556
- const conversationId = conversation.conversationId;
557
- const transcriptPurgedAtMs = conversation.transcriptPurgedAtMs;
558
- const canExposePayload = args.access?.canViewPrivateContent ?? false;
559
- const modelUsage = transcriptPurgedAtMs === void 0 ? args.modelUsage : [];
560
- const sentryConversationUrl = buildSentryConversationUrl(conversationId);
561
- return {
562
- ...conversationSummaryFromStoredConversation({
563
- access: args.access,
564
- ...args.archivedAtMs === void 0 ? void 0 : { archivedAtMs: args.archivedAtMs },
565
- automationTitle: args.automationTitle,
566
- auxiliaryCosts: args.auxiliaryCosts,
567
- ...args.childAgentTotals ? { childAgentTotals: args.childAgentTotals } : void 0,
568
- conversation,
569
- durationMs: args.durationMs,
570
- firstHumanMessageText: args.firstHumanMessageText,
571
- ...args.locationId ? { locationId: args.locationId } : void 0,
572
- ...args.teamDomainByTeamId ? { teamDomainByTeamId: args.teamDomainByTeamId } : void 0,
573
- usage: args.usage
574
- }),
575
- annotations: canExposePayload ? args.annotations : [],
576
- sidebarAnnotations: canExposePayload ? args.sidebarAnnotations : void 0,
577
- ...canExposePayload && args.brief ? { brief: args.brief } : void 0,
578
- events: args.events,
579
- ...canExposePayload && args.participants.length > 0 ? { participants: args.participants } : void 0,
580
- ...args.previousSeq !== void 0 ? { previousCursor: encodeConversationCursor({
581
- conversationId,
582
- seq: args.previousSeq
583
- }) } : void 0,
584
- ...modelUsage.length > 0 ? { modelUsage } : void 0,
585
- eventHistory: conversationEventHistory({
586
- canExposePayload,
587
- ...transcriptPurgedAtMs === void 0 ? void 0 : { transcriptPurgedAtMs }
588
- }),
589
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
590
- ...sentryConversationUrl ? { sentryConversationUrl } : void 0,
591
- ...args.sourceTask ? { sourceTask: args.sourceTask } : void 0
592
- };
593
- }
594
- async function readConversationDetailFromSql(conversationId, options) {
595
- const record = await readConversationRecordFromSql(conversationId);
596
- if (!record) return void 0;
597
- const archivedAtMs = options.viewer ? await readConversationArchivedAt(options.viewer, conversationId) : void 0;
598
- const executor = getSqlExecutor();
599
- const [accessByConversation, annotations, automationTitleByConversation, auxiliaryCostsByConversation, briefVersion, firstHumanMessageTextByConversation, modelUsage, metricsByConversation, participantsByConversation, sourceTask, teamDomainByTeamId] = await Promise.all([
600
- readConversationAccessFromSql(getDb(), [conversationId], options.viewer),
601
- listConversationAnnotations(getDb(), conversationId),
602
- readAutomationTitlesByConversationId([record.conversation]),
603
- readConversationAuxiliaryCostsFromSql(getDb(), [conversationId]),
604
- readLatestConversationBrief(getDb(), conversationId),
605
- readFirstHumanMessageTexts(getDb(), [record.conversation]),
606
- record.conversation.transcriptPurgedAtMs === void 0 ? readConversationModelUsageFromSql(executor, { conversationId }) : Promise.resolve([]),
607
- readConversationTreeMetricsFromSql(getDb(), [conversationId]),
608
- readConversationParticipants(getDb(), [conversationId]),
609
- readConversationSourceTask({
610
- conversationId,
611
- ...options.viewer ? { viewer: options.viewer } : void 0
612
- }),
613
- resolveSlackTeamDomains(record.conversation.location?.provider === "slack" ? [record.conversation.location.teamId] : [])
614
- ]);
615
- const access = accessByConversation.get(conversationId);
616
- const sidebarAnnotations = access?.canViewPrivateContent ? (await listConversationSidebarAnnotations([conversationId], /* @__PURE__ */ new Map([[conversationId, annotations]])))[conversationId] : void 0;
617
- const page = record.conversation.transcriptPurgedAtMs === void 0 ? await readConversationEventPage(executor, {
618
- canExposePayload: access?.canViewPrivateContent ?? false,
619
- conversationId,
620
- limit: options.limit
621
- }) : { events: [] };
622
- const brief = access?.canViewPrivateContent ? briefVersion?.content : void 0;
623
- const { briefMentions, events } = await attachConversationMentions(executor, conversationId, {
624
- ...brief ? { brief } : void 0,
625
- events: page.events
626
- });
627
- const metrics = metricsByConversation.get(conversationId);
628
- const auxiliaryCosts = auxiliaryCostsByConversation.get(conversationId);
629
- const childAgentTotals = childAgentTotalsReport(metrics, auxiliaryCosts);
630
- return projectConversationDetail({
631
- ...record,
632
- access,
633
- ...archivedAtMs === void 0 ? void 0 : { archivedAtMs },
634
- automationTitle: automationTitleByConversation.get(conversationId),
635
- annotations,
636
- sidebarAnnotations,
637
- auxiliaryCosts: auxiliaryCosts?.total,
638
- ...childAgentTotals ? { childAgentTotals } : void 0,
639
- ...briefVersion ? { brief: {
640
- content: briefVersion.content,
641
- ...briefMentions.length > 0 ? { mentions: briefMentions } : void 0,
642
- updatedAt: briefVersion.createdAt.toISOString(),
643
- version: briefVersion.version
644
- } } : void 0,
645
- durationMs: metrics?.durationMs ?? record.durationMs,
646
- events,
647
- firstHumanMessageText: firstHumanMessageTextByConversation.get(conversationId),
648
- modelUsage,
649
- participants: participantsByConversation.get(conversationId) ?? [],
650
- ...sourceTask ? { sourceTask } : void 0,
651
- teamDomainByTeamId,
652
- ...page.previousSeq === void 0 ? void 0 : { previousSeq: page.previousSeq },
653
- usage: metrics?.usage ?? record.usage ?? void 0
654
- });
655
- }
656
- /** Load one conversation from its canonical event history. */
657
- async function readConversationDetail(conversationId, options = {}) {
658
- const report = await readConversationDetailFromSql(conversationId, {
659
- ...options,
660
- limit: options.limit ?? 500
661
- });
662
- if (!report) return void 0;
663
- const [forks, agentTree] = await Promise.all([readConversationForks(conversationId, options.viewer), readConversationAgentTree(conversationId, options.viewer)]);
664
- return conversationDetailReportSchema.parse({
665
- ...report,
666
- ...forks,
667
- ...agentTree ? { agentTree } : void 0
668
- });
669
- }
670
222
  // src/api/conversations/event-list.ts
671
223
  /** Read one bounded page of reporting events before a signed cursor. */
672
224
  async function readConversationEvents(conversationId, beforeValue, options = {}) {
@@ -879,8 +431,15 @@ async function readConversationPendingMessages(conversationId, options = {}) {
879
431
  actorId,
880
432
  conversationId
881
433
  }) : void 0;
882
- const work = await getConversation({ conversationId });
883
- const messages = await attachPendingMentions(conversationId, (await Promise.all((work?.execution.pendingMessages ?? []).map((message) => projectPendingMessage(message, canExposePayload)))).filter((message) => Boolean(message)));
434
+ const pendingMessages = (await getConversation({ conversationId }))?.execution.pendingMessages ?? [];
435
+ const messages = await attachPendingMentions(conversationId, (await Promise.all(pendingMessages.map((message) => projectPendingMessage(message, canExposePayload)))).flatMap((message, index) => {
436
+ if (!message) return [];
437
+ const actorIdentityId = pendingMessages[index].input.authorIdentityId;
438
+ return [canExposePayload && actorIdentityId ? {
439
+ ...message,
440
+ actorIdentityId
441
+ } : message];
442
+ }));
884
443
  return conversationPendingMessagesReportSchema.parse({
885
444
  ...authorization ? { authorization: {
886
445
  authorizationUrl: authorization.authorizationUrl,
@@ -1256,58 +815,6 @@ async function readConversationStatsFromSql() {
1256
815
  async function readConversationStats() {
1257
816
  return conversationStatsReportSchema.parse(await readConversationStatsFromSql());
1258
817
  }
1259
- // src/api/conversations/stop.ts
1260
- /**
1261
- * Stop the active Turn for one conversation participant.
1262
- *
1263
- * The stop also removes the human messages queued before it. The viewer's own
1264
- * web text comes back in the response so the dashboard can restore it to the
1265
- * composer instead of losing it.
1266
- */
1267
- async function stopConversationTurnForViewer(viewer, conversationId, services) {
1268
- const conversationStore = getConversationStore();
1269
- if (!await conversationStore.get({ conversationId })) throwApiError(404, "Conversation not found.");
1270
- if (!(await readConversationAccessFromSql(getDb(), [conversationId], viewer)).get(conversationId)?.isParticipant) throwApiError(403, "Only conversation participants can stop this turn.");
1271
- try {
1272
- const result = await stopConversationTurn({
1273
- conversationId,
1274
- conversationStore,
1275
- queue: services.queue
1276
- });
1277
- if (result.status === "no_work") return {
1278
- conversationId,
1279
- status: "no_work",
1280
- stoppedInboundMessageIds: [],
1281
- stoppedMessages: []
1282
- };
1283
- await clearSlackPendingReactions({
1284
- getSlackAdapter: getProductionSlackAdapter,
1285
- messages: result.pendingMessages
1286
- });
1287
- return {
1288
- conversationId,
1289
- status: "requested",
1290
- stoppedInboundMessageIds: result.pendingMessages.map((message) => message.inboundMessageId),
1291
- stoppedMessages: viewerWebMessages(result.pendingMessages, viewer)
1292
- };
1293
- } catch (error) {
1294
- throwApiError(500, "Unable to stop the active turn.", error);
1295
- }
1296
- }
1297
- function viewerWebMessages(messages, viewer) {
1298
- const viewerUserId = webActorFromEmail(viewer.email).userId;
1299
- return messages.flatMap((message) => {
1300
- if (message.source !== "web") return [];
1301
- const metadata = legacyWebMailboxMetadataSchema.safeParse(message.input.metadata);
1302
- if (!metadata.success) return [];
1303
- if (metadata.data.authorUserId !== viewerUserId) return [];
1304
- const text = message.input.text.trim();
1305
- return text ? [{
1306
- inboundMessageId: message.inboundMessageId,
1307
- text
1308
- }] : [];
1309
- });
1310
- }
1311
818
  // src/api/conversations/watches.ts
1312
819
  /**
1313
820
  * Load the active Watches that one Conversation owns, or throw a standard API
@@ -1386,6 +893,12 @@ function createConversationRoutes(options) {
1386
893
  const body = context.req.valid("json");
1387
894
  return jsonResponse(archiveConversationResponseSchema, await archiveConversation(viewer, conversationId, body));
1388
895
  });
896
+ app.put("/:conversationId/read-position", requireViewer, validateRequest("param", conversationParamsSchema, "Invalid route parameters."), validateRequest("json", conversationReadPositionSchema, "Invalid request body."), async (context) => {
897
+ const viewer = context.get("viewer");
898
+ const { conversationId } = context.req.valid("param");
899
+ const body = context.req.valid("json");
900
+ return jsonResponse(conversationReadPositionSchema, await markConversationRead(viewer, conversationId, body));
901
+ });
1389
902
  app.get("/:conversationId/events", validateRequest("param", conversationParamsSchema, "Invalid route parameters."), validateRequest("query", conversationEventsQuerySchema, "Invalid query parameters."), async (context) => {
1390
903
  const { conversationId } = context.req.valid("param");
1391
904
  const { before, limit } = context.req.valid("query");
@@ -2726,7 +2239,7 @@ async function readPeoplePluginReports(args) {
2726
2239
  reports: [],
2727
2240
  source: "plugins"
2728
2241
  });
2729
- const { getPluginProfileReports } = await import("./credentialed-mh7HBqey.js").then((n) => n.a);
2242
+ const { getPluginProfileReports } = await import("./projection-BOpi891B.js").then((n) => n.Tn);
2730
2243
  return pluginOperationalReportFeedSchema.parse({
2731
2244
  generatedAt: new Date(nowMs).toISOString(),
2732
2245
  reports: await getPluginProfileReports({
@@ -3016,18 +2529,28 @@ function createPersonalSpendReader() {
3016
2529
  };
3017
2530
  }
3018
2531
  // src/api/people/routes.ts
2532
+ const AVATAR_IMMUTABLE_CACHE_CONTROL = "private, max-age=31536000, immutable";
3019
2533
  /** Create the HTTP routes owned by the People API. */
3020
2534
  function createPeopleRoutes() {
3021
2535
  const app = new Hono();
3022
2536
  const readPersonalSpend = createPersonalSpendReader();
3023
2537
  app.get("/avatars/:identityId", requireViewer, validateRequest("param", avatarParamsSchema, "Invalid route parameters."), async (context) => {
3024
2538
  const { identityId } = context.req.valid("param");
3025
- const image = await readAvatarImage(getDb(), getStateAdapter(), identityId);
3026
- if (!image) throwApiError(404, "Avatar not found.");
3027
- return new Response(Buffer.from(image.data, "base64"), { headers: {
3028
- "cache-control": "private, max-age=86400",
2539
+ const avatar = await readAvatarImage(getDb(), getStateAdapter(), identityId);
2540
+ if (!avatar) throwApiError(404, "Avatar not found.");
2541
+ const etag = `"${avatar.version}"`;
2542
+ const headers = {
2543
+ "cache-control": context.req.query("v") === avatar.version ? AVATAR_IMMUTABLE_CACHE_CONTROL : "private, no-cache",
2544
+ etag
2545
+ };
2546
+ if (context.req.header("if-none-match") === etag) return new Response(null, {
2547
+ headers,
2548
+ status: 304
2549
+ });
2550
+ return new Response(Buffer.from(avatar.image.data, "base64"), { headers: {
2551
+ ...headers,
3029
2552
  "content-security-policy": "default-src 'none'; sandbox",
3030
- "content-type": image.contentType,
2553
+ "content-type": avatar.image.contentType,
3031
2554
  "x-content-type-options": "nosniff"
3032
2555
  } });
3033
2556
  });
@@ -3479,6 +3002,7 @@ const ARCHIVED_LIMIT = 50;
3479
3002
  const cursorSchema = z.object({
3480
3003
  conversationId: z.string().min(1).optional(),
3481
3004
  createdAtMs: z.number().int(),
3005
+ forUserId: z.string().min(1).optional(),
3482
3006
  id: z.string().min(1),
3483
3007
  query: z.string().min(1).optional(),
3484
3008
  version: z.literal(2)
@@ -3499,7 +3023,7 @@ function encodeCursor(item, filters) {
3499
3023
  function decodeCursor(cursor, filters) {
3500
3024
  try {
3501
3025
  const parsed = cursorSchema.parse(JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")));
3502
- if (parsed.conversationId !== filters.conversationId || parsed.query !== filters.query) return;
3026
+ if (parsed.conversationId !== filters.conversationId || parsed.forUserId !== filters.forUserId || parsed.query !== filters.query) return;
3503
3027
  return {
3504
3028
  createdAt: new Date(parsed.createdAtMs),
3505
3029
  id: parsed.id
@@ -3553,6 +3077,7 @@ function createReviewItemRoutes(services) {
3553
3077
  const query = context.req.valid("query");
3554
3078
  const filters = {
3555
3079
  ...query.conversationId ? { conversationId: query.conversationId } : void 0,
3080
+ ...query.for === "viewer" ? { forUserId: viewer.id } : void 0,
3556
3081
  ...query.q ? { query: query.q } : void 0
3557
3082
  };
3558
3083
  const after = query.cursor ? decodeCursor(query.cursor, filters) : void 0;
@@ -3742,7 +3267,7 @@ function readDescriptionText(home) {
3742
3267
  }
3743
3268
  /** Read discovered skill names for authenticated runtime diagnostics. */
3744
3269
  async function readSkillReports() {
3745
- const { discoverSkills } = await import("./skills-DHK7L9Eo.js").then((n) => n.o);
3270
+ const { discoverSkills } = await import("./skills-BbtJSF2-.js").then((n) => n.o);
3746
3271
  const skills = await discoverSkills();
3747
3272
  return skillReportsSchema.parse(skills.map((skill) => ({
3748
3273
  name: skill.name,
@@ -3751,7 +3276,7 @@ async function readSkillReports() {
3751
3276
  }
3752
3277
  /** Read safe configured plugin metadata for authenticated runtime diagnostics. */
3753
3278
  async function readPlugins() {
3754
- const { pluginCatalogRuntime } = await import("./catalog-runtime-Bw9kjGVu.js").then((n) => n.t);
3279
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-TygDsPOU.js").then((n) => n.t);
3755
3280
  return pluginsSchema.parse(pluginCatalogRuntime.getProviders().map((plugin) => ({
3756
3281
  configKeys: plugin.manifest.configKeys ?? [],
3757
3282
  description: plugin.manifest.description,
@@ -3763,7 +3288,7 @@ async function readPlugins() {
3763
3288
  async function readRuntimeInfoReport() {
3764
3289
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
3765
3290
  import("./package-discovery-DJBompdd.js").then((n) => n.r),
3766
- import("./catalog-runtime-Bw9kjGVu.js").then((n) => n.t),
3291
+ import("./catalog-runtime-TygDsPOU.js").then((n) => n.t),
3767
3292
  readPlugins(),
3768
3293
  readSkillReports()
3769
3294
  ]);
@@ -3780,7 +3305,7 @@ async function readRuntimeInfoReport() {
3780
3305
  /** Read sanitized operational summaries contributed by plugins. */
3781
3306
  async function readPluginOperationalReportFeed() {
3782
3307
  const nowMs = Date.now();
3783
- const { getPluginOperationalReports } = await import("./credentialed-mh7HBqey.js").then((n) => n.a);
3308
+ const { getPluginOperationalReports } = await import("./projection-BOpi891B.js").then((n) => n.Tn);
3784
3309
  return pluginOperationalReportFeedSchema.parse({
3785
3310
  source: "plugins",
3786
3311
  generatedAt: new Date(nowMs).toISOString(),