@sentry/junior 0.147.0 → 0.149.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 (162) hide show
  1. package/dist/{agent-hooks-HS6VGUPM.js → agent-hooks-CWMCXAMT.js} +10 -10
  2. package/dist/api/conversations/access.d.ts +2 -1
  3. package/dist/api/conversations/aggregate.d.ts +2 -0
  4. package/dist/api/conversations/archive.d.ts +3 -5
  5. package/dist/api/conversations/create.d.ts +6 -0
  6. package/dist/api/conversations/detail.d.ts +2 -296
  7. package/dist/api/conversations/event-list.d.ts +2 -193
  8. package/dist/api/conversations/list.d.ts +4 -66
  9. package/dist/api/conversations/pending-messages.d.ts +15 -0
  10. package/dist/api/conversations/routes.d.ts +1 -1
  11. package/dist/api/conversations/stats.d.ts +0 -54
  12. package/dist/api/http.d.ts +2 -6
  13. package/dist/api/locations/detail.d.ts +3 -111
  14. package/dist/api/locations/list.d.ts +0 -45
  15. package/dist/api/locations/query.d.ts +2 -1
  16. package/dist/api/locations/routes.d.ts +1 -1
  17. package/dist/api/people/list.d.ts +0 -29
  18. package/dist/api/people/profile.d.ts +2 -106
  19. package/dist/api/people/profile.query.d.ts +2 -1
  20. package/dist/api/people/routes.d.ts +1 -1
  21. package/dist/api/people/shared.d.ts +3 -1
  22. package/dist/api/people/spend.d.ts +3 -4
  23. package/dist/api/personal-tokens/routes.d.ts +2 -2
  24. package/dist/api/route.d.ts +8 -14
  25. package/dist/api/schema/conversation.d.ts +130 -25
  26. package/dist/api/schema/location.d.ts +4 -0
  27. package/dist/api/schema/person.d.ts +7 -3
  28. package/dist/api/schema.d.ts +2 -2
  29. package/dist/api/schema.js +13 -1
  30. package/dist/api/stats.d.ts +0 -13
  31. package/dist/api/validation.d.ts +7 -0
  32. package/dist/api/viewer.d.ts +3 -0
  33. package/dist/api.d.ts +2 -1
  34. package/dist/api.js +772 -561
  35. package/dist/app.js +241 -204
  36. package/dist/{catalog-runtime-M6ZTPYRV.js → catalog-runtime-GAW5APVI.js} +5 -5
  37. package/dist/chat/actor.d.ts +4 -1
  38. package/dist/chat/agent/request.d.ts +2 -0
  39. package/dist/chat/agent/resume.d.ts +1 -0
  40. package/dist/chat/agent-dispatch/context.d.ts +2 -2
  41. package/dist/chat/agent-invocations/types.d.ts +52 -0
  42. package/dist/chat/api-turns/work.d.ts +108 -0
  43. package/dist/chat/app/services.d.ts +1 -1
  44. package/dist/chat/conversations/history.d.ts +60 -0
  45. package/dist/chat/conversations/provenance.d.ts +6 -0
  46. package/dist/chat/conversations/sql/actor-identity.d.ts +19 -0
  47. package/dist/chat/conversations/store.d.ts +1 -1
  48. package/dist/chat/resource-events/store.d.ts +1 -1
  49. package/dist/chat/runtime/reply-executor.d.ts +1 -3
  50. package/dist/chat/runtime/slack-runtime.d.ts +2 -1
  51. package/dist/chat/runtime/turn-preparation.d.ts +9 -0
  52. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  53. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  54. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  55. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  56. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  57. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  58. package/dist/chat/services/conversation-memory.d.ts +2 -2
  59. package/dist/chat/services/conversation-title.d.ts +20 -0
  60. package/dist/chat/slack/assistant-thread/title.d.ts +10 -27
  61. package/dist/chat/slack/channel.d.ts +26 -0
  62. package/dist/chat/slack/dispatch-turn.d.ts +1 -0
  63. package/dist/chat/slack/timestamp-param.d.ts +0 -2
  64. package/dist/chat/slack/{tools → tool-support}/canvas/api.d.ts +1 -1
  65. package/dist/chat/slack/{tools → tool-support}/canvas/context.d.ts +3 -0
  66. package/dist/chat/slack/tool-support/channel-access.d.ts +42 -0
  67. package/dist/chat/slack/tool-support/channel-target.d.ts +27 -0
  68. package/dist/chat/slack/tool-support/list/params.d.ts +5 -0
  69. package/dist/chat/slack/tools/canvas/create.d.ts +1 -1
  70. package/dist/chat/slack/tools/channel-join.d.ts +28 -0
  71. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -2
  72. package/dist/chat/slack/tools/conversation-message-search.d.ts +2 -8
  73. package/dist/chat/slack/tools/message-add-reaction.d.ts +1 -1
  74. package/dist/chat/slack/tools/public-search.d.ts +63 -2
  75. package/dist/chat/slack/tools/send-files.d.ts +1 -1
  76. package/dist/chat/slack/tools/thread-read.d.ts +2 -5
  77. package/dist/chat/source.d.ts +2 -0
  78. package/dist/chat/state/conversation.d.ts +1 -0
  79. package/dist/chat/task-execution/checkpoint.d.ts +1 -0
  80. package/dist/chat/task-execution/state.d.ts +9 -4
  81. package/dist/chat/task-execution/store.d.ts +3 -2
  82. package/dist/chat/task-execution/turn-cursor.d.ts +2 -0
  83. package/dist/chat/task-execution/worker.d.ts +2 -0
  84. package/dist/chat/tasks/read.d.ts +1 -1
  85. package/dist/chat/tools/search-conversation-events.d.ts +1 -1
  86. package/dist/chat/tools/types.d.ts +11 -4
  87. package/dist/chat/tools/watch-resource-events.d.ts +2 -2
  88. package/dist/{chunk-ORO3PWKQ.js → chunk-25SC7D6Z.js} +1 -1
  89. package/dist/{chunk-GGB4WL5A.js → chunk-2JHKISQA.js} +1 -1
  90. package/dist/{chunk-AVZDSUFB.js → chunk-2P6BQQER.js} +4 -4
  91. package/dist/{chunk-7NVJVJID.js → chunk-32CXPIZE.js} +2 -2
  92. package/dist/{chunk-YSB2N563.js → chunk-3GYZ5YEH.js} +121 -87
  93. package/dist/{chunk-DU74WPIC.js → chunk-3I2IVWSZ.js} +1 -1
  94. package/dist/{chunk-SVHUEHUL.js → chunk-3JVUWLR7.js} +65 -4
  95. package/dist/{chunk-RXBVB2JM.js → chunk-3KGQOKIG.js} +1 -1
  96. package/dist/{chunk-YBVXBNSV.js → chunk-4JGM7FXO.js} +2 -2
  97. package/dist/{chunk-AZ7EAOBE.js → chunk-4YKPOYRV.js} +5 -5
  98. package/dist/chunk-5CWUQXXT.js +806 -0
  99. package/dist/{chunk-DLDRQHG5.js → chunk-6G5RCDKP.js} +1 -1
  100. package/dist/{chunk-AYU72WO7.js → chunk-75DSYFFB.js} +47 -70
  101. package/dist/{chunk-YHDERDRO.js → chunk-7BH46NB4.js} +91 -75
  102. package/dist/{chunk-745QJ3ZA.js → chunk-ALJG73MQ.js} +2 -2
  103. package/dist/chunk-ART7P442.js +48 -0
  104. package/dist/{chunk-7A4M7PCV.js → chunk-DK5UT6PQ.js} +1180 -556
  105. package/dist/{chunk-AIOK6LAG.js → chunk-EW2LNCRL.js} +813 -44
  106. package/dist/chunk-EXW76GW4.js +36 -0
  107. package/dist/chunk-GDFF3BQG.js +136 -0
  108. package/dist/{chunk-7UQIVT6G.js → chunk-JMH3DBM5.js} +6 -6
  109. package/dist/{chunk-CWP5RHVX.js → chunk-JMSMXXED.js} +12 -5
  110. package/dist/{chunk-CSZINQJ2.js → chunk-MEMURSBG.js} +1 -1
  111. package/dist/{chunk-YNTTROJT.js → chunk-QIW7OQEO.js} +1 -1
  112. package/dist/{chunk-Q4R73I43.js → chunk-RB26T3LU.js} +58 -28
  113. package/dist/{chunk-4LJO56OJ.js → chunk-RLVZIWNQ.js} +1 -1
  114. package/dist/{chunk-SGZO7UWH.js → chunk-SWHPVZBV.js} +2 -2
  115. package/dist/{chunk-PVNG6UMU.js → chunk-TFBNBXPU.js} +22 -10
  116. package/dist/{chunk-EFLJ2WJH.js → chunk-URDCZAL4.js} +5 -5
  117. package/dist/{chunk-6H5SZR24.js → chunk-UZGASGOD.js} +300 -383
  118. package/dist/{chunk-YEO3VX72.js → chunk-W7UONUIZ.js} +8 -6
  119. package/dist/{chunk-JUUKWRBE.js → chunk-X22XXSZA.js} +1 -1
  120. package/dist/{chunk-X5UY34KT.js → chunk-XPYWWMHR.js} +8 -8
  121. package/dist/{chunk-IIOOO4FQ.js → chunk-ZYMYLLWG.js} +2 -1
  122. package/dist/cli/chat.js +32 -35
  123. package/dist/cli/check.js +6 -6
  124. package/dist/cli/plugins.js +13 -13
  125. package/dist/cli/snapshot-create.js +9 -9
  126. package/dist/cli/upgrade.js +7 -7
  127. package/dist/db/schema/agent-invocations.d.ts +20 -0
  128. package/dist/db/schema/conversation-annotations.d.ts +2 -2
  129. package/dist/db/schema.d.ts +22 -2
  130. package/dist/{db-CSEW4RII.js → db-JBZLRIVY.js} +7 -7
  131. package/dist/instrumentation.js +4 -4
  132. package/dist/nitro.js +2 -2
  133. package/dist/{oauth-callback-server-FUEV5HNG.js → oauth-callback-server-MNTVYY2I.js} +24 -26
  134. package/dist/{runner-U3XHSKSH.js → runner-K24BWZSZ.js} +23 -25
  135. package/dist/{skills-AKE6W3WI.js → skills-AU5ST2CG.js} +6 -6
  136. package/dist/spawn-6PO7CVJ6.js +38 -0
  137. package/dist/{validation-WDTAULUF.js → validation-IXGZCFRO.js} +6 -6
  138. package/dist/version.js +1 -1
  139. package/dist/{work-C3HAO2BG.js → work-WCZIYILX.js} +18 -19
  140. package/dist/{worker-LNP4YKBM.js → worker-U2AACBEY.js} +11 -10
  141. package/package.json +6 -5
  142. package/dist/api/health.d.ts +0 -7
  143. package/dist/api/personal-tokens/create.d.ts +0 -11
  144. package/dist/api/personal-tokens/list.d.ts +0 -12
  145. package/dist/api/personal-tokens/revoke.d.ts +0 -5
  146. package/dist/api/plugin-reports.d.ts +0 -63
  147. package/dist/api/plugins.d.ts +0 -8
  148. package/dist/api/runtime.d.ts +0 -24
  149. package/dist/api/skills.d.ts +0 -6
  150. package/dist/chat/slack/tools/channel-access.d.ts +0 -30
  151. package/dist/chunk-34GS7B6L.js +0 -115
  152. package/dist/chunk-AVOCH2Z5.js +0 -285
  153. package/dist/chunk-HLXXQI5Q.js +0 -238
  154. package/dist/chunk-K4SZAP3K.js +0 -67
  155. package/dist/chunk-R776HKPK.js +0 -81
  156. package/dist/chunk-Z4YU7Z4L.js +0 -76
  157. package/dist/spawn-B6GDTWJ4.js +0 -39
  158. /package/dist/chat/slack/{tools → tool-support}/canvas/markdown.d.ts +0 -0
  159. /package/dist/chat/slack/{tools → tool-support}/channel-capabilities.d.ts +0 -0
  160. /package/dist/chat/slack/{tools → tool-support}/context.d.ts +0 -0
  161. /package/dist/chat/slack/{tools → tool-support}/list/api.d.ts +0 -0
  162. /package/dist/chat/slack/{tools → tool-support}/slack-message-url.d.ts +0 -0
