@sentry/junior 0.184.0 → 0.186.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 (129) hide show
  1. package/dist/{agent-hooks-XTZVALGQ.js → agent-hooks-4B3Z4WRK.js} +13 -11
  2. package/dist/api/acp/schema.d.ts +20 -2
  3. package/dist/api/code/overview.d.ts +39 -0
  4. package/dist/api/locations/detail.d.ts +1 -1
  5. package/dist/api/people/code.d.ts +22 -0
  6. package/dist/api/schema/code.d.ts +46 -0
  7. package/dist/api/schema/conversation.d.ts +108 -20
  8. package/dist/api/schema/location.d.ts +1 -1
  9. package/dist/api/schema/person.d.ts +1 -1
  10. package/dist/api/schema.d.ts +4 -4
  11. package/dist/api/schema.js +12 -1
  12. package/dist/api.js +368 -78
  13. package/dist/app.js +1412 -538
  14. package/dist/{catalog-runtime-5357XR4F.js → catalog-runtime-AFJCAOHC.js} +4 -4
  15. package/dist/chat/api-turns/accepted-reply.d.ts +11 -0
  16. package/dist/chat/api-turns/cancellation.d.ts +3 -5
  17. package/dist/chat/api-turns/mailbox-input.d.ts +7 -0
  18. package/dist/chat/api-turns/routing.d.ts +18 -3
  19. package/dist/chat/api-turns/work.d.ts +4 -9
  20. package/dist/chat/app/conversation-work.d.ts +1 -1
  21. package/dist/chat/app/factory.d.ts +4 -0
  22. package/dist/chat/app/services.d.ts +4 -3
  23. package/dist/chat/conversations/history.d.ts +83 -9
  24. package/dist/chat/conversations/turn-lifecycle.d.ts +6 -3
  25. package/dist/chat/providers/slack/input.d.ts +32 -0
  26. package/dist/chat/{runtime → providers/slack}/processing-reaction.d.ts +6 -8
  27. package/dist/chat/{runtime/slack-resume.d.ts → providers/slack/resume.d.ts} +16 -19
  28. package/dist/chat/providers/slack/runtime.d.ts +1 -12
  29. package/dist/chat/providers/slack/turn.d.ts +10 -25
  30. package/dist/chat/resource-events/actor.d.ts +7 -2
  31. package/dist/chat/resource-events/app-publisher.d.ts +18 -0
  32. package/dist/chat/resource-events/catalog.d.ts +19 -0
  33. package/dist/chat/resource-events/ingest.d.ts +0 -1
  34. package/dist/chat/resource-events/notification.d.ts +28 -3
  35. package/dist/chat/resource-events/runtime-catalog.d.ts +7 -2
  36. package/dist/chat/resource-events/store.d.ts +2 -14
  37. package/dist/chat/resource-events/tool-support.d.ts +0 -5
  38. package/dist/chat/resource-events/workspace.d.ts +7 -3
  39. package/dist/chat/runtime/turn-execution.d.ts +7 -2
  40. package/dist/chat/sandbox/egress/credentials.d.ts +5 -4
  41. package/dist/chat/sandbox/egress/session.d.ts +10 -11
  42. package/dist/chat/sandbox/errors.d.ts +1 -1
  43. package/dist/chat/sandbox/ref.d.ts +1 -1
  44. package/dist/chat/sandbox/snapshot/events.d.ts +8 -1
  45. package/dist/chat/sandbox/snapshot/not-ready-error.d.ts +6 -1
  46. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  47. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  48. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  49. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  50. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  51. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  52. package/dist/chat/scheduled-tasks/types.d.ts +1 -1
  53. package/dist/chat/sentry-links.d.ts +2 -0
  54. package/dist/chat/services/provider-retry.d.ts +2 -0
  55. package/dist/chat/services/turn-failure-response.d.ts +4 -0
  56. package/dist/chat/services/turn-session-routing.d.ts +20 -5
  57. package/dist/chat/task-execution/paused-turn.d.ts +0 -2
  58. package/dist/chat/task-execution/slack-resource-event.d.ts +47 -0
  59. package/dist/chat/task-execution/slack-work.d.ts +0 -21
  60. package/dist/chat/task-execution/state.d.ts +2 -2
  61. package/dist/chat/task-execution/store.d.ts +1 -1
  62. package/dist/chat/task-execution/turn-wake.d.ts +12 -0
  63. package/dist/chat/tool-support/action-review.d.ts +1 -1
  64. package/dist/chat/tools/event-tasks.d.ts +1 -1
  65. package/dist/chat/tools/resource-events.d.ts +2 -2
  66. package/dist/chat/tools/search-conversation-events.d.ts +1 -1
  67. package/dist/chat/tools/types.d.ts +1 -1
  68. package/dist/chat/tools/watch-resource-events.d.ts +2 -2
  69. package/dist/{chunk-Z3QAXJR7.js → chunk-2IBDVE62.js} +12 -9
  70. package/dist/{chunk-4RONS3WR.js → chunk-2U7EPTRZ.js} +11 -5
  71. package/dist/{chunk-3S7ULUWW.js → chunk-3CVEE6LA.js} +1 -1
  72. package/dist/{chunk-IZDYYYGP.js → chunk-43LODHJM.js} +24 -384
  73. package/dist/{chunk-LHXIZLFF.js → chunk-64WF2GQ3.js} +1 -1
  74. package/dist/{chunk-YKX6MTLU.js → chunk-7EOWT62G.js} +1 -1
  75. package/dist/{chunk-UBOIHNTH.js → chunk-7EU4YSZ4.js} +362 -188
  76. package/dist/{chunk-UF6BUA5W.js → chunk-ACGWTJ74.js} +3 -3
  77. package/dist/{chunk-6UDPINOX.js → chunk-AD64YMEC.js} +19 -44
  78. package/dist/{chunk-MLSQX2EX.js → chunk-BRKB5R7P.js} +1 -1
  79. package/dist/{chunk-E57WPXWY.js → chunk-CM6RTYSO.js} +5 -5
  80. package/dist/{chunk-MAPJFUMV.js → chunk-CV5CPRVG.js} +3 -3
  81. package/dist/chunk-D4YV5TDV.js +45 -0
  82. package/dist/{chunk-HIPUIPJM.js → chunk-DHYZ4VKG.js} +383 -254
  83. package/dist/{chunk-DUKIDPF6.js → chunk-DOP3FIQC.js} +1 -1
  84. package/dist/{chunk-BT53RIGJ.js → chunk-EWRPUXE3.js} +12 -50
  85. package/dist/{chunk-BHYRYYLY.js → chunk-FFYDRLXW.js} +1 -1
  86. package/dist/chunk-GFBR4S3Q.js +406 -0
  87. package/dist/chunk-GQMQCY7H.js +58 -0
  88. package/dist/{chunk-JNO2GUDS.js → chunk-GSB3NTNS.js} +926 -1856
  89. package/dist/{chunk-2ZQPVX7C.js → chunk-H7X2G2KS.js} +25 -12
  90. package/dist/{chunk-WQFEWULF.js → chunk-HKFLRIBD.js} +2 -2
  91. package/dist/{chunk-SHJK75Z2.js → chunk-HWXP6RHG.js} +38 -4
  92. package/dist/{chunk-RF6J3DIO.js → chunk-INQHUE72.js} +84 -1
  93. package/dist/{chunk-4CNDGK25.js → chunk-JFPY6XFF.js} +911 -993
  94. package/dist/{chunk-6WGC3SO4.js → chunk-MCLWOBPN.js} +80 -18
  95. package/dist/{chunk-PJPRXO2T.js → chunk-NRMAOGRS.js} +1 -1
  96. package/dist/{chunk-ACPDVEJO.js → chunk-O6GQO7VP.js} +201 -218
  97. package/dist/{chunk-M5YWNMTE.js → chunk-OZG7E22Y.js} +24 -6
  98. package/dist/{chunk-WVIA7V3M.js → chunk-PFAZSRGR.js} +29 -1
  99. package/dist/{chunk-LCP4EDKR.js → chunk-PZZOGHVR.js} +14 -4
  100. package/dist/{chunk-QABCY33S.js → chunk-QBL2AVFF.js} +2 -2
  101. package/dist/{chunk-FKEZZMXS.js → chunk-QU3URVWG.js} +9 -94
  102. package/dist/{chunk-M3QYTBQ6.js → chunk-STOCCPWH.js} +11 -2
  103. package/dist/{chunk-K7VDC2QW.js → chunk-TXLBLGNQ.js} +6 -6
  104. package/dist/{chunk-JA43DV4Y.js → chunk-VOGOTEW7.js} +1 -1
  105. package/dist/{chunk-HUXFMDBZ.js → chunk-VQIWBJOP.js} +1 -1
  106. package/dist/{chunk-JM2COLGC.js → chunk-VQZYIVYN.js} +6 -6
  107. package/dist/{chunk-BCHYQS4B.js → chunk-VXL2LBPK.js} +1 -1
  108. package/dist/{chunk-6VOYP4M6.js → chunk-YXSBZRP7.js} +1 -1
  109. package/dist/cli/chat.js +35 -33
  110. package/dist/cli/check.js +5 -5
  111. package/dist/cli/plugins.js +14 -12
  112. package/dist/cli/snapshot-create.js +10 -9
  113. package/dist/cli/upgrade.js +7 -6
  114. package/dist/{db-MA52H62H.js → db-7XL4UPAE.js} +8 -6
  115. package/dist/instrumentation.js +3 -3
  116. package/dist/nitro.js +7 -6
  117. package/dist/{oauth-callback-server-DXGHKN6S.js → oauth-callback-server-RAOYHWDI.js} +30 -27
  118. package/dist/{runner-4IXUAQNK.js → runner-KMPBWRPM.js} +24 -19
  119. package/dist/{skills-X3DY7AXO.js → skills-IPCALGLQ.js} +5 -5
  120. package/dist/spawn-OSOWTMBG.js +41 -0
  121. package/dist/task-runner-IQNBA3RY.js +34 -0
  122. package/dist/{validation-Z6JXSTJS.js → validation-BSRCCX6N.js} +5 -5
  123. package/dist/version.js +1 -1
  124. package/dist/{work-OKWRFBDW.js → work-T6UFTOJY.js} +24 -19
  125. package/dist/worker-L35OG7VA.js +24 -0
  126. package/package.json +5 -5
  127. package/dist/spawn-43WGS6CH.js +0 -37
  128. package/dist/task-runner-VDRKITPV.js +0 -32
  129. package/dist/worker-B236AE72.js +0 -22
