@sentry/junior 0.272.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 (70) hide show
  1. package/dist/{adapter-BDYDi1F9.js → adapter-umap1cth.js} +1 -1
  2. package/dist/{admin-Dj7EFycG.js → admin-0o6E_3lo.js} +1 -1
  3. package/dist/{agent-runner-Cl6Usx2Y.js → agent-runner-D7Qz-lGO.js} +517 -298
  4. package/dist/api/schema.d.ts +1 -1
  5. package/dist/api/schema.js +4 -4
  6. package/dist/api.d.ts +1 -1
  7. package/dist/api.js +56 -573
  8. package/dist/{app-DjZblE2J.d.ts → app-BcyhJq8x.d.ts} +4 -9
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +673 -127
  11. package/dist/{assistant-reply-DN2w6JL_.js → assistant-reply-wZ7i4OFa.js} +1 -1
  12. package/dist/{automation-CV7g5h3A.js → automation-yffXcZt9.js} +1 -1
  13. package/dist/{briefs-PysAqnjV.js → briefs-CgMnlpIp.js} +4 -4
  14. package/dist/{catalog-runtime-BNeCWIaF.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-FRphcDWW.js → client-DhqbV0Fe.js} +63 -5
  24. package/dist/{reporting-schema-CZREhYc-.js → common-Eu6sx09c.js} +4 -1
  25. package/dist/{config-BUP9wjiR.js → config-C6FTd7PX.js} +6 -14
  26. package/dist/{connections-CesHau9u.js → connections-Dq72KajR.js} +11 -11
  27. package/dist/{conversation-BtQzLoK8.js → conversation-T4oabbBi.js} +10 -0
  28. package/dist/{conversation-privacy-DiGvA9a3.js → conversation-privacy-BeRgrsph.js} +1 -1
  29. package/dist/{credential-sync-D2odJC30.js → credential-sync-BmCbHAp4.js} +15 -16
  30. package/dist/{db-BPLmOwXf.d.ts → db-DOzBGLn5.d.ts} +1 -1
  31. package/dist/{executor-DGDpyegI.js → executor-BafgvQcc.js} +2 -2
  32. package/dist/instrumentation.js +114 -3
  33. package/dist/{job-queue-CjB97sii.js → job-queue-C4lphW9Y.js} +3 -3
  34. package/dist/{logging-CjkDr-jP.js → logging-CNs1e04n.js} +6 -1
  35. package/dist/{message-D9y8PKA1.js → message-CETC5lsl.js} +9 -9
  36. package/dist/nitro.d.ts +1 -1
  37. package/dist/nitro.js +3 -3
  38. package/dist/{oauth-callback-server-02Pej6hj.js → oauth-callback-server-DcQZsKMm.js} +2 -2
  39. package/dist/{credentialed-CgTulRDH.js → projection-BOpi891B.js} +4576 -49
  40. package/dist/{prompt-vfceH0c-.js → prompt-B1ySjUyf.js} +1 -1
  41. package/dist/{registry-7XgwNgQe.js → registry-B-yUrv3h.js} +2 -2
  42. package/dist/{resolve-CuuxNfWG.js → resolve-DqVtd4Q9.js} +3 -3
  43. package/dist/{result-BRhb6iN1.js → result-DQwTiLTw.js} +9 -9
  44. package/dist/{runner-DaWCofuS.js → runner-Dco5qMq6.js} +11 -11
  45. package/dist/{sandbox-egress-signals-Bn6FA1UC.js → sandbox-egress-signals-DFc_9Mcv.js} +1 -1
  46. package/dist/{schema-D5VLF4NT.js → schema-5xHDGlBW.js} +9 -7
  47. package/dist/{schema-CNjAzYOw.d.ts → schema-9OGdsaxF.d.ts} +13 -0
  48. package/dist/{service-account-D9vrsi8d.js → service-account-CWfk9Cs-.js} +3 -3
  49. package/dist/{service-account-H45LIAG2.js → service-account-smdPFWNy.js} +3 -3
  50. package/dist/{session-stop-HKl0ECX0.js → session-stop-DaT_7tYa.js} +11 -11
  51. package/dist/{skills-CsiKu3-g.js → skills-BbtJSF2-.js} +2 -2
  52. package/dist/{spawn-DqjBxqWh.js → spawn-Ceikdjkp.js} +2 -2
  53. package/dist/{list-JZUP1yog.js → stop-dKUc-x2K.js} +996 -432
  54. package/dist/{store-DyVKH0QB.js → store-C1xuZcLd.js} +4 -4
  55. package/dist/{store-CPF2kzbu.js → store-DaxUuOmD.js} +3 -3
  56. package/dist/{task-queue-BbQXCcNX.js → task-queue-ymAh_-P1.js} +4 -4
  57. package/dist/{task-runner-ChzW5E7R.js → task-runner-vlH6pEm3.js} +6 -6
  58. package/dist/{thread-state-Ds8JTsX6.js → thread-state-BkUpE9Ae.js} +4 -5
  59. package/dist/{turn-failure-response-DCMvjsA0.js → turn-failure-response-DIsD7ip_.js} +7 -8
  60. package/dist/{turn-lifecycle-DM_77q33.js → turn-lifecycle-BdPKdPS8.js} +1 -1
  61. package/dist/turn-session-routing-BdCTDq9m.js +1625 -0
  62. package/dist/{validation-DcGelbBi.js → validation-BQjCfh84.js} +1 -1
  63. package/dist/{version-C48azPKq.js → version-BpiFGZZW.js} +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/{viewer-BCRuO8Wg.js → viewer-C6Y1aXjZ.js} +27 -15
  66. package/dist/{work-zxkmbJAp.js → work-ChPh_YEh.js} +10 -38
  67. package/dist/{worker-CNNxHF1V.js → worker-Be_sK_ou.js} +6 -6
  68. package/package.json +4 -4
  69. package/dist/turn-session-routing-BV_gX7n3.js +0 -6072
  70. 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-BV_gX7n3.js";