package/dist/api.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ acceptedConversationMessageSchema,
2
3
  activityDays,
3
4
  actorDirectoryReportSchema,
4
5
  actorProfileReportSchema,
@@ -12,8 +13,11 @@ import {
12
13
  conversationFeedQuerySchema,
13
14
  conversationFeedSchema,
14
15
  conversationParamsSchema,
16
+ conversationPendingMessagesReportSchema,
15
17
  conversationReportEventSchema,
16
18
  conversationStatsReportSchema,
19
+ createConversationBodySchema,
20
+ createConversationMessageBodySchema,
17
21
  createPersonalTokenBodySchema,
18
22
  createdPersonalTokenSchema,
19
23
  locationDetailReportSchema,
@@ -29,15 +33,21 @@ import {
29
33
  taskListSchema,
30
34
  taskParamsSchema,
31
35
  taskRunListSchema
32
- } from "./chunk-SVHUEHUL.js";
36
+ } from "./chunk-3JVUWLR7.js";
33
37
  import {
38
+ appendAndEnqueueApiConversationMessage,
39
+ createAndEnqueueApiConversation,
34
40
  formatSlackConversationRedactedLabel,
35
41
  readHealthReport,
36
- resolveSlackConversationContextFromThreadId
37
- } from "./chunk-34GS7B6L.js";
42
+ resolveSlackConversationContextFromThreadId,
43
+ webActorFromEmail
44
+ } from "./chunk-5CWUQXXT.js";
38
45
  import {
39
46
  buildSentryConversationUrl
40
47
  } from "./chunk-4CODA7P5.js";
48
+ import {
49
+ getConversation
50
+ } from "./chunk-JMSMXXED.js";
41
51
  import {
42
52
  readTaskExecutionByConversationId,
43
53
  readTaskExecutionDays,
@@ -45,7 +55,8 @@ import {
45
55
  readTaskExecutionSummaries,
46
56
  readTaskExecutions,
47
57
  readTaskRuns
48
- } from "./chunk-AVOCH2Z5.js";
58
+ } from "./chunk-UZGASGOD.js";
59
+ import "./chunk-4YKPOYRV.js";
49
60
  import {
50
61
  healthReportSchema,
51
62
  pluginOperationalReportFeedSchema,
@@ -53,6 +64,11 @@ import {
53
64
  runtimeInfoReportSchema,
54
65
  skillReportsSchema
55
66
  } from "./chunk-UTL3MHPC.js";
67
+ import {
68
+ getVercelConversationWorkQueue
69
+ } from "./chunk-WR76IVA5.js";
70
+ import "./chunk-JAL3CH4G.js";
71
+ import "./chunk-5I6ORJUY.js";
56
72
  import {
57
73
  createSchedulerSqlStore,
58
74
  deleteEventTask,
@@ -64,18 +80,17 @@ import {
64
80
  listPublicEventTasksForTeams,
65
81
  listPublicScheduledTasksForTeams,
66
82
  readStats
67
- } from "./chunk-EFLJ2WJH.js";
83
+ } from "./chunk-URDCZAL4.js";
68
84
  import {
69
85
  parseSlackThreadId
70
- } from "./chunk-CSZINQJ2.js";
71
- import {
72
- fallbackShortTitle
73
- } from "./chunk-Z4YU7Z4L.js";
86
+ } from "./chunk-MEMURSBG.js";
74
87
  import {
75
88
  agentTurnUsageSchema,
89
+ fallbackShortTitle,
76
90
  hasAgentTurnUsage,
77
91
  renderJuniorNativeConversationEvent
78
- } from "./chunk-HLXXQI5Q.js";
92
+ } from "./chunk-EW2LNCRL.js";
93
+ import "./chunk-3Y47VMHV.js";
79
94
  import "./chunk-PDO5BLNM.js";
80
95
  import {
81
96
  createPluginLogger,
@@ -83,56 +98,73 @@ import {
83
98
  listConversationAnnotations,
84
99
  renderPluginConversationEvent,
85
100
  resolveViewerUser
86
- } from "./chunk-YHDERDRO.js";
101
+ } from "./chunk-7BH46NB4.js";
87
102
  import {
88
103
  getSlackClient,
89
104
  withSlackRetries
90
- } from "./chunk-SGZO7UWH.js";
105
+ } from "./chunk-SWHPVZBV.js";
91
106
  import {
92
107
  decodeStoredConversationEvent,
108
+ getConversationStore,
93
109
  getDb,
94
110
  getSqlExecutor,
95
111
  locationFromRow,
96
112
  parseSessionSource
97
- } from "./chunk-YSB2N563.js";
113
+ } from "./chunk-3GYZ5YEH.js";
98
114
  import {
99
115
  juniorApiTokens,
100
116
  juniorConversationEvents,
101
117
  juniorConversations,
102
118
  juniorIdentities,
103
119
  juniorUsers
104
- } from "./chunk-DU74WPIC.js";
120
+ } from "./chunk-3I2IVWSZ.js";
105
121
  import "./chunk-EM225ITJ.js";
106
- import "./chunk-ORO3PWKQ.js";
107
- import "./chunk-7NVJVJID.js";
122
+ import "./chunk-25SC7D6Z.js";
123
+ import "./chunk-32CXPIZE.js";
108
124
  import {
109
125
  juniorDestinations
110
126
  } from "./chunk-VFUK3X5B.js";
111
127
  import {
112
128
  canExposeConversationPayload
113
- } from "./chunk-RXBVB2JM.js";
129
+ } from "./chunk-3KGQOKIG.js";
130
+ import "./chunk-RUIXF3YQ.js";
114
131
  import "./chunk-HZF5P5UN.js";
115
132
  import {
116
133
  parseSlackChannelId,
117
134
  parseSlackMessageTs,
118
135
  parseSlackTeamId
119
136
  } from "./chunk-F3B2YJ76.js";
120
- import "./chunk-4LJO56OJ.js";
137
+ import {
138
+ logException
139
+ } from "./chunk-RLVZIWNQ.js";
121
140
  import "./chunk-6GWA276C.js";
122
- import "./chunk-DLDRQHG5.js";
123
- import "./chunk-IIOOO4FQ.js";
141
+ import "./chunk-6G5RCDKP.js";
142
+ import "./chunk-ZYMYLLWG.js";
124
143
  import "./chunk-MLKGABMK.js";
125
144
 
126
145
  // src/api.ts
127
146
  import { Hono as Hono7 } from "hono";
147
+ import { HTTPException as HTTPException2 } from "hono/http-exception";
128
148
 
129
149
  // src/api/conversations/routes.ts
130
150
  import { Hono } from "hono";
131
151
 
132
152
  // src/api/http.ts
133
153
  import { HTTPException } from "hono/http-exception";
154
+ var NO_STORE_HEADERS = { "cache-control": "no-store" };
155
+ function noStoreInit(init) {
156
+ if (!init) return { headers: NO_STORE_HEADERS };
157
+ const headers = new Headers(init.headers);
158
+ if (!headers.has("cache-control")) {
159
+ headers.set("cache-control", "no-store");
160
+ }
161
+ return { ...init, headers };
162
+ }
134
163
  function jsonResponse(schema, value, init) {
135
- return Response.json(schema.parse(value), init);
164
+ return Response.json(schema.parse(value), noStoreInit(init));
165
+ }
166
+ function emptyResponse(status = 204) {
167
+ return new Response(null, noStoreInit({ status }));
136
168
  }