package/dist/api.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  cancelConversationPendingMessagesBodySchema,
10
10
  cancelConversationPendingMessagesResponseSchema,
11
11
  codeOverviewReportSchema,
12
+ codePersonReportSchema,
12
13
  conversationAttachmentParamsSchema,
13
14
  conversationDetailQuerySchema,
14
15
  conversationDetailReportSchema,
@@ -42,7 +43,7 @@ import {
42
43
  workspaceListSchema,
43
44
  workspaceParamsSchema,
44
45
  workspaceSchema
45
- } from "./chunk-WVIA7V3M.js";
46
+ } from "./chunk-PFAZSRGR.js";
46
47
  import {
47
48
  appendAndEnqueueApiConversationMessage,
48
49
  conversationArchiveTimes,
@@ -55,17 +56,20 @@ import {
55
56
  createAndEnqueueApiConversation,
56
57
  createVercelAttachmentStorage,
57
58
  formatSlackConversationRedactedLabel,
59
+ lookupSlackUser,
58
60
  readConversationAccessFromSql,
59
61
  readHealthReport,
60
62
  resolveSlackConversationContextFromThreadId,
61
63
  viewerConversationMembership,
62
64
  webActorFromEmail
63
- } from "./chunk-UBOIHNTH.js";
65
+ } from "./chunk-7EU4YSZ4.js";
64
66
  import {
65
67
  buildSentryConversationUrl,
68
+ buildSentryEventUrl,
66
69
  getWebAuthorization
67
- } from "./chunk-LCP4EDKR.js";
68
- import "./chunk-M5YWNMTE.js";
70
+ } from "./chunk-PZZOGHVR.js";
71
+ import "./chunk-GQMQCY7H.js";
72
+ import "./chunk-OZG7E22Y.js";
69
73
  import {
70
74
  readTaskExecutionByConversationId,
71
75
  readTaskExecutionDays,
@@ -73,9 +77,9 @@ import {
73
77
  readTaskExecutionSummaries,
74
78
  readTaskExecutions,
75
79
  readTaskRuns
76
- } from "./chunk-6WGC3SO4.js";
77
- import "./chunk-M3QYTBQ6.js";
78
- import "./chunk-E57WPXWY.js";
80
+ } from "./chunk-MCLWOBPN.js";
81
+ import "./chunk-STOCCPWH.js";
82
+ import "./chunk-CM6RTYSO.js";
79
83
  import {
80
84
  healthReportSchema,
81
85
  pluginOperationalReportFeedSchema,
@@ -83,7 +87,7 @@ import {
83
87
  runtimeInfoReportSchema,
84
88
  skillReportsSchema
85
89
  } from "./chunk-UTL3MHPC.js";
86
- import "./chunk-UF6BUA5W.js";
90
+ import "./chunk-ACGWTJ74.js";
87
91
  import {
88
92
  deleteEventTask,
89
93
  eventTaskBelongsToUser,
@@ -94,37 +98,38 @@ import {
94
98
  listEventTasksCreatedBy,
95
99
  listPublicEventTasksForTeams,
96
100
  listPublicScheduledTasksForTeams,
97
- lookupSlackUser,
98
101
  parseScheduledTaskRow,
99
102
  readLiveAttachment,
100
103
  readScheduledTask,
101
104
  readStats,
102
105
  saveScheduledTask
103
- } from "./chunk-HIPUIPJM.js";
106
+ } from "./chunk-DHYZ4VKG.js";
104
107
  import {
105
108
  parseSlackThreadId
106
- } from "./chunk-YKX6MTLU.js";
109
+ } from "./chunk-7EOWT62G.js";
110
+ import "./chunk-INQHUE72.js";
107
111
  import {
108
112
  cancelHumanFacingPendingMessages,
109
113
  getConversation
110
- } from "./chunk-Z3QAXJR7.js";
111
- import "./chunk-JA43DV4Y.js";
114
+ } from "./chunk-2IBDVE62.js";
115
+ import "./chunk-VOGOTEW7.js";
112
116
  import {
113
117
  agentTurnUsageSchema,
114
118
  fallbackShortTitle,
115
119
  hasAgentTurnUsage,
116
120
  renderJuniorNativeConversationEvent
117
- } from "./chunk-2ZQPVX7C.js";
121
+ } from "./chunk-H7X2G2KS.js";
122
+ import "./chunk-2LYHWMR4.js";
118
123
  import {
119
124
  getCachedSnapshot
120
- } from "./chunk-MAPJFUMV.js";
125
+ } from "./chunk-CV5CPRVG.js";
121
126
  import "./chunk-PDO5BLNM.js";