2
- import { p as escapeXml } from "./client-FRphcDWW.js";
3
- import { m as logInfo, p as logException } from "./logging-CjkDr-jP.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-BCRuO8Wg.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 juniorCodeChanges, b as juniorConversationEvents, h as juniorConversationParticipants, m as juniorConversationReadPositions, w as juniorCodeRepositories, x as juniorApiTokens, y as juniorConversationMetrics } from "./executor-DGDpyegI.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-BNeCWIaF.js";
9
- import { $ as AutomationEditError, At as saveUserCustomization, Ct as conversationSubtree, 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, q as readConversationSourceTask, st as createAndEnqueueConversation, tt as listOwnedWatches, ut as webActorFromEmail, wt as conversationSubtreeColumns, x as createSlackReviewItemShow, xt as listChildAgents } from "./agent-runner-Cl6Usx2Y.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-CgTulRDH.js";
11
- import { r as getStateAdapter } from "./adapter-BDYDi1F9.js";
12
- 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-BtQzLoK8.js";
13
- import { m as getChildAgentLabel } from "./store-DyVKH0QB.js";
14
- import { d as getEventCatalog } from "./session-stop-HKl0ECX0.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-CuuxNfWG.js";
17
- import { a as getVercelConversationWorkQueue } from "./job-queue-CjB97sii.js";
18
- import { E as steerWebPendingMessages, a as cancelHumanFacingPendingMessages, g as getConversationWorkState, h as getConversation } from "./store-CPF2kzbu.js";
19
- import { n as finishServiceAccountSignIn, o as serviceAccountPlugins, r as getServiceAccountStatus, s as startServiceAccountSignIn, t as disconnectServiceAccount } from "./service-account-H45LIAG2.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-CV7g5h3A.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-JZUP1yog.js";
23
- import { f as legacyWebMailboxMetadataSchema } from "./result-BRhb6iN1.js";
24
- import { a as startConnection, l as getWebAuthorization, n as isConnectableProvider, r as listConnections, t as disconnectConnection, u as htmlCallbackResponse } from "./connections-CesHau9u.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-D5VLF4NT.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, max, 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,406 +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/read-position.ts
553
- /** Read how far the viewer has read one Conversation, if they have read it. */
554
- async function readConversationReadPosition(viewer, conversationId) {
555
- const [row] = await getDb().select({ lastReadSeq: juniorConversationReadPositions.lastReadSeq }).from(juniorConversationReadPositions).where(and(eq(juniorConversationReadPositions.userId, viewer.id), eq(juniorConversationReadPositions.conversationId, conversationId))).limit(1);
556
- return row;
557
- }
558
- /**
559
- * Move the viewer's read position forward.
560
- *
561
- * The stored value never goes back, so devices can report in any order. A
562
- * value past the last event is cut to the last event, so a bad client cannot
563
- * hide messages that arrive later.
564
- */
565
- async function markConversationRead(viewer, conversationId, body) {
566
- const db = getDb();
567
- const [conversation] = await db.select({ lastSeq: max(juniorConversationEvents.seq) }).from(juniorConversations).leftJoin(juniorConversationEvents, eq(juniorConversationEvents.conversationId, juniorConversations.conversationId)).where(eq(juniorConversations.conversationId, conversationId)).groupBy(juniorConversations.conversationId);
568
- if (!conversation) throwApiError(404, "Conversation not found.");
569
- const [row] = await db.insert(juniorConversationReadPositions).values({
570
- conversationId,
571
- lastReadSeq: Math.min(body.lastReadSeq, conversation.lastSeq ?? 0),
572
- updatedAt: /* @__PURE__ */ new Date(),
573
- userId: viewer.id
574
- }).onConflictDoUpdate({
575
- target: [juniorConversationReadPositions.userId, juniorConversationReadPositions.conversationId],
576
- set: {
577
- lastReadSeq: sql`greatest(${juniorConversationReadPositions.lastReadSeq}, excluded.last_read_seq)`,
578
- updatedAt: sql`excluded.updated_at`
579
- }
580
- }).returning({ lastReadSeq: juniorConversationReadPositions.lastReadSeq });
581
- return row;
582
- }
583
- // src/api/conversations/detail.ts
584
- /** Project stored metadata and a bounded event page into a signed history cursor. */
585
- function projectConversationDetail(args) {
586
- const { conversation } = args;
587
- const conversationId = conversation.conversationId;
588
- const transcriptPurgedAtMs = conversation.transcriptPurgedAtMs;
589
- const canExposePayload = args.access?.canViewPrivateContent ?? false;
590
- const modelUsage = transcriptPurgedAtMs === void 0 ? args.modelUsage : [];
591
- const sentryConversationUrl = buildSentryConversationUrl(conversationId);
592
- return {
593
- ...conversationSummaryFromStoredConversation({
594
- access: args.access,
595
- ...args.archivedAtMs === void 0 ? void 0 : { archivedAtMs: args.archivedAtMs },
596
- automationTitle: args.automationTitle,
597
- auxiliaryCosts: args.auxiliaryCosts,
598
- ...args.childAgentTotals ? { childAgentTotals: args.childAgentTotals } : void 0,
599
- conversation,
600
- durationMs: args.durationMs,
601
- firstHumanMessageText: args.firstHumanMessageText,
602
- ...args.locationId ? { locationId: args.locationId } : void 0,
603
- ...args.teamDomainByTeamId ? { teamDomainByTeamId: args.teamDomainByTeamId } : void 0,
604
- usage: args.usage
605
- }),
606
- annotations: canExposePayload ? args.annotations : [],
607
- sidebarAnnotations: canExposePayload ? args.sidebarAnnotations : void 0,
608
- ...canExposePayload && args.brief ? { brief: args.brief } : void 0,
609
- events: args.events,
610
- ...canExposePayload && args.participants.length > 0 ? { participants: args.participants } : void 0,
611
- ...args.previousSeq !== void 0 ? { previousCursor: encodeConversationCursor({
612
- conversationId,
613
- seq: args.previousSeq
614
- }) } : void 0,
615
- ...modelUsage.length > 0 ? { modelUsage } : void 0,
616
- eventHistory: conversationEventHistory({
617
- canExposePayload,
618
- ...transcriptPurgedAtMs === void 0 ? void 0 : { transcriptPurgedAtMs }
619
- }),
620
- generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
621
- ...sentryConversationUrl ? { sentryConversationUrl } : void 0,
622
- ...args.sourceTask ? { sourceTask: args.sourceTask } : void 0
623
- };
624
- }
625
- async function readConversationDetailFromSql(conversationId, options) {
626
- const record = await readConversationRecordFromSql(conversationId);
627
- if (!record) return void 0;
628
- const archivedAtMs = options.viewer ? await readConversationArchivedAt(options.viewer, conversationId) : void 0;
629
- const executor = getSqlExecutor();
630
- const [accessByConversation, annotations, automationTitleByConversation, auxiliaryCostsByConversation, briefVersion, firstHumanMessageTextByConversation, modelUsage, metricsByConversation, participantsByConversation, sourceTask, teamDomainByTeamId] = await Promise.all([
631
- readConversationAccessFromSql(getDb(), [conversationId], options.viewer),
632
- listConversationAnnotations(getDb(), conversationId),
633
- readAutomationTitlesByConversationId([record.conversation]),
634
- readConversationAuxiliaryCostsFromSql(getDb(), [conversationId]),
635
- readLatestConversationBrief(getDb(), conversationId),
636
- readFirstHumanMessageTexts(getDb(), [record.conversation]),
637
- record.conversation.transcriptPurgedAtMs === void 0 ? readConversationModelUsageFromSql(executor, { conversationId }) : Promise.resolve([]),
638
- readConversationTreeMetricsFromSql(getDb(), [conversationId]),
639
- readConversationParticipants(getDb(), [conversationId]),
640
- readConversationSourceTask({
641
- conversationId,
642
- ...options.viewer ? { viewer: options.viewer } : void 0
643
- }),
644
- resolveSlackTeamDomains(record.conversation.location?.provider === "slack" ? [record.conversation.location.teamId] : [])
645
- ]);
646
- const access = accessByConversation.get(conversationId);
647
- const sidebarAnnotations = access?.canViewPrivateContent ? (await listConversationSidebarAnnotations([conversationId], /* @__PURE__ */ new Map([[conversationId, annotations]])))[conversationId] : void 0;
648
- const page = record.conversation.transcriptPurgedAtMs === void 0 ? await readConversationEventPage(executor, {
649
- canExposePayload: access?.canViewPrivateContent ?? false,
650
- conversationId,
651
- limit: options.limit
652
- }) : { events: [] };
653
- const brief = access?.canViewPrivateContent ? briefVersion?.content : void 0;
654
- const { briefMentions, events } = await attachConversationMentions(executor, conversationId, {
655
- ...brief ? { brief } : void 0,
656
- events: page.events
657
- });
658
- const metrics = metricsByConversation.get(conversationId);
659
- const auxiliaryCosts = auxiliaryCostsByConversation.get(conversationId);
660
- const childAgentTotals = childAgentTotalsReport(metrics, auxiliaryCosts);
661
- return projectConversationDetail({
662
- ...record,
663
- access,
664
- ...archivedAtMs === void 0 ? void 0 : { archivedAtMs },
665
- automationTitle: automationTitleByConversation.get(conversationId),
666
- annotations,
667
- sidebarAnnotations,
668
- auxiliaryCosts: auxiliaryCosts?.total,
669
- ...childAgentTotals ? { childAgentTotals } : void 0,
670
- ...briefVersion ? { brief: {
671
- content: briefVersion.content,
672
- ...briefMentions.length > 0 ? { mentions: briefMentions } : void 0,
673
- updatedAt: briefVersion.createdAt.toISOString(),
674
- version: briefVersion.version
675
- } } : void 0,
676
- durationMs: metrics?.durationMs ?? record.durationMs,
677
- events,
678
- firstHumanMessageText: firstHumanMessageTextByConversation.get(conversationId),
679
- modelUsage,
680
- participants: participantsByConversation.get(conversationId) ?? [],
681
- ...sourceTask ? { sourceTask } : void 0,
682
- teamDomainByTeamId,
683
- ...page.previousSeq === void 0 ? void 0 : { previousSeq: page.previousSeq },
684
- usage: metrics?.usage ?? record.usage ?? void 0
685
- });
686
- }
687
- /** Load one conversation from its canonical event history. */
688
- async function readConversationDetail(conversationId, options = {}) {
689
- const report = await readConversationDetailFromSql(conversationId, {
690
- ...options,
691
- limit: options.limit ?? 500
692
- });
693
- if (!report) return void 0;
694
- const [forks, agentTree, readPosition] = await Promise.all([
695
- readConversationForks(conversationId, options.viewer),
696
- readConversationAgentTree(conversationId, options.viewer),
697
- options.viewer ? readConversationReadPosition(options.viewer, conversationId) : void 0
698
- ]);
699
- return conversationDetailReportSchema.parse({
700
- ...report,
701
- ...forks,
702
- ...agentTree ? { agentTree } : void 0,
703
- ...readPosition ? { readPosition } : void 0
704
- });
705
- }
706
222
  // src/api/conversations/event-list.ts