137
169
  function throwApiError(status, message2, cause) {
138
170
  throw new HTTPException(status, {
@@ -141,35 +173,25 @@ function throwApiError(status, message2, cause) {
141
173
  res: jsonResponse(apiErrorSchema, { error: message2 }, { status })
142
174
  });
143
175
  }
144
- function parseParams(schema, params) {
145
- const result = schema.safeParse(params);
146
- if (result.success) return result.data;
147
- return throwApiError(400, "Invalid route parameters.", result.error);
148
- }
149
- function parseQuery(schema, query) {
150
- const result = schema.safeParse(query);
151
- if (result.success) return result.data;
152
- return throwApiError(400, "Invalid query parameters.", result.error);
153
- }
154
- function parseBody(schema, body) {
155
- const result = schema.safeParse(body);
156
- if (result.success) return result.data;
157
- return throwApiError(400, "Invalid request body.", result.error);
176
+
177
+ // src/api/validation.ts
178
+ import { zValidator } from "@hono/zod-validator";
179
+ function validateRequest(target, schema, message2) {
180
+ return zValidator(target, schema, (result) => {
181
+ if (!result.success) throwApiError(400, message2, result.error);
182
+ });
158
183
  }
159
184
 
160
- // src/api/route.ts
161
- function defineApiRoute(route) {
162
- return route;
163
- }
164
- function registerApiRoutes(app, routes5) {
165
- for (const route of routes5) {
166
- app.on(
167
- route.method,
168
- route.path,
169
- async (context) => jsonResponse(route.responseSchema, await route.handler(context))
170
- );
185
+ // src/api/viewer.ts
186
+ import { createMiddleware } from "hono/factory";
187
+ var requireViewer = createMiddleware(
188
+ async (context, next) => {
189
+ if (!context.get("viewer")) {
190
+ throwApiError(401, "Authentication required.");
191
+ }
192
+ await next();
171
193
  }
172
- }
194
+ );
173
195
 
174
196
  // src/api/conversations/archive.ts
175
197
  import { and, eq, isNotNull } from "drizzle-orm";
@@ -190,32 +212,134 @@ async function archiveIfUnchanged(args) {
190
212
  if (!args.archived && existing.archivedAt === null) return "updated";
191
213
  return "conflict";
192
214
  }
193
- var archive_default = defineApiRoute({
194
- method: "patch",
195
- path: "/:conversationId/archive",
196
- responseSchema: archiveConversationResponseSchema,
197
- handler: async (c) => {
198
- const { conversationId } = parseParams(
199
- conversationParamsSchema,
200
- c.req.param()
215
+ async function archiveConversation(conversationId, body) {
216
+ const result = await archiveIfUnchanged({ ...body, conversationId });
217
+ if (result === "not_found") {
218
+ throwApiError(404, "Conversation not found.");
219
+ }
220
+ if (result === "conflict") {
221
+ throwApiError(409, "Conversation received new activity.");
222
+ }
223
+ return { archived: body.archived };
224
+ }
225
+
226
+ // src/api/conversations/access.ts
227
+ import { eq as eq2, inArray } from "drizzle-orm";
228
+ import { alias } from "drizzle-orm/pg-core";
229
+ var rootConversation = alias(juniorConversations, "access_root_conversation");
230
+ var rootDestination = alias(juniorDestinations, "access_root_destination");
231
+ var rootIdentity = alias(juniorIdentities, "access_root_identity");
232
+ async function readConversationAccessFromSql(db, conversationIds, viewer) {
233
+ if (conversationIds.length === 0) return /* @__PURE__ */ new Map();
234
+ const normalizedViewerEmail = viewer?.email.trim().toLowerCase() || void 0;
235
+ const rows = await db.select({
236
+ conversationId: juniorConversations.conversationId,
237
+ parentConversationId: juniorConversations.parentConversationId,
238
+ storedRootConversationId: juniorConversations.rootConversationId,
239
+ rootConversationId: rootConversation.conversationId,
240
+ rootParentConversationId: rootConversation.parentConversationId,
241
+ rootRootConversationId: rootConversation.rootConversationId,
242
+ visibility: rootDestination.visibility,
243
+ rootEmailNormalized: rootIdentity.emailNormalized,
244
+ rootEmailVerified: rootIdentity.emailVerified,
245
+ rootUserId: rootIdentity.userId
246
+ }).from(juniorConversations).leftJoin(
247
+ rootConversation,
248
+ eq2(
249
+ rootConversation.conversationId,
250
+ juniorConversations.rootConversationId
251
+ )
252
+ ).leftJoin(
253
+ rootDestination,
254
+ eq2(rootDestination.id, rootConversation.destinationId)
255
+ ).leftJoin(
256
+ rootIdentity,
257
+ eq2(rootIdentity.id, rootConversation.actorIdentityId)
258
+ ).where(inArray(juniorConversations.conversationId, [...conversationIds]));
259
+ return new Map(
260
+ rows.map((row) => {
261
+ const hasValidRoot = row.rootConversationId !== null && row.rootConversationId === row.storedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && (row.parentConversationId !== null || row.storedRootConversationId === row.conversationId);
262
+ const validRootConversationId = hasValidRoot ? row.rootConversationId ?? void 0 : void 0;
263
+ const visibility = validRootConversationId === void 0 ? null : row.visibility;
264
+ const isParticipant = validRootConversationId !== void 0 && viewer !== void 0 && (row.rootUserId === viewer.id || normalizedViewerEmail !== void 0 && row.rootEmailVerified === true && row.rootEmailNormalized === normalizedViewerEmail);
265
+ const canViewPrivateContent = isParticipant || validRootConversationId !== void 0 && canExposeConversationPayload({
266
+ visibility: visibility ?? void 0
267
+ });
268
+ return [
269
+ row.conversationId,
270
+ {
271
+ canViewPrivateContent,
272
+ isParticipant,
273
+ visibility
274
+ }
275
+ ];
276
+ })
277
+ );
278
+ }
279
+
280
+ // src/api/conversations/create.ts
281
+ function actorFromViewer(viewer) {
282
+ const normalized = viewer.email.trim().toLowerCase();
283
+ return webActorFromEmail(normalized, {
284
+ ...viewer.displayName ? { fullName: viewer.displayName } : {},
285
+ userName: normalized.split("@")[0] || normalized
286
+ });
287
+ }
288
+ async function createConversationForViewer(viewer, body) {
289
+ try {
290
+ return await createAndEnqueueApiConversation(
291
+ {
292
+ actor: actorFromViewer(viewer),
293
+ idempotencyKey: body.idempotencyKey,
294
+ message: body.message,
295
+ ...body.visibility ? { visibility: body.visibility } : {}
296
+ },
297
+ {
298
+ conversationStore: getConversationStore(),
299
+ queue: getVercelConversationWorkQueue()
300
+ }
201
301
  );
202
- let input;
203
- try {
204
- input = await c.req.json();
205
- } catch (error) {
206
- throwApiError(400, "Invalid request body.", error);
207
- }
208
- const body = parseBody(archiveConversationBodySchema, input);
209
- const result = await archiveIfUnchanged({ ...body, conversationId });
210
- if (result === "not_found") {
211
- throwApiError(404, "Conversation not found.");
212
- }
213
- if (result === "conflict") {
214
- throwApiError(409, "Conversation received new activity.");
215
- }
216
- return { archived: body.archived };
302
+ } catch (error) {
303
+ throwApiError(500, "Unable to create conversation.", error);
217
304
  }
218
- });
305
+ }
306
+ async function appendConversationMessageForViewer(viewer, conversationId, body) {
307
+ const conversation = await getConversationStore().get({
308
+ conversationId
309
+ });
310
+ if (!conversation) {
311
+ throwApiError(404, "Conversation not found.");
312
+ }
313
+ const destinationPlatform = conversation.destination?.platform;
314
+ const acceptsApiMessages = destinationPlatform === "local" && conversationId.startsWith("local:web:") || destinationPlatform === "slack";
315
+ if (!acceptsApiMessages) {
316
+ throwApiError(409, "Conversation does not accept API messages.");
317
+ }
318
+ const access = await readConversationAccessFromSql(
319
+ getDb(),
320
+ [conversationId],
321
+ viewer
322
+ );
323
+ if (!access.get(conversationId)?.isParticipant) {
324
+ throwApiError(403, "Only conversation participants can add messages.");
325
+ }
326
+ try {
327
+ return await appendAndEnqueueApiConversationMessage(
328
+ {
329
+ actor: actorFromViewer(viewer),
330
+ conversationId,
331
+ idempotencyKey: body.idempotencyKey,
332
+ message: body.message
333
+ },
334
+ {
335
+ conversationStore: getConversationStore(),
336
+ queue: getVercelConversationWorkQueue()
337
+ }
338
+ );
339
+ } catch (error) {
340
+ throwApiError(500, "Unable to append message.", error);
341
+ }
342
+ }
219
343
 
220
344
  // src/chat/slack/source-link.ts
221
345
  function parseSlackTeamDomain(value) {
@@ -332,7 +456,7 @@ async function resolveSlackTeamDomains(teamIds) {
332
456
  }
333
457
 
334
458
  // src/chat/pi/sql-model-usage.ts
335
- import { and as and2, eq as eq2, sql } from "drizzle-orm";
459
+ import { and as and2, eq as eq3, sql } from "drizzle-orm";
336
460
  var message = sql`${juniorConversationEvents.payload}`;
337
461
  var usage = sql`${message}->'usage'`;
338
462
  var cost = sql`${usage}->'cost'`;
@@ -426,14 +550,14 @@ async function readConversationModelUsageFromSql(executor, options) {
426
550
  costTotal: summedCost(nonnegativeNumber(cost, "total"))
427
551
  }).from(juniorConversationEvents).innerJoin(
428
552
  juniorConversations,
429
- eq2(
553
+ eq3(
430
554
  juniorConversations.conversationId,
431
555
  juniorConversationEvents.conversationId
432
556
  )
433
557
  ).where(
434
558
  and2(
435
- options.includeDescendants ? eq2(juniorConversations.rootConversationId, options.conversationId) : eq2(juniorConversationEvents.conversationId, options.conversationId),
436
- eq2(juniorConversationEvents.type, "assistant_message"),
559
+ options.includeDescendants ? eq3(juniorConversations.rootConversationId, options.conversationId) : eq3(juniorConversationEvents.conversationId, options.conversationId),
560
+ eq3(juniorConversationEvents.type, "assistant_message"),
437
561
  sql`jsonb_typeof(${message}->'provider') = 'string'`,
438
562
  sql`jsonb_typeof(${message}->'model') = 'string'`,
439
563
  sql`coalesce(${message}->>'provider', '') <> ''`,
@@ -498,9 +622,9 @@ import {
498
622
  and as and3,
499
623
  asc,
500
624
  desc,
501
- eq as eq3,
625
+ eq as eq4,
502
626
  getTableColumns,
503
- inArray,
627
+ inArray as inArray2,
504
628
  lt,
505
629
  sql as sql2
506
630
  } from "drizzle-orm";
@@ -705,6 +829,7 @@ function reportEventData(args) {
705
829
  type: "message",
706
830
  messageId: data.messageId,
707
831
  role: data.role,
832
+ ...data.meta?.source === "web" ? { source: "web" } : {},
708
833
  ...actorIdentity ? { actorIdentity } : {},
709
834
  ...typeof data.meta?.eventType === "string" ? { eventType: data.meta.eventType } : {},
710
835
  ...args.canExposePayload ? { text: data.text } : { redacted: true }
@@ -912,17 +1037,17 @@ async function readConversationEventRows(executor, args) {
912
1037
  end`
913
1038
  }).from(juniorConversationEvents).where(
914
1039
  and3(
915
- eq3(juniorConversationEvents.conversationId, args.conversationId),
1040
+ eq4(juniorConversationEvents.conversationId, args.conversationId),
916
1041
  args.beforeSeq === void 0 ? void 0 : lt(juniorConversationEvents.seq, args.beforeSeq),
917
- inArray(
1042
+ inArray2(
918
1043
  juniorConversationEvents.type,
919
1044
  args.types ?? [...conversationReportSourceEventTypes]
920
1045
  ),
921
- args.subagentInvocationIds === void 0 ? void 0 : inArray(
1046
+ args.subagentInvocationIds === void 0 ? void 0 : inArray2(
922
1047
  sql2`${juniorConversationEvents.payload}->>'subagentInvocationId'`,
923
1048
  args.subagentInvocationIds
924
1049
  ),
925
- args.toolCallIds === void 0 ? void 0 : inArray(
1050
+ args.toolCallIds === void 0 ? void 0 : inArray2(
926
1051
  sql2`${juniorConversationEvents.payload}->>'toolCallId'`,
927
1052
  args.toolCallIds
928
1053
  )
@@ -1043,8 +1168,8 @@ function surfaceFromConversationId(conversationId) {
1043
1168
  return "internal";
1044
1169
  }
1045
1170
  function surfaceFromSource(source, conversationId) {
1046
- if (source === "slack" || source === "api" || source === "scheduler") {
1047
- return source;
1171
+ if (source === "slack" || source === "api" || source === "web" || source === "scheduler") {
1172
+ return source === "web" ? "api" : source;
1048
1173
  }
1049
1174
  return surfaceFromConversationId(conversationId);
1050
1175
  }
@@ -1165,6 +1290,7 @@ function conversationSummaryFromStoredConversation(args) {
1165
1290
  visibility
1166
1291
  }),
1167
1292
  isParticipant: args.access?.isParticipant ?? false,
1293
+ ...visibility ? { visibility } : {},
1168
1294
  lastProgressAt: new Date(
1169
1295
  conversation.execution.updatedAtMs ?? conversation.updatedAtMs
1170
1296
  ).toISOString(),
@@ -1184,59 +1310,6 @@ function conversationSummaryFromStoredConversation(args) {
1184
1310
  };
1185
1311
  }
1186
1312
 
1187
- // src/api/conversations/access.ts
1188
- import { eq as eq4, inArray as inArray2 } from "drizzle-orm";
1189
- import { alias } from "drizzle-orm/pg-core";
1190
- var rootConversation = alias(juniorConversations, "access_root_conversation");
1191
- var rootDestination = alias(juniorDestinations, "access_root_destination");
1192
- var rootIdentity = alias(juniorIdentities, "access_root_identity");
1193
- async function readConversationAccessFromSql(db, conversationIds, verifiedViewerEmail) {
1194
- if (conversationIds.length === 0) return /* @__PURE__ */ new Map();
1195
- const normalizedViewerEmail = verifiedViewerEmail?.trim().toLowerCase() || void 0;
1196
- const rows = await db.select({
1197
- conversationId: juniorConversations.conversationId,
1198
- parentConversationId: juniorConversations.parentConversationId,
1199
- storedRootConversationId: juniorConversations.rootConversationId,
1200
- rootConversationId: rootConversation.conversationId,
1201
- rootParentConversationId: rootConversation.parentConversationId,
1202
- rootRootConversationId: rootConversation.rootConversationId,
1203
- visibility: rootDestination.visibility,
1204
- rootEmailNormalized: rootIdentity.emailNormalized,
1205
- rootEmailVerified: rootIdentity.emailVerified
1206
- }).from(juniorConversations).leftJoin(
1207
- rootConversation,
1208
- eq4(
1209
- rootConversation.conversationId,
1210
- juniorConversations.rootConversationId
1211
- )
1212
- ).leftJoin(
1213
- rootDestination,
1214
- eq4(rootDestination.id, rootConversation.destinationId)
1215
- ).leftJoin(
1216
- rootIdentity,
1217
- eq4(rootIdentity.id, rootConversation.actorIdentityId)
1218
- ).where(inArray2(juniorConversations.conversationId, [...conversationIds]));
1219
- return new Map(
1220
- rows.map((row) => {
1221
- const hasValidRoot = row.rootConversationId !== null && row.rootConversationId === row.storedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && (row.parentConversationId !== null || row.storedRootConversationId === row.conversationId);
1222
- const validRootConversationId = hasValidRoot ? row.rootConversationId ?? void 0 : void 0;
1223
- const visibility = validRootConversationId === void 0 ? null : row.visibility;
1224
- const isParticipant = validRootConversationId !== void 0 && normalizedViewerEmail !== void 0 && row.rootEmailVerified === true && row.rootEmailNormalized === normalizedViewerEmail;
1225
- const canViewPrivateContent = isParticipant || validRootConversationId !== void 0 && canExposeConversationPayload({
1226
- visibility: visibility ?? void 0
1227
- });
1228
- return [
1229
- row.conversationId,
1230
- {
1231
- canViewPrivateContent,
1232
- isParticipant,
1233
- visibility
1234
- }
1235
- ];
1236
- })
1237
- );
1238
- }
1239
-
1240
1313
  // src/api/conversations/usage.ts
1241
1314
  import { and as and4, eq as eq5, inArray as inArray3, isNull, sql as sql3 } from "drizzle-orm";
1242
1315
  import { alias as alias2 } from "drizzle-orm/pg-core";
@@ -1565,11 +1638,7 @@ async function readConversationFeedFromSql(options = {}) {
1565
1638
  metricsByRoot,
1566
1639
  teamDomainByTeamId
1567
1640
  ] = await Promise.all([
1568
- readConversationAccessFromSql(
1569
- db,
1570
- conversationIds,
1571
- options.verifiedViewerEmail
1572
- ),
1641
+ readConversationAccessFromSql(db, conversationIds, options.viewer),
1573
1642
  readConversationAuxiliaryCostsFromSql(db, conversationIds, {
1574
1643
  includeDescendants: true
1575
1644
  }),
@@ -1603,22 +1672,6 @@ async function readConversationFeed(options = {}) {
1603
1672
  await readConversationFeedFromSql(options)
1604
1673
  );
1605
1674
  }
1606
- var list_default = defineApiRoute({
1607
- method: "get",
1608
- path: "/",
1609
- responseSchema: conversationFeedSchema,
1610
- handler: async (c) => {
1611
- const { actorEmail } = parseQuery(
1612
- conversationFeedQuerySchema,
1613
- c.req.query()
1614
- );
1615
- const verifiedViewerEmail = c.get("verifiedViewerEmail");
1616
- return readConversationFeed({
1617
- ...actorEmail ? { actorEmail } : {},
1618
- ...verifiedViewerEmail ? { verifiedViewerEmail } : {}
1619
- });
1620
- }
1621
- });
1622
1675
 
1623
1676
  // src/chat/tasks/read.ts
1624
1677
  import { and as and7, eq as eq8, or as or2 } from "drizzle-orm";
@@ -2086,12 +2139,9 @@ async function readConversationSourceTask(args) {
2086
2139
  conversationId: args.conversationId
2087
2140
  });
2088
2141
  if (!execution) return void 0;
2089
- const email = args.verifiedViewerEmail?.trim();
2090
- if (!email) return { kind: execution.kind };
2091
- const user = await resolveViewerUser(email);
2092
- if (!user) return { kind: execution.kind };
2142
+ if (!args.viewer) return { kind: execution.kind };
2093
2143
  const candidate = await resolveViewerTaskCandidate(
2094
- user,
2144
+ args.viewer,
2095
2145
  execution.kind,
2096
2146
  execution.taskId
2097
2147
  );
@@ -2199,11 +2249,7 @@ async function readConversationDetailFromSql(conversationId, options) {
2199
2249
  sourceTask,
2200
2250
  teamDomainByTeamId
2201
2251
  ] = await Promise.all([
2202
- readConversationAccessFromSql(
2203
- getDb(),
2204
- [conversationId],
2205
- options.verifiedViewerEmail
2206
- ),
2252
+ readConversationAccessFromSql(getDb(), [conversationId], options.viewer),
2207
2253
  listConversationAnnotations(getDb(), conversationId),
2208
2254
  readConversationAuxiliaryCostsFromSql(getDb(), [conversationId], {
2209
2255
  includeDescendants: includeDescendantMetrics
@@ -2218,7 +2264,7 @@ async function readConversationDetailFromSql(conversationId, options) {
2218
2264
  ),
2219
2265
  readConversationSourceTask({
2220
2266
  conversationId,
2221
- ...options.verifiedViewerEmail ? { verifiedViewerEmail: options.verifiedViewerEmail } : {}
2267
+ ...options.viewer ? { viewer: options.viewer } : {}
2222
2268
  }),
2223
2269
  resolveSlackTeamDomains(
2224
2270
  record.conversation.sessionSource?.platform === "slack" ? [record.conversation.sessionSource.teamId] : []
@@ -2252,25 +2298,6 @@ async function readConversationDetail(conversationId, options = {}) {
2252
2298
  });
2253
2299
  return report ? conversationDetailReportSchema.parse(report) : void 0;
2254
2300
  }
2255
- var detail_default = defineApiRoute({
2256
- method: "get",
2257
- path: "/:conversationId",
2258
- responseSchema: conversationDetailReportSchema,
2259
- handler: async (c) => {
2260
- const { conversationId } = parseParams(
2261
- conversationParamsSchema,
2262
- c.req.param()
2263
- );
2264
- const query = parseQuery(conversationDetailQuerySchema, c.req.query());
2265
- const verifiedViewerEmail = c.get("verifiedViewerEmail");
2266
- const report = await readConversationDetail(conversationId, {
2267
- ...query,
2268
- ...verifiedViewerEmail ? { verifiedViewerEmail } : {}
2269
- });
2270
- if (!report) throwApiError(404, "Conversation not found.");
2271
- return report;
2272
- }
2273
- });
2274
2301
 
2275
2302
  // src/api/conversations/event-list.ts
2276
2303
  async function readConversationEvents(conversationId, beforeValue, options = {}) {
@@ -2284,7 +2311,7 @@ async function readConversationEvents(conversationId, beforeValue, options = {})
2284
2311
  const accessByConversation = await readConversationAccessFromSql(
2285
2312
  getDb(),
2286
2313
  [conversationId],
2287
- options.verifiedViewerEmail
2314
+ options.viewer
2288
2315
  );
2289
2316
  const access = accessByConversation.get(conversationId);
2290
2317
  const canExposePayload = access?.canViewPrivateContent ?? false;
@@ -2310,28 +2337,119 @@ async function readConversationEvents(conversationId, beforeValue, options = {})
2310
2337
  generatedAt: (/* @__PURE__ */ new Date()).toISOString()
2311
2338
  });
2312
2339
  }
2313
- var event_list_default = defineApiRoute({
2314
- method: "get",
2315
- path: "/:conversationId/events",
2316
- responseSchema: conversationEventPageSchema,
2317
- handler: async (c) => {
2318
- const { conversationId } = parseParams(
2319
- conversationParamsSchema,
2320
- c.req.param()
2340
+
2341
+ // src/api/conversations/pending-messages.ts
2342
+ import { z as z4 } from "zod";
2343
+ var apiTurnMailboxMetadataSchema = z4.object({
2344
+ authorEmail: z4.string().email(),
2345
+ authorFullName: z4.string().min(1).optional(),
2346
+ authorUserId: z4.string().min(1),
2347
+ authorUserName: z4.string().min(1).optional(),
2348
+ kind: z4.literal("api_turn"),
2349
+ messageId: z4.string().min(1)
2350
+ }).strict();
2351
+ var slackMailboxAuthorSchema = z4.object({
2352
+ userId: z4.string(),
2353
+ userName: z4.string().optional(),
2354
+ fullName: z4.string().optional(),
2355
+ isMe: z4.boolean().optional()
2356
+ }).passthrough();
2357
+ var slackMailboxMessageSchema = z4.object({
2358
+ author: slackMailboxAuthorSchema,
2359
+ id: z4.string().min(1)
2360
+ }).passthrough();
2361
+ var slackMailboxMetadataSchema = z4.object({
2362
+ message: slackMailboxMessageSchema,
2363
+ platform: z4.literal("slack")
2364
+ }).passthrough();
2365
+ function isoFromMs(value) {
2366
+ return new Date(value).toISOString();
2367
+ }
2368
+ function actorIdentityFromApiMetadata(metadata2) {
2369
+ return {
2370
+ email: metadata2.authorEmail.trim().toLowerCase(),
2371
+ ...metadata2.authorFullName ? { fullName: metadata2.authorFullName } : {}
2372
+ };
2373
+ }
2374
+ function actorIdentityFromSlackMetadata(metadata2) {
2375
+ if (metadata2.message.author.isMe === true) return void 0;
2376
+ const slackUserId = metadata2.message.author.userId.trim();
2377
+ if (!slackUserId) return void 0;
2378
+ const slackUserName = metadata2.message.author.userName?.trim();
2379
+ const fullName = metadata2.message.author.fullName?.trim();
2380
+ return {
2381
+ slackUserId,
2382
+ ...slackUserName ? { slackUserName } : {},
2383
+ ...fullName ? { fullName } : {}
2384
+ };
2385
+ }
2386
+ function projectPendingMessage(message2, canExposePayload) {
2387
+ if (message2.source === "web") {
2388
+ const metadata2 = apiTurnMailboxMetadataSchema.safeParse(
2389
+ message2.input.metadata
2321
2390
  );
2322
- const { before, limit } = parseQuery(
2323
- conversationEventsQuerySchema,
2324
- c.req.query()
2391
+ if (!metadata2.success) return void 0;
2392
+ const text = message2.input.text.trim();
2393
+ if (!text) return void 0;
2394
+ return {
2395
+ createdAt: isoFromMs(message2.createdAtMs),
2396
+ delivery: message2.delivery,
2397
+ inboundMessageId: message2.inboundMessageId,
2398
+ messageId: metadata2.data.messageId,
2399
+ receivedAt: isoFromMs(message2.receivedAtMs),
2400
+ role: "user",
2401
+ source: "web",
2402
+ ...canExposePayload ? {
2403
+ actorIdentity: actorIdentityFromApiMetadata(metadata2.data),
2404
+ text
2405
+ } : { redacted: true }
2406
+ };
2407
+ }
2408
+ if (message2.source === "slack") {
2409
+ const metadata2 = slackMailboxMetadataSchema.safeParse(
2410
+ message2.input.metadata
2325
2411
  );
2326
- const verifiedViewerEmail = c.get("verifiedViewerEmail");
2327
- const report = await readConversationEvents(conversationId, before, {
2328
- limit,
2329
- ...verifiedViewerEmail ? { verifiedViewerEmail } : {}
2330
- });
2331
- if (!report) throwApiError(404, "Conversation not found.");
2332
- return report;
2412
+ if (!metadata2.success) return void 0;
2413
+ if (metadata2.data.message.author.isMe === true) return void 0;
2414
+ const text = message2.input.text.trim();
2415
+ if (!text) return void 0;
2416
+ const actorIdentity = actorIdentityFromSlackMetadata(metadata2.data);
2417
+ return {
2418
+ createdAt: isoFromMs(message2.createdAtMs),
2419
+ delivery: message2.delivery,
2420
+ inboundMessageId: message2.inboundMessageId,
2421
+ messageId: metadata2.data.message.id,
2422
+ receivedAt: isoFromMs(message2.receivedAtMs),
2423
+ role: "user",
2424
+ source: "slack",
2425
+ ...canExposePayload ? {
2426
+ ...actorIdentity ? { actorIdentity } : {},
2427
+ text
2428
+ } : { redacted: true }
2429
+ };
2333
2430
  }
2334
- });
2431
+ return void 0;
2432
+ }
2433
+ async function readConversationPendingMessages(conversationId, options = {}) {
2434
+ const conversation = await getConversationStore().get({ conversationId });
2435
+ if (!conversation) return void 0;
2436
+ const access = (await readConversationAccessFromSql(getDb(), [conversationId], options.viewer)).get(conversationId);
2437
+ const canExposePayload = access?.canViewPrivateContent ?? false;
2438
+ const work = await getConversation({ conversationId });
2439
+ const messages = (work?.execution.pendingMessages ?? []).map((message2) => projectPendingMessage(message2, canExposePayload)).filter(
2440
+ (message2) => Boolean(message2)
2441
+ );
2442
+ return conversationPendingMessagesReportSchema.parse({
2443
+ conversationId,
2444
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2445
+ messages
2446
+ });
2447
+ }
2448
+ async function requireConversationPendingMessages(conversationId, options = {}) {
2449
+ const report = await readConversationPendingMessages(conversationId, options);
2450
+ if (!report) throwApiError(404, "Conversation not found.");
2451
+ return report;
2452
+ }
2335
2453
 
2336
2454
  // src/api/conversations/stats.query.ts
2337
2455
  import { and as and8, eq as eq9, gte, isNull as isNull4, lte, sql as sql6 } from "drizzle-orm";
@@ -2339,6 +2457,13 @@ import { alias as alias4 } from "drizzle-orm/pg-core";
2339
2457
 
2340
2458
  // src/api/conversations/aggregate.ts
2341
2459
  import { sql as sql5 } from "drizzle-orm";
2460
+ function usageTokenValue(source, field) {
2461
+ return sql5`CASE
2462
+ WHEN ${source.usage}->>${field} IS NOT NULL
2463
+ THEN (${source.usage}->>${field})::double precision
2464
+ ELSE NULL
2465
+ END`;
2466
+ }
2342
2467
  function tokenValue(source) {
2343
2468
  return sql5`
2344
2469
  CASE
@@ -2386,11 +2511,13 @@ function conversationAggregateColumns(sources) {
2386
2511
  WHERE ${roots.executionStatus} NOT IN ('idle', 'failed')
2387
2512
  )::integer`,
2388
2513
  conversations: sql5`${conversationCount}::integer`,
2514
+ cachedInputTokens: sql5`SUM(${usageTokenValue(metrics, "cachedInputTokens")})::double precision`,
2389
2515
  costUsd: sql5`SUM(${costValue(metrics)})::double precision`,
2390
2516
  durationMs: sql5`COALESCE(SUM(${metrics.durationMs}), 0)::double precision`,
2391
2517
  failed: sql5`${conversationCount} FILTER (
2392
2518
  WHERE ${roots.executionStatus} = 'failed'
2393
2519
  )::integer`,
2520
+ inputTokens: sql5`SUM(${usageTokenValue(metrics, "inputTokens")})::double precision`,
2394
2521
  tokens: sql5`SUM(${tokenValue(metrics)})::double precision`
2395
2522
  };
2396
2523
  }
@@ -2432,7 +2559,7 @@ function actorLabel(row) {
2432
2559
  }
2433
2560
  function surfaceLabel(source) {
2434
2561
  if (source === "scheduler") return "Scheduler";
2435
- if (source === "api") return "API";
2562
+ if (source === "api" || source === "web") return "Web";
2436
2563
  if (source === "internal" || source === "local") return "Internal";
2437
2564
  return "Conversation";
2438
2565
  }
@@ -2496,7 +2623,9 @@ function metricDays(rows, endMs) {
2496
2623
  conversations: row?.conversations ?? 0,
2497
2624
  date,
2498
2625
  durationMs: row?.durationMs ?? 0,
2626
+ ...row?.cachedInputTokens !== null && row?.cachedInputTokens !== void 0 ? { cachedInputTokens: row.cachedInputTokens } : {},
2499
2627
  ...row?.costUsd !== null && row?.costUsd !== void 0 ? { costUsd: addUsd(void 0, row.costUsd) } : {},
2628
+ ...row?.inputTokens !== null && row?.inputTokens !== void 0 ? { inputTokens: row.inputTokens } : {},
2500
2629
  ...row?.tokens !== null && row?.tokens !== void 0 ? { tokens: row.tokens } : {}
2501
2630
  });
2502
2631
  }
@@ -2607,10 +2736,12 @@ async function aggregateStats(db, start, end) {
2607
2736
  juniorDestinations.visibility
2608
2737
  ),
2609
2738
  db.select({
2739
+ cachedInputTokens: treeAggregateColumns.cachedInputTokens,
2610
2740
  conversations: treeAggregateColumns.conversations,
2611
2741
  costUsd: treeAggregateColumns.costUsd,
2612
2742
  date: activityDate,
2613
2743
  durationMs: treeAggregateColumns.durationMs,
2744
+ inputTokens: treeAggregateColumns.inputTokens,
2614
2745
  tokens: treeAggregateColumns.tokens
2615
2746
  }).from(juniorConversations).innerJoin(
2616
2747
  treeConversation,
@@ -2666,6 +2797,7 @@ async function readConversationStatsFromSql() {
2666
2797
  }
2667
2798
  return {
2668
2799
  active: totals?.active ?? 0,
2800
+ ...totals?.cachedInputTokens !== null && totals?.cachedInputTokens !== void 0 ? { cachedInputTokens: totals.cachedInputTokens } : {},
2669
2801
  conversations: totals?.conversations ?? 0,
2670
2802
  durationMs: totals?.durationMs ?? 0,
2671
2803
  failed: totals?.failed ?? 0,
@@ -2676,6 +2808,7 @@ async function readConversationStatsFromSql() {
2676
2808
  actors: statsItems(actors),
2677
2809
  source: "conversation_index",
2678
2810
  ...totals?.costUsd !== null && totals?.costUsd !== void 0 ? { costUsd: addUsd(void 0, totals.costUsd) } : {},
2811
+ ...totals?.inputTokens !== null && totals?.inputTokens !== void 0 ? { inputTokens: totals.inputTokens } : {},
2679
2812
  ...totals?.tokens !== null && totals?.tokens !== void 0 ? { tokens: totals.tokens } : {},
2680
2813
  windowEnd: end.toISOString(),
2681
2814
  windowStart: start.toISOString()
@@ -2688,93 +2821,162 @@ async function readConversationStats() {
2688
2821
  await readConversationStatsFromSql()
2689
2822
  );
2690
2823
  }
2691
- var stats_default = defineApiRoute({
2692
- method: "get",
2693
- path: "/stats",
2694
- responseSchema: conversationStatsReportSchema,
2695
- handler: readConversationStats
2696
- });
2697
2824
 
2698
2825
  // src/api/conversations/routes.ts
2699
- var routes = [
2700
- list_default,
2701
- stats_default,
2702
- archive_default,
2703
- event_list_default,
2704
- detail_default
2705
- ];
2706
2826
  function createConversationRoutes() {
2707
2827
  const app = new Hono();
2708
- registerApiRoutes(app, routes);
2709
- return app;
2710
- }
2711
-
2712
- // src/reporting.ts
2713
- import { readFileSync } from "fs";
2714
- import path from "path";
2715
- function readDescriptionText(home) {
2716
- try {
2717
- const raw = readFileSync(path.join(home, "DESCRIPTION.md"), "utf8").trim();
2718
- return raw || void 0;
2719
- } catch {
2720
- return void 0;
2721
- }
2722
- }
2723
- async function readSkillReports() {
2724
- const { discoverSkills } = await import("./skills-AKE6W3WI.js");
2725
- const skills = await discoverSkills();
2726
- return skillReportsSchema.parse(
2727
- skills.map((skill) => ({
2728
- name: skill.name,
2729
- pluginProvider: skill.pluginProvider
2730
- }))
2828
+ app.get(
2829
+ "/",
2830
+ validateRequest(
2831
+ "query",
2832
+ conversationFeedQuerySchema,
2833
+ "Invalid query parameters."
2834
+ ),
2835
+ async (context) => {
2836
+ const { actorEmail } = context.req.valid("query");
2837
+ const viewer = context.get("viewer");
2838
+ return jsonResponse(
2839
+ conversationFeedSchema,
2840
+ await readConversationFeed({
2841
+ ...actorEmail ? { actorEmail } : {},
2842
+ ...viewer ? { viewer } : {}
2843
+ })
2844
+ );
2845
+ }
2731
2846
  );
2732
- }
2733
- async function readPlugins() {
2734
- const { pluginCatalogRuntime } = await import("./catalog-runtime-M6ZTPYRV.js");
2735
- return pluginsSchema.parse(
2736
- pluginCatalogRuntime.getProviders().map((plugin) => ({
2737
- configKeys: plugin.manifest.configKeys ?? [],
2738
- description: plugin.manifest.description,
2739
- displayName: plugin.manifest.displayName,
2740
- name: plugin.manifest.name
2741
- }))
2847
+ app.get(
2848
+ "/stats",
2849
+ async () => jsonResponse(conversationStatsReportSchema, await readConversationStats())
2742
2850
  );
2851
+ app.post(
2852
+ "/",
2853
+ requireViewer,
2854
+ validateRequest(
2855
+ "json",
2856
+ createConversationBodySchema,
2857
+ "Invalid request body."
2858
+ ),
2859
+ async (context) => {
2860
+ const viewer = context.get("viewer");
2861
+ const body = context.req.valid("json");
2862
+ return jsonResponse(
2863
+ acceptedConversationMessageSchema,
2864
+ await createConversationForViewer(viewer, body)
2865
+ );
2866
+ }
2867
+ );
2868
+ app.post(
2869
+ "/:conversationId/messages",
2870
+ requireViewer,
2871
+ validateRequest(
2872
+ "param",
2873
+ conversationParamsSchema,
2874
+ "Invalid route parameters."
2875
+ ),
2876
+ validateRequest(
2877
+ "json",
2878
+ createConversationMessageBodySchema,
2879
+ "Invalid request body."
2880
+ ),
2881
+ async (context) => {
2882
+ const viewer = context.get("viewer");
2883
+ const { conversationId } = context.req.valid("param");
2884
+ const body = context.req.valid("json");
2885
+ return jsonResponse(
2886
+ acceptedConversationMessageSchema,
2887
+ await appendConversationMessageForViewer(viewer, conversationId, body)
2888
+ );
2889
+ }
2890
+ );
2891
+ app.patch(
2892
+ "/:conversationId/archive",
2893
+ validateRequest(
2894
+ "param",
2895
+ conversationParamsSchema,
2896
+ "Invalid route parameters."
2897
+ ),
2898
+ validateRequest(
2899
+ "json",
2900
+ archiveConversationBodySchema,
2901
+ "Invalid request body."
2902
+ ),
2903
+ async (context) => {
2904
+ const { conversationId } = context.req.valid("param");
2905
+ const body = context.req.valid("json");
2906
+ return jsonResponse(
2907
+ archiveConversationResponseSchema,
2908
+ await archiveConversation(conversationId, body)
2909
+ );
2910
+ }
2911
+ );
2912
+ app.get(
2913
+ "/:conversationId/events",
2914
+ validateRequest(
2915
+ "param",
2916
+ conversationParamsSchema,
2917
+ "Invalid route parameters."
2918
+ ),
2919
+ validateRequest(
2920
+ "query",
2921
+ conversationEventsQuerySchema,
2922
+ "Invalid query parameters."
2923
+ ),
2924
+ async (context) => {
2925
+ const { conversationId } = context.req.valid("param");
2926
+ const { before, limit } = context.req.valid("query");
2927
+ const viewer = context.get("viewer");
2928
+ const report = await readConversationEvents(conversationId, before, {
2929
+ limit,
2930
+ ...viewer ? { viewer } : {}
2931
+ });
2932
+ if (!report) throwApiError(404, "Conversation not found.");
2933
+ return jsonResponse(conversationEventPageSchema, report);
2934
+ }
2935
+ );
2936
+ app.get(
2937
+ "/:conversationId/pending-messages",
2938
+ validateRequest(
2939
+ "param",
2940
+ conversationParamsSchema,
2941
+ "Invalid route parameters."
2942
+ ),
2943
+ async (context) => {
2944
+ const { conversationId } = context.req.valid("param");
2945
+ const viewer = context.get("viewer");
2946
+ return jsonResponse(
2947
+ conversationPendingMessagesReportSchema,
2948
+ await requireConversationPendingMessages(conversationId, {
2949
+ viewer
2950
+ })
2951
+ );
2952
+ }
2953
+ );
2954
+ app.get(
2955
+ "/:conversationId",
2956
+ validateRequest(
2957
+ "param",
2958
+ conversationParamsSchema,
2959
+ "Invalid route parameters."
2960
+ ),
2961
+ validateRequest(
2962
+ "query",
2963
+ conversationDetailQuerySchema,
2964
+ "Invalid query parameters."
2965
+ ),
2966
+ async (context) => {
2967
+ const { conversationId } = context.req.valid("param");
2968
+ const query = context.req.valid("query");
2969
+ const viewer = context.get("viewer");
2970
+ const report = await readConversationDetail(conversationId, {
2971
+ ...query,
2972
+ ...viewer ? { viewer } : {}
2973
+ });
2974
+ if (!report) throwApiError(404, "Conversation not found.");
2975
+ return jsonResponse(conversationDetailReportSchema, report);
2976
+ }
2977
+ );
2978
+ return app;
2743
2979
  }
2744
- async function readRuntimeInfoReport() {
2745
- const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
2746
- import("./discovery-GUFTFBTL.js"),
2747
- import("./catalog-runtime-M6ZTPYRV.js"),
2748
- readPlugins(),
2749
- readSkillReports()
2750
- ]);
2751
- const home = homeDir();
2752
- return runtimeInfoReportSchema.parse({
2753
- cwd: process.cwd(),
2754
- homeDir: home,
2755
- descriptionText: readDescriptionText(home),
2756
- providers: plugins.map((plugin) => plugin.name),
2757
- skills,
2758
- packagedContent: pluginCatalogRuntime.getPackageContent()
2759
- });
2760
- }
2761
- async function readPluginOperationalReportFeed() {
2762
- const nowMs = Date.now();
2763
- const { getPluginOperationalReports } = await import("./agent-hooks-HS6VGUPM.js");
2764
- return pluginOperationalReportFeedSchema.parse({
2765
- source: "plugins",
2766
- generatedAt: new Date(nowMs).toISOString(),
2767
- reports: await getPluginOperationalReports(nowMs)
2768
- });
2769
- }
2770
-
2771
- // src/api/health.ts
2772
- var health_default = defineApiRoute({
2773
- method: "get",
2774
- path: "/api/health",
2775
- responseSchema: healthReportSchema,
2776
- handler: readHealthReport
2777
- });
2778
2980
 
2779
2981
  // src/api/locations/routes.ts
2780
2982
  import { Hono as Hono2 } from "hono";
@@ -3175,7 +3377,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3175
3377
  readConversationAccessFromSql(
3176
3378
  getDb(),
3177
3379
  recentConversationIds,
3178
- options.verifiedViewerEmail
3380
+ options.viewer
3179
3381
  ),
3180
3382
  readRootConversationMetricsFromSql(getDb(), recentConversationIds)
3181
3383
  ]);
@@ -3203,21 +3405,6 @@ async function readLocationDetail(locationId, options = {}) {
3203
3405
  const report = await readLocationDetailFromSql(locationId, options);
3204
3406
  return report ? locationDetailReportSchema.parse(report) : void 0;
3205
3407
  }
3206
- var detail_default2 = defineApiRoute({
3207
- method: "get",
3208
- path: "/:locationId",
3209
- responseSchema: locationDetailReportSchema,
3210
- handler: async (c) => {
3211
- const { locationId } = parseParams(locationParamsSchema, c.req.param());
3212
- const verifiedViewerEmail = c.get("verifiedViewerEmail");
3213
- const report = await readLocationDetail(
3214
- locationId,
3215
- verifiedViewerEmail ? { verifiedViewerEmail } : {}
3216
- );
3217
- if (!report) throwApiError(404, "Location not found.");
3218
- return report;
3219
- }
3220
- });
3221
3408
 
3222
3409
  // src/api/locations/list.ts
3223
3410
  async function readLocationDirectory() {
@@ -3225,18 +3412,32 @@ async function readLocationDirectory() {
3225
3412
  await readLocationDirectoryFromSql()
3226
3413
  );
3227
3414
  }
3228
- var list_default2 = defineApiRoute({
3229
- method: "get",
3230
- path: "/",
3231
- responseSchema: locationDirectoryReportSchema,
3232
- handler: readLocationDirectory
3233
- });
3234
3415
 
3235
3416
  // src/api/locations/routes.ts
3236
- var routes2 = [list_default2, detail_default2];
3237
3417
  function createLocationRoutes() {
3238
3418
  const app = new Hono2();
3239
- registerApiRoutes(app, routes2);
3419
+ app.get(
3420
+ "/",
3421
+ async () => jsonResponse(locationDirectoryReportSchema, await readLocationDirectory())
3422
+ );
3423
+ app.get(
3424
+ "/:locationId",
3425
+ validateRequest(
3426
+ "param",
3427
+ locationParamsSchema,
3428
+ "Invalid route parameters."
3429
+ ),
3430
+ async (context) => {
3431
+ const { locationId } = context.req.valid("param");
3432
+ const viewer = context.get("viewer");
3433
+ const report = await readLocationDetail(
3434
+ locationId,
3435
+ viewer ? { viewer } : {}
3436
+ );
3437
+ if (!report) throwApiError(404, "Location not found.");
3438
+ return jsonResponse(locationDetailReportSchema, report);
3439
+ }
3440
+ );
3240
3441
  return app;
3241
3442
  }
3242
3443
 
@@ -3474,12 +3675,6 @@ async function readPeopleListFromSql() {
3474
3675
  async function readPeopleList() {
3475
3676
  return actorDirectoryReportSchema.parse(await readPeopleListFromSql());
3476
3677
  }
3477
- var list_default3 = defineApiRoute({
3478
- method: "get",
3479
- path: "/",
3480
- responseSchema: actorDirectoryReportSchema,
3481
- handler: readPeopleList
3482
- });
3483
3678
 
3484
3679
  // src/api/people/profile.query.ts
3485
3680
  import { and as and12, eq as eq13, gte as gte4, sql as sql10 } from "drizzle-orm";
@@ -3631,7 +3826,7 @@ async function readPeopleProfileFromSql(email, options = {}) {
3631
3826
  readConversationAccessFromSql(
3632
3827
  getDb(),
3633
3828
  recentConversationIds,
3634
- options.verifiedViewerEmail
3829
+ options.viewer
3635
3830
  ),
3636
3831
  readRootConversationMetricsFromSql(getDb(), recentConversationIds)
3637
3832
  ]);
@@ -3667,19 +3862,6 @@ async function readPeopleProfile(email, options = {}) {
3667
3862
  await readPeopleProfileFromSql(email, options)
3668
3863
  );
3669
3864
  }
3670
- var profile_default = defineApiRoute({
3671
- method: "get",
3672
- path: "/:email",
3673
- responseSchema: actorProfileReportSchema,
3674
- handler: async (c) => {
3675
- const { email } = parseParams(personParamsSchema, c.req.param());
3676
- const verifiedViewerEmail = c.get("verifiedViewerEmail");
3677
- return readPeopleProfile(
3678
- email,
3679
- verifiedViewerEmail ? { verifiedViewerEmail } : {}
3680
- );
3681
- }
3682
- });
3683
3865
 
3684
3866
  // src/api/people/spend.query.ts
3685
3867
  import { and as and13, eq as eq14, gte as gte5, lte as lte2, sql as sql11 } from "drizzle-orm";
@@ -3741,9 +3923,9 @@ async function readPersonalSpendFromSql(email, nowMs = Date.now()) {
3741
3923
 
3742
3924
  // src/api/people/spend.ts
3743
3925
  var PERSONAL_SPEND_CACHE_TTL_MS = 5 * 6e4;
3744
- function createPersonalSpendRoute() {
3926
+ function createPersonalSpendReader() {
3745
3927
  const cache = /* @__PURE__ */ new Map();
3746
- async function read(email) {
3928
+ return async function readPersonalSpend(email) {
3747
3929
  const normalizedEmail = email.trim().toLowerCase();
3748
3930
  const nowMs = Date.now();
3749
3931
  const cached = cache.get(normalizedEmail);
@@ -3763,39 +3945,39 @@ function createPersonalSpendRoute() {
3763
3945
  if (cache.get(normalizedEmail) === entry) cache.delete(normalizedEmail);
3764
3946
  throw error;
3765
3947
  }
3766
- }
3767
- return defineApiRoute({
3768
- method: "get",
3769
- path: "/me/spend",
3770
- responseSchema: personalSpendReportSchema,
3771
- handler: async (context) => {
3772
- const email = context.get("verifiedViewerEmail");
3773
- if (!email) throwApiError(403, "Verified viewer email required.");
3774
- return read(email);
3775
- }
3776
- });
3948
+ };
3777
3949
  }
3778
3950
 
3779
3951
  // src/api/people/routes.ts
3780
3952
  function createPeopleRoutes() {
3781
3953
  const app = new Hono3();
3782
- const routes5 = [
3783
- createPersonalSpendRoute(),
3784
- list_default3,
3785
- profile_default
3786
- ];
3787
- registerApiRoutes(app, routes5);
3954
+ const readPersonalSpend = createPersonalSpendReader();
3955
+ app.get("/me/spend", requireViewer, async (context) => {
3956
+ const viewer = context.get("viewer");
3957
+ return jsonResponse(
3958
+ personalSpendReportSchema,
3959
+ await readPersonalSpend(viewer.email)
3960
+ );
3961
+ });
3962
+ app.get(
3963
+ "/",
3964
+ async () => jsonResponse(actorDirectoryReportSchema, await readPeopleList())
3965
+ );
3966
+ app.get(
3967
+ "/:email",
3968
+ validateRequest("param", personParamsSchema, "Invalid route parameters."),
3969
+ async (context) => {
3970
+ const { email } = context.req.valid("param");
3971
+ const viewer = context.get("viewer");
3972
+ return jsonResponse(
3973
+ actorProfileReportSchema,
3974
+ await readPeopleProfile(email, viewer ? { viewer } : {})
3975
+ );
3976
+ }
3977
+ );
3788
3978
  return app;
3789
3979
  }
3790
3980
 
3791
- // src/api/plugin-reports.ts
3792
- var plugin_reports_default = defineApiRoute({
3793
- method: "get",
3794
- path: "/api/plugin-reports",
3795
- responseSchema: pluginOperationalReportFeedSchema,
3796
- handler: readPluginOperationalReportFeed
3797
- });
3798
-
3799
3981
  // src/api/personal-tokens/routes.ts
3800
3982
  import { Hono as Hono4 } from "hono";
3801
3983
 
@@ -3874,63 +4056,57 @@ async function authenticatePersonalToken(token2, now = /* @__PURE__ */ new Date(
3874
4056
  return match.ownerEmailNormalized;
3875
4057
  }
3876
4058
 
3877
- // src/api/personal-tokens/create.ts
3878
- var create_default = defineApiRoute({
3879
- method: "post",
3880
- path: "/",
3881
- responseSchema: createdPersonalTokenSchema,
3882
- handler: async (c) => {
3883
- const email = c.get("verifiedViewerEmail");
3884
- if (!email) throwApiError(403, "Verified viewer email required.");
3885
- let input;
3886
- try {
3887
- input = await c.req.json();
3888
- } catch (error) {
3889
- throwApiError(400, "Invalid request body.", error);
3890
- }
3891
- const body = parseBody(createPersonalTokenBodySchema, input);
3892
- return createPersonalToken({ email, name: body.name });
3893
- }
3894
- });
3895
-
3896
- // src/api/personal-tokens/list.ts
3897
- var list_default4 = defineApiRoute({
3898
- method: "get",
3899
- path: "/",
3900
- responseSchema: personalTokenListSchema,
3901
- handler: async (c) => {
3902
- const email = c.get("verifiedViewerEmail");
3903
- if (!email) throwApiError(403, "Verified viewer email required.");
3904
- return { tokens: await listPersonalTokens(email) };
3905
- }
3906
- });
3907
-
3908
- // src/api/personal-tokens/revoke.ts
3909
- var revoke_default = defineApiRoute({
3910
- method: "delete",
3911
- path: "/:id",
3912
- responseSchema: revokePersonalTokenResponseSchema,
3913
- handler: async (c) => {
3914
- const email = c.get("verifiedViewerEmail");
3915
- if (!email) throwApiError(403, "Verified viewer email required.");
3916
- const { id } = parseParams(personalTokenParamsSchema, c.req.param());
3917
- if (!await revokePersonalToken({ email, id })) {
3918
- throwApiError(404, "Personal API token not found.");
3919
- }
3920
- return { revoked: true };
3921
- }
3922
- });
3923
-
3924
4059
  // src/api/personal-tokens/routes.ts
3925
- var routes3 = [list_default4, create_default, revoke_default];
3926
4060
  function createPersonalTokenRoutes() {
3927
4061
  const app = new Hono4();
3928
- registerApiRoutes(app, routes3);
4062
+ app.get("/", requireViewer, async (context) => {
4063
+ const viewer = context.get("viewer");
4064
+ return jsonResponse(personalTokenListSchema, {
4065
+ tokens: await listPersonalTokens(viewer.email)
4066
+ });
4067
+ });
4068
+ app.post(
4069
+ "/",
4070
+ requireViewer,
4071
+ validateRequest(
4072
+ "json",
4073
+ createPersonalTokenBodySchema,
4074
+ "Invalid request body."
4075
+ ),
4076
+ async (context) => {
4077
+ const viewer = context.get("viewer");
4078
+ const body = context.req.valid("json");
4079
+ return jsonResponse(
4080
+ createdPersonalTokenSchema,
4081
+ await createPersonalToken({ email: viewer.email, name: body.name })
4082
+ );
4083
+ }
4084
+ );
4085
+ app.delete(
4086
+ "/:id",
4087
+ requireViewer,
4088
+ validateRequest(
4089
+ "param",
4090
+ personalTokenParamsSchema,
4091
+ "Invalid route parameters."
4092
+ ),
4093
+ async (context) => {
4094
+ const viewer = context.get("viewer");
4095
+ const { id } = context.req.valid("param");
4096
+ if (!await revokePersonalToken({ email: viewer.email, id })) {
4097
+ throwApiError(404, "Personal API token not found.");
4098
+ }
4099
+ return jsonResponse(revokePersonalTokenResponseSchema, {
4100
+ revoked: true
4101
+ });
4102
+ }
4103
+ );
3929
4104
  return app;
3930
4105
  }
3931
4106
 
3932
4107
  // src/api/user-pages/routes.ts
3933
4108
  import { Hono as Hono5 } from "hono";
4109
+ import { z as z5 } from "zod";
3934
4110
  import {
3935
4111
  pluginUserPageContentSchema as pluginUserPageContentSchema2,
3936
4112
  pluginUserPageInputSchema,
@@ -3968,8 +4144,8 @@ async function readPluginUserPage(input) {
3968
4144
  (candidate) => candidate.id === input.pageId
3969
4145
  );
3970
4146
  if (!plugin || !page) return void 0;
3971
- const viewer2 = await resolveViewerUser(input.email);
3972
- if (!viewer2) {
4147
+ const viewer = await resolveViewerUser(input.email);
4148
+ if (!viewer) {
3973
4149
  throw new Error("Authenticated viewer user could not be resolved");
3974
4150
  }
3975
4151
  const content = pluginUserPageContentSchema.parse(
@@ -3978,7 +4154,7 @@ async function readPluginUserPage(input) {
3978
4154
  db: getDb(),
3979
4155
  log: createPluginLogger(plugin.manifest.name),
3980
4156
  plugin: { name: plugin.manifest.name },
3981
- viewer: viewer2
4157
+ viewer
3982
4158
  },
3983
4159
  input.query
3984
4160
  )
@@ -3996,167 +4172,114 @@ async function readPluginUserPage(input) {
3996
4172
  }
3997
4173
 
3998
4174
  // src/api/user-pages/routes.ts
4175
+ var userPageQuerySchema = z5.object({
4176
+ cursor: z5.preprocess(
4177
+ (value) => value || void 0,
4178
+ pluginUserPageInputSchema.shape.cursor
4179
+ ),
4180
+ filter: z5.preprocess(
4181
+ (value) => value || void 0,
4182
+ pluginUserPageInputSchema.shape.filter
4183
+ ),
4184
+ limit: z5.preprocess(
4185
+ (value) => value === "" ? void 0 : value,
4186
+ z5.coerce.number().int().min(1).max(50).default(20)
4187
+ ),
4188
+ q: z5.preprocess(
4189
+ (value) => value || void 0,
4190
+ pluginUserPageInputSchema.shape.query
4191
+ )
4192
+ });
3999
4193
  function createUserPageRoutes() {
4000
4194
  const app = new Hono5();
4001
4195
  app.get(
4002
4196
  "/",
4003
4197
  () => jsonResponse(pluginUserPageLinksSchema2, readPluginUserPageLinks())
4004
4198
  );
4005
- app.get("/:pluginName/:pageId", async (context) => {
4006
- const email = context.get("verifiedViewerEmail")?.trim();
4007
- if (!email) {
4008
- return jsonResponse(
4009
- apiErrorSchema,
4010
- { error: "Authentication required." },
4011
- { status: 401 }
4012
- );
4013
- }
4014
- const pageInput = pluginUserPageInputSchema.safeParse({
4015
- cursor: context.req.query("cursor") || void 0,
4016
- filter: context.req.query("filter") || void 0,
4017
- limit: context.req.query("limit") ? Number(context.req.query("limit")) : 20,
4018
- query: context.req.query("q") || void 0
4019
- });
4020
- if (!pageInput.success) {
4021
- return jsonResponse(
4199
+ app.get(
4200
+ "/:pluginName/:pageId",
4201
+ requireViewer,
4202
+ validateRequest("query", userPageQuerySchema, "Invalid user page query."),
4203
+ async (context) => {
4204
+ const viewer = context.get("viewer");
4205
+ const { q, ...query } = context.req.valid("query");
4206
+ const page = await readPluginUserPage({
4207
+ email: viewer.email,
4208
+ pageId: context.req.param("pageId"),
4209
+ pluginName: context.req.param("pluginName"),
4210
+ query: { ...query, ...q === void 0 ? {} : { query: q } }
4211
+ });
4212
+ return page ? jsonResponse(pluginUserPageContentSchema2, page) : jsonResponse(
4022
4213
  apiErrorSchema,
4023
- { error: "Invalid user page query." },
4024
- { status: 400 }
4214
+ { error: "User page not found." },
4215
+ { status: 404 }
4025
4216
  );
4026
4217
  }
4027
- const page = await readPluginUserPage({
4028
- email,
4029
- pageId: context.req.param("pageId"),
4030
- pluginName: context.req.param("pluginName"),
4031
- query: pageInput.data
4032
- });
4033
- return page ? jsonResponse(pluginUserPageContentSchema2, page) : jsonResponse(
4034
- apiErrorSchema,
4035
- { error: "User page not found." },
4036
- { status: 404 }
4037
- );
4038
- });
4218
+ );
4039
4219
  return app;
4040
4220
  }
4041
4221
 
4042
4222
  // src/api/tasks/routes.ts
4043
4223
  import { Hono as Hono6 } from "hono";
4044
- async function viewer(context) {
4045
- const email = context.get("verifiedViewerEmail")?.trim();
4046
- return email ? await resolveViewerUser(email) : void 0;
4047
- }
4048
4224
  function createTaskRoutes() {
4049
4225
  const app = new Hono6();
4050
- app.get("/", async (context) => {
4051
- const user = await viewer(context);
4052
- return user ? jsonResponse(taskListSchema, await readViewerTasks(user)) : jsonResponse(
4053
- apiErrorSchema,
4054
- { error: "Authentication required." },
4055
- { status: 401 }
4056
- );
4226
+ app.get("/", requireViewer, async (context) => {
4227
+ const user = context.get("viewer");
4228
+ return jsonResponse(taskListSchema, await readViewerTasks(user));
4057
4229
  });
4058
- app.get("/runs", async (context) => {
4059
- const user = await viewer(context);
4060
- return user ? jsonResponse(taskRunListSchema, await readViewerTaskRuns(user)) : jsonResponse(
4061
- apiErrorSchema,
4062
- { error: "Authentication required." },
4063
- { status: 401 }
4064
- );
4230
+ app.get("/runs", requireViewer, async (context) => {
4231
+ const user = context.get("viewer");
4232
+ return jsonResponse(taskRunListSchema, await readViewerTaskRuns(user));
4065
4233
  });
4066
- app.get("/:kind/:id/executions", async (context) => {
4067
- const user = await viewer(context);
4068
- if (!user) {
4069
- return jsonResponse(
4070
- apiErrorSchema,
4071
- { error: "Authentication required." },
4072
- { status: 401 }
4073
- );
4074
- }
4075
- const params = taskParamsSchema.safeParse(context.req.param());
4076
- if (!params.success) {
4077
- return jsonResponse(
4078
- apiErrorSchema,
4079
- { error: "Invalid task." },
4080
- { status: 400 }
4081
- );
4082
- }
4083
- try {
4084
- return jsonResponse(
4085
- taskExecutionListSchema,
4086
- await readViewerTaskExecutions(user, params.data.kind, params.data.id)
4087
- );
4088
- } catch (error) {
4089
- if (error instanceof ViewerTaskNotFoundError) {
4234
+ app.get(
4235
+ "/:kind/:id/executions",
4236
+ requireViewer,
4237
+ validateRequest("param", taskParamsSchema, "Invalid task."),
4238
+ async (context) => {
4239
+ const user = context.get("viewer");
4240
+ const params = context.req.valid("param");
4241
+ try {
4090
4242
  return jsonResponse(
4091
- apiErrorSchema,
4092
- { error: error.message },
4093
- { status: 404 }
4243
+ taskExecutionListSchema,
4244
+ await readViewerTaskExecutions(user, params.kind, params.id)
4094
4245
  );
4246
+ } catch (error) {
4247
+ if (error instanceof ViewerTaskNotFoundError) {
4248
+ return jsonResponse(
4249
+ apiErrorSchema,
4250
+ { error: error.message },
4251
+ { status: 404 }
4252
+ );
4253
+ }
4254
+ throw error;
4095
4255
  }
4096
- throw error;
4097
4256
  }
4098
- });
4099
- app.delete("/:kind/:id", async (context) => {
4100
- const user = await viewer(context);
4101
- if (!user) {
4102
- return jsonResponse(
4103
- apiErrorSchema,
4104
- { error: "Authentication required." },
4105
- { status: 401 }
4106
- );
4107
- }
4108
- const params = taskParamsSchema.safeParse(context.req.param());
4109
- if (!params.success) {
4110
- return jsonResponse(
4111
- apiErrorSchema,
4112
- { error: "Invalid task." },
4113
- { status: 400 }
4114
- );
4115
- }
4116
- try {
4117
- await deleteViewerTask(user, params.data.kind, params.data.id);
4118
- return new Response(null, {
4119
- headers: { "cache-control": "no-store" },
4120
- status: 204
4121
- });
4122
- } catch (error) {
4123
- if (error instanceof ViewerTaskNotFoundError) {
4124
- return jsonResponse(
4125
- apiErrorSchema,
4126
- { error: error.message },
4127
- { status: 404 }
4128
- );
4257
+ );
4258
+ app.delete(
4259
+ "/:kind/:id",
4260
+ requireViewer,
4261
+ validateRequest("param", taskParamsSchema, "Invalid task."),
4262
+ async (context) => {
4263
+ const user = context.get("viewer");
4264
+ const params = context.req.valid("param");
4265
+ try {
4266
+ await deleteViewerTask(user, params.kind, params.id);
4267
+ return emptyResponse();
4268
+ } catch (error) {
4269
+ if (error instanceof ViewerTaskNotFoundError) {
4270
+ return jsonResponse(
4271
+ apiErrorSchema,
4272
+ { error: error.message },
4273
+ { status: 404 }
4274
+ );
4275
+ }
4276
+ throw error;
4129
4277
  }
4130
- throw error;
4131
4278
  }
4132
- });
4279
+ );
4133
4280
  return app;
4134
4281
  }
4135
4282
 
4136
- // src/api/plugins.ts
4137
- var plugins_default = defineApiRoute({
4138
- method: "get",
4139
- path: "/api/plugins",
4140
- responseSchema: pluginsSchema,
4141
- handler: readPlugins
4142
- });
4143
-
4144
- // src/api/runtime.ts
4145
- var runtime_default = defineApiRoute({
4146
- method: "get",
4147
- path: "/api/runtime",
4148
- responseSchema: runtimeInfoReportSchema,
4149
- handler: readRuntimeInfoReport
4150
- });
4151
-
4152
- // src/api/skills.ts
4153
- var skills_default = defineApiRoute({
4154
- method: "get",
4155
- path: "/api/skills",
4156
- responseSchema: skillReportsSchema,
4157
- handler: readSkillReports
4158
- });
4159
-
4160
4283
  // src/api/stats.ts
4161
4284
  var WINDOW_DAYS2 = 90;
4162
4285
  async function readStatsReport() {
@@ -4173,25 +4296,96 @@ async function readStatsReport() {
4173
4296
  windowStart: start
4174
4297
  });
4175
4298
  }
4176
- var stats_default2 = defineApiRoute({
4177
- method: "get",
4178
- path: "/api/stats",
4179
- responseSchema: statsReportSchema,
4180
- handler: readStatsReport
4181
- });
4299
+
4300
+ // src/reporting.ts
4301
+ import { readFileSync } from "fs";
4302
+ import path from "path";
4303
+ function readDescriptionText(home) {
4304
+ try {
4305
+ const raw = readFileSync(path.join(home, "DESCRIPTION.md"), "utf8").trim();
4306
+ return raw || void 0;
4307
+ } catch {
4308
+ return void 0;
4309
+ }
4310
+ }
4311
+ async function readSkillReports() {
4312
+ const { discoverSkills } = await import("./skills-AU5ST2CG.js");
4313
+ const skills = await discoverSkills();
4314
+ return skillReportsSchema.parse(
4315
+ skills.map((skill) => ({
4316
+ name: skill.name,
4317
+ pluginProvider: skill.pluginProvider
4318
+ }))
4319
+ );
4320
+ }
4321
+ async function readPlugins() {
4322
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-GAW5APVI.js");
4323
+ return pluginsSchema.parse(
4324
+ pluginCatalogRuntime.getProviders().map((plugin) => ({
4325
+ configKeys: plugin.manifest.configKeys ?? [],
4326
+ description: plugin.manifest.description,
4327
+ displayName: plugin.manifest.displayName,
4328
+ name: plugin.manifest.name
4329
+ }))
4330
+ );
4331
+ }
4332
+ async function readRuntimeInfoReport() {
4333
+ const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
4334
+ import("./discovery-GUFTFBTL.js"),
4335
+ import("./catalog-runtime-GAW5APVI.js"),
4336
+ readPlugins(),
4337
+ readSkillReports()
4338
+ ]);
4339
+ const home = homeDir();
4340
+ return runtimeInfoReportSchema.parse({
4341
+ cwd: process.cwd(),
4342
+ homeDir: home,
4343
+ descriptionText: readDescriptionText(home),
4344
+ providers: plugins.map((plugin) => plugin.name),
4345
+ skills,
4346
+ packagedContent: pluginCatalogRuntime.getPackageContent()
4347
+ });
4348
+ }
4349
+ async function readPluginOperationalReportFeed() {
4350
+ const nowMs = Date.now();
4351
+ const { getPluginOperationalReports } = await import("./agent-hooks-CWMCXAMT.js");
4352
+ return pluginOperationalReportFeedSchema.parse({
4353
+ source: "plugins",
4354
+ generatedAt: new Date(nowMs).toISOString(),
4355
+ reports: await getPluginOperationalReports(nowMs)
4356
+ });
4357
+ }
4182
4358
 
4183
4359
  // src/api.ts
4184
- var routes4 = [
4185
- health_default,
4186
- runtime_default,
4187
- plugins_default,
4188
- skills_default,
4189
- plugin_reports_default,
4190
- stats_default2
4191
- ];
4192
4360
  function createJuniorApi() {
4193
4361
  const app = new Hono7();
4194
- registerApiRoutes(app, routes4);
4362
+ app.get(
4363
+ "/api/health",
4364
+ async () => jsonResponse(healthReportSchema, readHealthReport())
4365
+ );
4366
+ app.get(
4367
+ "/api/runtime",
4368
+ async () => jsonResponse(runtimeInfoReportSchema, await readRuntimeInfoReport())
4369
+ );
4370
+ app.get(
4371
+ "/api/plugins",
4372
+ async () => jsonResponse(pluginsSchema, await readPlugins())
4373
+ );
4374
+ app.get(
4375
+ "/api/skills",
4376
+ async () => jsonResponse(skillReportsSchema, await readSkillReports())
4377
+ );
4378
+ app.get(
4379
+ "/api/plugin-reports",
4380
+ async () => jsonResponse(
4381
+ pluginOperationalReportFeedSchema,
4382
+ await readPluginOperationalReportFeed()
4383
+ )
4384
+ );
4385
+ app.get(
4386
+ "/api/stats",
4387
+ async () => jsonResponse(statsReportSchema, await readStatsReport())
4388
+ );
4195
4389
  app.route("/api/conversations", createConversationRoutes());
4196
4390
  app.route("/api/personal-tokens", createPersonalTokenRoutes());
4197
4391
  app.route("/api/people", createPeopleRoutes());
@@ -4207,10 +4401,27 @@ function createJuniorApi() {
4207
4401
  }
4208
4402
  )
4209
4403
  );
4404
+ app.onError((error, _context) => {
4405
+ if (error instanceof HTTPException2) {
4406
+ if (error.res) return error.res;
4407
+ return jsonResponse(
4408
+ apiErrorSchema,
4409
+ { error: error.message || "Request failed." },
4410
+ { status: error.status }
4411
+ );
4412
+ }
4413
+ logException(error, "api.unhandled.exception");
4414
+ return jsonResponse(
4415
+ apiErrorSchema,
4416
+ { error: "Internal Server Error" },
4417
+ { status: 500 }
4418
+ );
4419
+ });
4210
4420
  return app;
4211
4421
  }
4212
4422
  export {
4213
4423
  authenticatePersonalToken,
4214
4424
  createJuniorApi,
4215
- jsonResponse
4425
+ jsonResponse,
4426
+ resolveViewerUser
4216
4427
  };