122
127
  import {
123
128
  getVercelConversationWorkQueue
124
129
  } from "./chunk-DKIJ2RP5.js";
125
130
  import "./chunk-JAL3CH4G.js";
126
131
  import "./chunk-7H2VZTWN.js";
127
- import "./chunk-MLSQX2EX.js";
132
+ import "./chunk-BRKB5R7P.js";
128
133
  import {
129
134
  SANDBOX_RUNTIME,
130
135
  WorkspaceValidationError,
@@ -144,17 +149,16 @@ import {
144
149
  updateWorkspace,
145
150
  withSlackRetries,
146
151
  workspaceRepoCheckoutPath
147
- } from "./chunk-4CNDGK25.js";
152
+ } from "./chunk-JFPY6XFF.js";
148
153
  import "./chunk-3Y47VMHV.js";
149
154
  import {
150
- decodeStoredConversationEvent,
151
155
  getConversationStore,
152
156
  getDb,
153
157
  getSqlExecutor,
154
158
  locationFromRow,
155
159
  parseSessionSource,
156
160
  resolveRootConversationId
157
- } from "./chunk-IZDYYYGP.js";
161
+ } from "./chunk-43LODHJM.js";
158
162
  import {
159
163
  conversationReadColumns,
160
164
  juniorApiTokens,
@@ -166,19 +170,23 @@ import {
166
170
  juniorIdentities,
167
171
  juniorSchedulerTasks,
168
172
  juniorUsers
169
- } from "./chunk-BCHYQS4B.js";
173
+ } from "./chunk-VXL2LBPK.js";
170
174
  import {
171
175
  create,
172
176
  hash
173
- } from "./chunk-SHJK75Z2.js";
174
- import "./chunk-DUKIDPF6.js";
177
+ } from "./chunk-HWXP6RHG.js";
178
+ import "./chunk-DOP3FIQC.js";
179
+ import {
180
+ decodeStoredConversationEvent
181
+ } from "./chunk-GFBR4S3Q.js";
175
182
  import {
176
183
  juniorDestinations
177
184
  } from "./chunk-VFUK3X5B.js";
178
- import "./chunk-6UDPINOX.js";
179
- import "./chunk-BHYRYYLY.js";
180
- import "./chunk-PJPRXO2T.js";
181
- import "./chunk-LHXIZLFF.js";
185
+ import "./chunk-AD64YMEC.js";
186
+ import "./chunk-D4YV5TDV.js";
187
+ import "./chunk-FFYDRLXW.js";
188
+ import "./chunk-NRMAOGRS.js";
189
+ import "./chunk-64WF2GQ3.js";
182
190
  import "./chunk-CEMMLV5L.js";