707
223
  /** Read one bounded page of reporting events before a signed cursor. */
708
224
  async function readConversationEvents(conversationId, beforeValue, options = {}) {
@@ -915,8 +431,15 @@ async function readConversationPendingMessages(conversationId, options = {}) {
915
431
  actorId,
916
432
  conversationId
917
433
  }) : void 0;
918
- const work = await getConversation({ conversationId });
919
- 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
+ }));
920
443
  return conversationPendingMessagesReportSchema.parse({
921
444
  ...authorization ? { authorization: {
922
445
  authorizationUrl: authorization.authorizationUrl,
@@ -1292,58 +815,6 @@ async function readConversationStatsFromSql() {
1292
815
  async function readConversationStats() {
1293
816
  return conversationStatsReportSchema.parse(await readConversationStatsFromSql());
1294
817
  }
1295
- // src/api/conversations/stop.ts
1296
- /**
1297
- * Stop the active Turn for one conversation participant.
1298
- *
1299
- * The stop also removes the human messages queued before it. The viewer's own
1300
- * web text comes back in the response so the dashboard can restore it to the
1301
- * composer instead of losing it.
1302
- */
1303
- async function stopConversationTurnForViewer(viewer, conversationId, services) {
1304
- const conversationStore = getConversationStore();
1305
- if (!await conversationStore.get({ conversationId })) throwApiError(404, "Conversation not found.");
1306
- if (!(await readConversationAccessFromSql(getDb(), [conversationId], viewer)).get(conversationId)?.isParticipant) throwApiError(403, "Only conversation participants can stop this turn.");
1307
- try {
1308
- const result = await stopConversationTurn({
1309
- conversationId,
1310
- conversationStore,
1311
- queue: services.queue
1312
- });
1313
- if (result.status === "no_work") return {
1314
- conversationId,
1315
- status: "no_work",
1316
- stoppedInboundMessageIds: [],
1317
- stoppedMessages: []
1318
- };
1319
- await clearSlackPendingReactions({
1320
- getSlackAdapter: getProductionSlackAdapter,
1321
- messages: result.pendingMessages
1322
- });
1323
- return {
1324
- conversationId,
1325
- status: "requested",
1326
- stoppedInboundMessageIds: result.pendingMessages.map((message) => message.inboundMessageId),
1327
- stoppedMessages: viewerWebMessages(result.pendingMessages, viewer)
1328
- };
1329
- } catch (error) {
1330
- throwApiError(500, "Unable to stop the active turn.", error);
1331
- }
1332
- }
1333
- function viewerWebMessages(messages, viewer) {
1334
- const viewerUserId = webActorFromEmail(viewer.email).userId;
1335
- return messages.flatMap((message) => {
1336
- if (message.source !== "web") return [];
1337
- const metadata = legacyWebMailboxMetadataSchema.safeParse(message.input.metadata);
1338
- if (!metadata.success) return [];
1339
- if (metadata.data.authorUserId !== viewerUserId) return [];
1340
- const text = message.input.text.trim();
1341
- return text ? [{
1342
- inboundMessageId: message.inboundMessageId,
1343
- text
1344
- }] : [];
1345
- });
1346
- }
1347
818
  // src/api/conversations/watches.ts
1348
819
  /**
1349
820
  * Load the active Watches that one Conversation owns, or throw a standard API
@@ -2768,7 +2239,7 @@ async function readPeoplePluginReports(args) {
2768
2239
  reports: [],
2769
2240
  source: "plugins"
2770
2241
  });
2771
- const { getPluginProfileReports } = await import("./credentialed-CgTulRDH.js").then((n) => n.a);
2242
+ const { getPluginProfileReports } = await import("./projection-BOpi891B.js").then((n) => n.Tn);
2772
2243
  return pluginOperationalReportFeedSchema.parse({
2773
2244
  generatedAt: new Date(nowMs).toISOString(),
2774
2245
  reports: await getPluginProfileReports({
@@ -3058,18 +2529,28 @@ function createPersonalSpendReader() {
3058
2529
  };
3059
2530
  }
3060
2531
  // src/api/people/routes.ts
2532
+ const AVATAR_IMMUTABLE_CACHE_CONTROL = "private, max-age=31536000, immutable";
3061
2533
  /** Create the HTTP routes owned by the People API. */
3062
2534
  function createPeopleRoutes() {
3063
2535
  const app = new Hono();
3064
2536
  const readPersonalSpend = createPersonalSpendReader();
3065
2537
  app.get("/avatars/:identityId", requireViewer, validateRequest("param", avatarParamsSchema, "Invalid route parameters."), async (context) => {
3066
2538
  const { identityId } = context.req.valid("param");
3067
- const image = await readAvatarImage(getDb(), getStateAdapter(), identityId);
3068
- if (!image) throwApiError(404, "Avatar not found.");
3069
- return new Response(Buffer.from(image.data, "base64"), { headers: {
3070
- "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,
3071
2552
  "content-security-policy": "default-src 'none'; sandbox",
3072
- "content-type": image.contentType,
2553
+ "content-type": avatar.image.contentType,
3073
2554
  "x-content-type-options": "nosniff"
3074
2555
  } });
3075
2556
  });
@@ -3521,6 +3002,7 @@ const ARCHIVED_LIMIT = 50;
3521
3002
  const cursorSchema = z.object({
3522
3003
  conversationId: z.string().min(1).optional(),
3523
3004
  createdAtMs: z.number().int(),
3005
+ forUserId: z.string().min(1).optional(),
3524
3006
  id: z.string().min(1),
3525
3007
  query: z.string().min(1).optional(),
3526
3008
  version: z.literal(2)
@@ -3541,7 +3023,7 @@ function encodeCursor(item, filters) {
3541
3023
  function decodeCursor(cursor, filters) {
3542
3024
  try {
3543
3025
  const parsed = cursorSchema.parse(JSON.parse(Buffer.from(cursor, "base64url").toString("utf8")));
3544
- 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;
3545
3027
  return {
3546
3028
  createdAt: new Date(parsed.createdAtMs),
3547
3029
  id: parsed.id
@@ -3595,6 +3077,7 @@ function createReviewItemRoutes(services) {
3595
3077
  const query = context.req.valid("query");
3596
3078
  const filters = {
3597
3079
  ...query.conversationId ? { conversationId: query.conversationId } : void 0,
3080
+ ...query.for === "viewer" ? { forUserId: viewer.id } : void 0,
3598
3081
  ...query.q ? { query: query.q } : void 0
3599
3082
  };
3600
3083
  const after = query.cursor ? decodeCursor(query.cursor, filters) : void 0;
@@ -3784,7 +3267,7 @@ function readDescriptionText(home) {
3784
3267
  }
3785
3268
  /** Read discovered skill names for authenticated runtime diagnostics. */
3786
3269
  async function readSkillReports() {
3787
- const { discoverSkills } = await import("./skills-CsiKu3-g.js").then((n) => n.o);
3270
+ const { discoverSkills } = await import("./skills-BbtJSF2-.js").then((n) => n.o);
3788
3271
  const skills = await discoverSkills();
3789
3272
  return skillReportsSchema.parse(skills.map((skill) => ({
3790
3273
  name: skill.name,
@@ -3793,7 +3276,7 @@ async function readSkillReports() {
3793
3276
  }
3794
3277
  /** Read safe configured plugin metadata for authenticated runtime diagnostics. */
3795
3278
  async function readPlugins() {
3796
- const { pluginCatalogRuntime } = await import("./catalog-runtime-BNeCWIaF.js").then((n) => n.t);
3279
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-TygDsPOU.js").then((n) => n.t);
3797
3280
  return pluginsSchema.parse(pluginCatalogRuntime.getProviders().map((plugin) => ({
3798
3281
  configKeys: plugin.manifest.configKeys ?? [],
3799
3282
  description: plugin.manifest.description,
@@ -3805,7 +3288,7 @@ async function readPlugins() {
3805
3288
  async function readRuntimeInfoReport() {
3806
3289
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
3807
3290
  import("./package-discovery-DJBompdd.js").then((n) => n.r),
3808
- import("./catalog-runtime-BNeCWIaF.js").then((n) => n.t),
3291
+ import("./catalog-runtime-TygDsPOU.js").then((n) => n.t),
3809
3292
  readPlugins(),
3810
3293
  readSkillReports()
3811
3294
  ]);
@@ -3822,7 +3305,7 @@ async function readRuntimeInfoReport() {
3822
3305
  /** Read sanitized operational summaries contributed by plugins. */
3823
3306
  async function readPluginOperationalReportFeed() {
3824
3307
  const nowMs = Date.now();
3825
- const { getPluginOperationalReports } = await import("./credentialed-CgTulRDH.js").then((n) => n.a);
3308
+ const { getPluginOperationalReports } = await import("./projection-BOpi891B.js").then((n) => n.Tn);
3826
3309
  return pluginOperationalReportFeedSchema.parse({
3827
3310
  source: "plugins",
3828
3311
  generatedAt: new Date(nowMs).toISOString(),