183
191
  import {
184
192
  parseSlackChannelId,
@@ -190,9 +198,9 @@ import "./chunk-TXKUKMVO.js";
190
198
  import {
191
199
  logException,
192
200
  logWarn
193
- } from "./chunk-3S7ULUWW.js";
201
+ } from "./chunk-3CVEE6LA.js";
194
202
  import "./chunk-PEQXYSSE.js";
195
- import "./chunk-6VOYP4M6.js";
203
+ import "./chunk-YXSBZRP7.js";
196
204
 
197
205
  // src/api.ts
198
206
  import { Hono as Hono9 } from "hono";
@@ -997,13 +1005,21 @@ function reportEventData(args) {
997
1005
  turnId: data.turnId,
998
1006
  state: data.outcome === "success" ? "succeeded" : "no_reply"
999
1007
  };
1000
- case "turn_failed":
1008
+ case "turn_failed": {
1009
+ const eventId = data.eventId;
1010
+ const sentryEventUrl = eventId ? buildSentryEventUrl(eventId) : void 0;
1001
1011
  return {
1002
1012
  type: "turn_lifecycle",
1003
1013
  turnId: data.turnId,
1004
1014
  state: "failed",
1005
- failureKind: data.failureCode === "delivery_failed" ? "delivery" : "agent"
1015
+ failureCode: data.failureCode,
1016
+ ...data.failureReason ? { failureReason: data.failureReason } : void 0,
1017
+ // Slack failure replies already show event ids, so keep them on the
1018
+ // conversation report too.
1019
+ ...eventId ? { eventId } : void 0,
1020
+ ...sentryEventUrl ? { sentryEventUrl } : void 0
1006
1021
  };
1022
+ }
1007
1023
  case "compaction":
1008
1024
  return {
1009
1025
  type: "compaction",
@@ -3500,17 +3516,40 @@ import { desc as desc4, eq as eq11, gte as gte2, or as or5, sql as sql11 } from
3500
3516
  import { z as z5 } from "zod";
3501
3517
  var DAY_MS = 24 * 60 * 60 * 1e3;
3502
3518
  var WINDOW_DAYS2 = 30;
3519
+ var ACTIVITY_DAYS = 90;
3503
3520
  var summaryRowSchema = z5.object({
3504
3521
  closed: z5.number().int().nonnegative(),
3522
+ costUsd: z5.number().nonnegative().nullable(),
3505
3523
  created: z5.number().int().nonnegative(),
3524
+ medianCostUsd: z5.number().nonnegative().nullable(),
3525
+ medianMergeTimeMs: z5.number().nonnegative().nullable(),
3506
3526
  merged: z5.number().int().nonnegative(),
3507
3527
  open: z5.number().int().nonnegative()
3508
- }).strict();
3509
- var repositoryRowSchema = summaryRowSchema.extend({
3528
+ }).strict().transform((row) => ({
3529
+ ...row,
3530
+ costUsd: row.costUsd ?? void 0,
3531
+ medianCostUsd: row.medianCostUsd ?? void 0,
3532
+ medianMergeTimeMs: row.medianMergeTimeMs ?? void 0
3533
+ }));
3534
+ var repositoryRowSchema = z5.object({
3535
+ closed: z5.number().int().nonnegative(),
3536
+ created: z5.number().int().nonnegative(),
3510
3537
  id: z5.string().min(1),
3538
+ medianCostUsd: z5.number().nonnegative().nullable(),
3539
+ merged: z5.number().int().nonnegative(),
3511
3540
  name: z5.string().min(1),
3541
+ open: z5.number().int().nonnegative(),
3512
3542
  provider: z5.string().min(1),
3513
3543
  url: z5.string().nullable()
3544
+ }).strict().transform((row) => ({
3545
+ ...row,
3546
+ medianCostUsd: row.medianCostUsd ?? void 0
3547
+ }));
3548
+ var activityDaySchema = z5.object({
3549
+ closed: z5.number().int().nonnegative(),
3550
+ created: z5.number().int().nonnegative(),
3551
+ date: z5.string().date(),
3552
+ merged: z5.number().int().nonnegative()
3514
3553
  }).strict();
3515
3554
  function queryRows(result) {
3516
3555
  if (typeof result !== "object" || result === null || !("rows" in result) || !Array.isArray(result.rows)) {
@@ -3518,41 +3557,252 @@ function queryRows(result) {
3518
3557
  }
3519
3558
  return result.rows;
3520
3559
  }
3521
- async function readCodeOverview(nowMs = Date.now()) {
3560
+ function startOfUtcDay(timestampMs) {
3561
+ const date = new Date(timestampMs);
3562
+ date.setUTCHours(0, 0, 0, 0);
3563
+ return date;
3564
+ }
3565
+ function mergeRate(merged, closed) {
3566
+ const completed = merged + closed;
3567
+ return completed > 0 ? merged / completed : void 0;
3568
+ }
3569
+ function conversationTreeCostExpr() {
3570
+ return sql11`
3571
+ coalesce((
3572
+ SELECT sum(
3573
+ CASE
3574
+ WHEN conversations.usage_json->'cost'->>'total' IS NOT NULL
3575
+ THEN (conversations.usage_json->'cost'->>'total')::double precision
3576
+ WHEN coalesce(
3577
+ conversations.usage_json->'cost'->>'input',
3578
+ conversations.usage_json->'cost'->>'output',
3579
+ conversations.usage_json->'cost'->>'cacheRead',
3580
+ conversations.usage_json->'cost'->>'cacheWrite'
3581
+ ) IS NOT NULL
3582
+ THEN coalesce((conversations.usage_json->'cost'->>'input')::double precision, 0)
3583
+ + coalesce((conversations.usage_json->'cost'->>'output')::double precision, 0)
3584
+ + coalesce((conversations.usage_json->'cost'->>'cacheRead')::double precision, 0)
3585
+ + coalesce((conversations.usage_json->'cost'->>'cacheWrite')::double precision, 0)
3586
+ ELSE 0
3587
+ END
3588
+ )
3589
+ FROM junior_conversations AS conversations
3590
+ WHERE conversations.root_conversation_id IN (
3591
+ SELECT coalesce(roots.root_conversation_id, roots.conversation_id)
3592
+ FROM junior_conversations AS roots
3593
+ WHERE roots.conversation_id = ANY (conversation_ids.ids)
3594
+ )
3595
+ ), 0::double precision)
3596
+ `;
3597
+ }
3598
+ function ownedByUserSql(conversationIds, userId) {
3599
+ return sql11`EXISTS (
3600
+ SELECT 1
3601
+ FROM unnest(${conversationIds}) AS linked(conversation_id)
3602
+ INNER JOIN junior_conversations AS conversations
3603
+ ON conversations.conversation_id = linked.conversation_id
3604
+ INNER JOIN junior_identities AS identities
3605
+ ON identities.id = conversations.actor_identity_id
3606
+ WHERE identities.user_id = ${userId}
3607
+ )`;
3608
+ }
3609
+ function ownershipFilter(userId) {
3610
+ if (!userId) return void 0;
3611
+ return ownedByUserSql(sql11`${juniorCodeChanges.conversationIds}`, userId);
3612
+ }
3613
+ async function readCodeWindows(args) {
3522
3614
  const db = getDb();
3523
- const windowEnd = new Date(nowMs);
3524
- const windowStart = new Date(nowMs - WINDOW_DAYS2 * DAY_MS);
3615
+ const windowEnd = new Date(args.nowMs);
3616
+ const windowStart = new Date(args.nowMs - WINDOW_DAYS2 * DAY_MS);
3617
+ const activityStart = startOfUtcDay(args.nowMs - (ACTIVITY_DAYS - 1) * DAY_MS);
3525
3618
  const changes = juniorCodeChanges;
3526
- const repositories = juniorCodeRepositories;
3527
- const [summaryResult, repositoryResult, recentChanges] = await Promise.all([
3619
+ const ownership = ownershipFilter(args.userId);
3620
+ const conversationTreeCost = conversationTreeCostExpr();
3621
+ const [summaryResult, activityResult] = await Promise.all([
3528
3622
  db.execute(sql11`
3623
+ WITH recent_changes AS (
3624
+ SELECT
3625
+ ${changes.id},
3626
+ ${changes.openedAt},
3627
+ ${changes.mergedAt},
3628
+ ${changes.closedAt},
3629
+ ${changes.state},
3630
+ ${changes.conversationIds} AS conversation_ids
3631
+ FROM ${changes}
3632
+ ${ownership ? sql11`WHERE ${ownership}` : sql11``}
3633
+ ), change_costs AS (
3634
+ SELECT
3635
+ recent_changes.id,
3636
+ recent_changes.opened_at,
3637
+ conversation_ids.ids AS ids,
3638
+ ${conversationTreeCost} AS cost_usd
3639
+ FROM recent_changes
3640
+ CROSS JOIN LATERAL (
3641
+ SELECT recent_changes.conversation_ids AS ids
3642
+ ) AS conversation_ids
3643
+ WHERE recent_changes.opened_at >= ${windowStart}
3644
+ )
3529
3645
  SELECT
3530
- count(*) FILTER (WHERE ${changes.openedAt} >= ${windowStart})::integer AS "created",
3531
- count(*) FILTER (WHERE ${changes.mergedAt} >= ${windowStart})::integer AS "merged",
3646
+ count(*) FILTER (WHERE recent_changes.opened_at >= ${windowStart})::integer AS "created",
3647
+ count(*) FILTER (WHERE recent_changes.merged_at >= ${windowStart})::integer AS "merged",
3532
3648
  count(*) FILTER (
3533
- WHERE ${changes.state} = 'closed' AND ${changes.closedAt} >= ${windowStart}
3649
+ WHERE recent_changes.state = 'closed'
3650
+ AND recent_changes.closed_at >= ${windowStart}
3534
3651
  )::integer AS "closed",
3535
- count(*) FILTER (WHERE ${changes.state} = 'open')::integer AS "open"
3536
- FROM ${changes}
3652
+ count(*) FILTER (WHERE recent_changes.state = 'open')::integer AS "open",
3653
+ (
3654
+ percentile_cont(0.5) WITHIN GROUP (
3655
+ ORDER BY extract(
3656
+ epoch FROM (recent_changes.merged_at - recent_changes.opened_at)
3657
+ ) * 1000
3658
+ ) FILTER (
3659
+ WHERE recent_changes.state = 'merged'
3660
+ AND recent_changes.merged_at >= ${windowStart}
3661
+ )
3662
+ )::double precision AS "medianMergeTimeMs",
3663
+ coalesce((
3664
+ SELECT ${conversationTreeCost}
3665
+ FROM (
3666
+ SELECT ARRAY(
3667
+ SELECT DISTINCT unnest(change_costs.ids)
3668
+ FROM change_costs
3669
+ ) AS ids
3670
+ ) AS conversation_ids
3671
+ ), 0)::double precision AS "costUsd",
3672
+ (
3673
+ SELECT percentile_cont(0.5) WITHIN GROUP (
3674
+ ORDER BY change_costs.cost_usd
3675
+ )
3676
+ FROM change_costs
3677
+ WHERE change_costs.cost_usd > 0
3678
+ )::double precision AS "medianCostUsd"
3679
+ FROM recent_changes
3537
3680
  `),
3538
3681
  db.execute(sql11`
3682
+ WITH days AS (
3683
+ SELECT generate_series(
3684
+ date_trunc('day', ${activityStart}::timestamptz AT TIME ZONE 'UTC'),
3685
+ date_trunc('day', ${windowEnd}::timestamptz AT TIME ZONE 'UTC'),
3686
+ interval '1 day'
3687
+ ) AS day
3688
+ ), daily AS (
3689
+ SELECT
3690
+ date_trunc('day', event_at AT TIME ZONE 'UTC') AS day,
3691
+ count(*) FILTER (WHERE kind = 'created')::integer AS created,
3692
+ count(*) FILTER (WHERE kind = 'merged')::integer AS merged,
3693
+ count(*) FILTER (WHERE kind = 'closed')::integer AS closed
3694
+ FROM (
3695
+ SELECT ${changes.openedAt} AS event_at, 'created'::text AS kind
3696
+ FROM ${changes}
3697
+ WHERE ${changes.openedAt} >= ${activityStart}
3698
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3699
+ UNION ALL
3700
+ SELECT ${changes.mergedAt} AS event_at, 'merged'::text AS kind
3701
+ FROM ${changes}
3702
+ WHERE ${changes.mergedAt} >= ${activityStart}
3703
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3704
+ UNION ALL
3705
+ SELECT ${changes.closedAt} AS event_at, 'closed'::text AS kind
3706
+ FROM ${changes}
3707
+ WHERE ${changes.state} = 'closed'
3708
+ AND ${changes.closedAt} >= ${activityStart}
3709
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3710
+ ) AS events
3711
+ GROUP BY date_trunc('day', event_at AT TIME ZONE 'UTC')
3712
+ )
3713
+ SELECT
3714
+ to_char(days.day, 'YYYY-MM-DD') AS "date",
3715
+ coalesce(daily.created, 0)::integer AS "created",
3716
+ coalesce(daily.merged, 0)::integer AS "merged",
3717
+ coalesce(daily.closed, 0)::integer AS "closed"
3718
+ FROM days
3719
+ LEFT JOIN daily ON daily.day = days.day
3720
+ ORDER BY days.day
3721
+ `)
3722
+ ]);
3723
+ const summary = summaryRowSchema.parse(
3724
+ queryRows(summaryResult)[0] ?? {
3725
+ closed: 0,
3726
+ costUsd: 0,
3727
+ created: 0,
3728
+ medianCostUsd: null,
3729
+ medianMergeTimeMs: null,
3730
+ merged: 0,
3731
+ open: 0
3732
+ }
3733
+ );
3734
+ return {
3735
+ activityDays: z5.array(activityDaySchema).parse(queryRows(activityResult)),
3736
+ summary: {
3737
+ ...summary,
3738
+ mergeRate: mergeRate(summary.merged, summary.closed)
3739
+ },
3740
+ windowEnd,
3741
+ windowStart
3742
+ };
3743
+ }
3744
+ async function readCodeOverview(nowMs = Date.now()) {
3745
+ const db = getDb();
3746
+ const changes = juniorCodeChanges;
3747
+ const repositories = juniorCodeRepositories;
3748
+ const windows = await readCodeWindows({ nowMs });
3749
+ const conversationTreeCost = conversationTreeCostExpr();
3750
+ const [repositoryResult, recentChanges] = await Promise.all([
3751
+ db.execute(sql11`
3752
+ WITH recent_changes AS (
3753
+ SELECT
3754
+ ${changes.id},
3755
+ ${changes.repositoryId},
3756
+ ${changes.openedAt},
3757
+ ${changes.mergedAt},
3758
+ ${changes.closedAt},
3759
+ ${changes.state},
3760
+ ${changes.conversationIds} AS conversation_ids
3761
+ FROM ${changes}
3762
+ WHERE ${changes.openedAt} >= ${windows.windowStart}
3763
+ OR ${changes.mergedAt} >= ${windows.windowStart}
3764
+ OR ${changes.closedAt} >= ${windows.windowStart}
3765
+ OR ${changes.state} = 'open'
3766
+ ), change_costs AS (
3767
+ SELECT
3768
+ recent_changes.repository_id,
3769
+ conversation_ids.ids AS ids,
3770
+ ${conversationTreeCost} AS cost_usd
3771
+ FROM recent_changes
3772
+ CROSS JOIN LATERAL (
3773
+ SELECT recent_changes.conversation_ids AS ids
3774
+ ) AS conversation_ids
3775
+ WHERE recent_changes.opened_at >= ${windows.windowStart}
3776
+ )
3539
3777
  SELECT
3540
3778
  ${repositories.id} AS "id",
3541
3779
  ${repositories.name} AS "name",
3542
3780
  ${repositories.provider} AS "provider",
3543
3781
  ${repositories.url} AS "url",
3544
- count(${changes.id}) FILTER (WHERE ${changes.openedAt} >= ${windowStart})::integer AS "created",
3545
- count(${changes.id}) FILTER (WHERE ${changes.mergedAt} >= ${windowStart})::integer AS "merged",
3546
- count(${changes.id}) FILTER (
3547
- WHERE ${changes.state} = 'closed' AND ${changes.closedAt} >= ${windowStart}
3782
+ count(recent_changes.id) FILTER (
3783
+ WHERE recent_changes.opened_at >= ${windows.windowStart}
3784
+ )::integer AS "created",
3785
+ count(recent_changes.id) FILTER (
3786
+ WHERE recent_changes.merged_at >= ${windows.windowStart}
3787
+ )::integer AS "merged",
3788
+ count(recent_changes.id) FILTER (
3789
+ WHERE recent_changes.state = 'closed'
3790
+ AND recent_changes.closed_at >= ${windows.windowStart}
3548
3791
  )::integer AS "closed",
3549
- count(${changes.id}) FILTER (WHERE ${changes.state} = 'open')::integer AS "open"
3792
+ count(recent_changes.id) FILTER (
3793
+ WHERE recent_changes.state = 'open'
3794
+ )::integer AS "open",
3795
+ (
3796
+ SELECT percentile_cont(0.5) WITHIN GROUP (
3797
+ ORDER BY change_costs.cost_usd
3798
+ )
3799
+ FROM change_costs
3800
+ WHERE change_costs.repository_id = ${repositories.id}
3801
+ AND change_costs.cost_usd > 0
3802
+ )::double precision AS "medianCostUsd"
3550
3803
  FROM ${repositories}
3551
- LEFT JOIN ${changes} ON ${changes.repositoryId} = ${repositories.id}
3552
- WHERE ${changes.openedAt} >= ${windowStart}
3553
- OR ${changes.mergedAt} >= ${windowStart}
3554
- OR ${changes.closedAt} >= ${windowStart}
3555
- OR ${changes.state} = 'open'
3804
+ INNER JOIN recent_changes
3805
+ ON recent_changes.repository_id = ${repositories.id}
3556
3806
  GROUP BY ${repositories.id}
3557
3807
  ORDER BY "merged" DESC, "created" DESC, ${repositories.name} ASC
3558
3808
  LIMIT 25
@@ -3570,16 +3820,15 @@ async function readCodeOverview(nowMs = Date.now()) {
3570
3820
  url: changes.url
3571
3821
  }).from(changes).innerJoin(repositories, eq11(changes.repositoryId, repositories.id)).where(
3572
3822
  or5(
3573
- gte2(changes.openedAt, windowStart),
3574
- gte2(changes.mergedAt, windowStart),
3575
- gte2(changes.closedAt, windowStart),
3823
+ gte2(changes.openedAt, windows.windowStart),
3824
+ gte2(changes.mergedAt, windows.windowStart),
3825
+ gte2(changes.closedAt, windows.windowStart),
3576
3826
  eq11(changes.state, "open")
3577
3827
  )
3578
3828
  ).orderBy(desc4(changes.updatedAt)).limit(25)
3579
3829
  ]);
3580
- const summary = summaryRowSchema.parse(queryRows(summaryResult)[0]);
3581
- const completed = summary.merged + summary.closed;
3582
3830
  return codeOverviewReportSchema.parse({
3831
+ activityDays: windows.activityDays,
3583
3832
  changes: recentChanges.map((change) => ({
3584
3833
  ...change,
3585
3834
  closedAt: change.closedAt?.toISOString(),
@@ -3588,17 +3837,29 @@ async function readCodeOverview(nowMs = Date.now()) {
3588
3837
  title: change.title ?? void 0,
3589
3838
  url: change.url ?? void 0
3590
3839
  })),
3591
- generatedAt: windowEnd.toISOString(),
3840
+ generatedAt: windows.windowEnd.toISOString(),
3592
3841
  repositories: z5.array(repositoryRowSchema).parse(queryRows(repositoryResult)).map((repository) => ({
3593
3842
  ...repository,
3843
+ mergeRate: mergeRate(repository.merged, repository.closed),
3594
3844
  url: repository.url ?? void 0
3595
3845
  })),
3596
- summary: {
3597
- ...summary,
3598
- mergeRate: completed > 0 ? summary.merged / completed : void 0
3599
- },
3600
- windowEnd: windowEnd.toISOString(),
3601
- windowStart: windowStart.toISOString()
3846
+ summary: windows.summary,
3847
+ windowEnd: windows.windowEnd.toISOString(),
3848
+ windowStart: windows.windowStart.toISOString()
3849
+ });
3850
+ }
3851
+ async function readPersonCodeOverview(args) {
3852
+ const nowMs = args.nowMs ?? Date.now();
3853
+ const windows = await readCodeWindows({
3854
+ nowMs,
3855
+ userId: args.userId
3856
+ });
3857
+ return codePersonReportSchema.parse({
3858
+ activityDays: windows.activityDays,
3859
+ generatedAt: windows.windowEnd.toISOString(),
3860
+ summary: windows.summary,
3861
+ windowEnd: windows.windowEnd.toISOString(),
3862
+ windowStart: windows.windowStart.toISOString()
3602
3863
  });
3603
3864
  }
3604
3865
 
@@ -3677,7 +3938,7 @@ function summaryFromRow(row, options = {}) {
3677
3938
 
3678
3939
  // src/api/locations/query.ts
3679
3940
  var RECENT_LIMIT = 25;
3680
- var ACTIVITY_DAYS = 90;
3941
+ var ACTIVITY_DAYS2 = 90;
3681
3942
  var treeConversation2 = alias4(
3682
3943
  juniorConversations,
3683
3944
  "location_tree_conversation"
@@ -3819,7 +4080,7 @@ function directoryActivityDays(rows, nowMs) {
3819
4080
  const end = new Date(nowMs);
3820
4081
  end.setUTCHours(0, 0, 0, 0);
3821
4082
  const start = new Date(end);
3822
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4083
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3823
4084
  const activity = [];
3824
4085
  for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
3825
4086
  const date = cursor.toISOString().slice(0, 10);
@@ -3838,7 +4099,7 @@ async function readLocationDirectoryFromSql() {
3838
4099
  const end = new Date(nowMs);
3839
4100
  end.setUTCHours(0, 0, 0, 0);
3840
4101
  const start = new Date(end);
3841
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4102
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3842
4103
  const [rows, activityRows] = await Promise.all([
3843
4104
  directoryRows(getDb()),
3844
4105
  directoryActivityRows(getDb(), start)
@@ -3906,7 +4167,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3906
4167
  const end = new Date(nowMs);
3907
4168
  end.setUTCHours(0, 0, 0, 0);
3908
4169
  const start = new Date(end);
3909
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4170
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3910
4171
  const where = publicLocationWhere(locationId);
3911
4172
  const activityDate = sql12`TO_CHAR(
3912
4173
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
@@ -4000,7 +4261,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
4000
4261
  addMetrics(item, row);
4001
4262
  actors.push(item);
4002
4263
  }
4003
- const activity = activityDays(days, nowMs, ACTIVITY_DAYS);
4264
+ const activity = activityDays(days, nowMs, ACTIVITY_DAYS2);
4004
4265
  const recentConversationIds = recentRows.map((row) => row.conversationId);
4005
4266
  const [accessByConversation, metricsByRoot] = await Promise.all([
4006
4267
  readConversationAccessFromSql(
@@ -4073,6 +4334,27 @@ function createLocationRoutes() {
4073
4334
  // src/api/people/routes.ts
4074
4335
  import { Hono as Hono4 } from "hono";
4075
4336
 
4337
+ // src/api/people/code.ts
4338
+ async function readPeopleCode(email) {
4339
+ const subject = await findUserByEmail(email);
4340
+ if (!subject) {
4341
+ const now = /* @__PURE__ */ new Date();
4342
+ return codePersonReportSchema.parse({
4343
+ activityDays: [],
4344
+ generatedAt: now.toISOString(),
4345
+ summary: {
4346
+ closed: 0,
4347
+ created: 0,
4348
+ merged: 0,
4349
+ open: 0
4350
+ },
4351
+ windowEnd: now.toISOString(),
4352
+ windowStart: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3).toISOString()
4353
+ });
4354
+ }
4355
+ return await readPersonCodeOverview({ userId: subject.id });
4356
+ }
4357
+
4076
4358
  // src/api/people/list.query.ts
4077
4359
  import { and as and13, eq as eq14, gte as gte4, sql as sql14 } from "drizzle-orm";
4078
4360
 
@@ -4080,7 +4362,7 @@ import { and as and13, eq as eq14, gte as gte4, sql as sql14 } from "drizzle-orm
4080
4362
  import { and as and12, asc as asc4, desc as desc6, eq as eq13, isNull as isNull6, notExists, or as or6, sql as sql13 } from "drizzle-orm";
4081
4363
  import { alias as alias5 } from "drizzle-orm/pg-core";
4082
4364
  var RECENT_LIMIT2 = 25;
4083
- var ACTIVITY_DAYS2 = 365;
4365
+ var ACTIVITY_DAYS3 = 365;
4084
4366
  var peopleTreeConversation = alias5(
4085
4367
  juniorConversations,
4086
4368
  "people_tree_conversation"
@@ -4163,7 +4445,7 @@ function activityDays2(days, nowMs) {
4163
4445
  const end = new Date(nowMs);
4164
4446
  end.setUTCHours(0, 0, 0, 0);
4165
4447
  const start = new Date(end);
4166
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4448
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4167
4449
  for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
4168
4450
  const date = cursor.toISOString().slice(0, 10);
4169
4451
  items.push(days.get(date) ?? emptyActivityDay(date));
@@ -4314,7 +4596,7 @@ async function readPeoplePluginReports(args) {
4314
4596
  source: "plugins"
4315
4597
  });
4316
4598
  }
4317
- const { getPluginProfileReports } = await import("./agent-hooks-XTZVALGQ.js");
4599
+ const { getPluginProfileReports } = await import("./agent-hooks-4B3Z4WRK.js");
4318
4600
  return pluginOperationalReportFeedSchema.parse({
4319
4601
  generatedAt: new Date(nowMs).toISOString(),
4320
4602
  reports: await getPluginProfileReports({
@@ -4332,7 +4614,7 @@ function emptyProfile(email, nowMs) {
4332
4614
  const end = new Date(nowMs);
4333
4615
  end.setUTCHours(0, 0, 0, 0);
4334
4616
  const start = new Date(end);
4335
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4617
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4336
4618
  return {
4337
4619
  activityDays: activityDays2(/* @__PURE__ */ new Map(), nowMs),
4338
4620
  generatedAt: new Date(nowMs).toISOString(),
@@ -4387,7 +4669,7 @@ async function readPeopleProfileFromSql(email, options = {}) {
4387
4669
  const end = new Date(nowMs);
4388
4670
  end.setUTCHours(0, 0, 0, 0);
4389
4671
  const start = new Date(end);
4390
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4672
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4391
4673
  const where = verifiedActorWhere(normalizedEmail);
4392
4674
  const metricsJoin = peopleTreeMetricsJoin();
4393
4675
  const surface = surfaceExpression();
@@ -4626,6 +4908,14 @@ function createPeopleRoutes() {
4626
4908
  );
4627
4909
  }
4628
4910
  );
4911
+ app.get(
4912
+ "/:email/code",
4913
+ validateRequest("param", personParamsSchema, "Invalid route parameters."),
4914
+ async (context) => {
4915
+ const { email } = context.req.valid("param");
4916
+ return jsonResponse(codePersonReportSchema, await readPeopleCode(email));
4917
+ }
4918
+ );
4629
4919
  app.get(
4630
4920
  "/:email",
4631
4921
  validateRequest("param", personParamsSchema, "Invalid route parameters."),
@@ -5110,7 +5400,7 @@ function readDescriptionText(home) {
5110
5400
  }
5111
5401
  }
5112
5402
  async function readSkillReports() {
5113
- const { discoverSkills } = await import("./skills-X3DY7AXO.js");
5403
+ const { discoverSkills } = await import("./skills-IPCALGLQ.js");
5114
5404
  const skills = await discoverSkills();
5115
5405
  return skillReportsSchema.parse(
5116
5406
  skills.map((skill) => ({
@@ -5120,7 +5410,7 @@ async function readSkillReports() {
5120
5410
  );
5121
5411
  }
5122
5412
  async function readPlugins() {
5123
- const { pluginCatalogRuntime } = await import("./catalog-runtime-5357XR4F.js");
5413
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-AFJCAOHC.js");
5124
5414
  return pluginsSchema.parse(
5125
5415
  pluginCatalogRuntime.getProviders().map((plugin) => ({
5126
5416
  configKeys: plugin.manifest.configKeys ?? [],
@@ -5133,7 +5423,7 @@ async function readPlugins() {
5133
5423
  async function readRuntimeInfoReport() {
5134
5424
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
5135
5425
  import("./discovery-DWVDM7UK.js"),
5136
- import("./catalog-runtime-5357XR4F.js"),
5426
+ import("./catalog-runtime-AFJCAOHC.js"),
5137
5427
  readPlugins(),
5138
5428
  readSkillReports()
5139
5429
  ]);
@@ -5149,7 +5439,7 @@ async function readRuntimeInfoReport() {
5149
5439
  }
5150
5440
  async function readPluginOperationalReportFeed() {
5151
5441
  const nowMs = Date.now();
5152
- const { getPluginOperationalReports } = await import("./agent-hooks-XTZVALGQ.js");
5442
+ const { getPluginOperationalReports } = await import("./agent-hooks-4B3Z4WRK.js");
5153
5443
  return pluginOperationalReportFeedSchema.parse({
5154
5444
  source: "plugins",
5155
5445
  generatedAt: new Date(nowMs).toISOString(),