@sentry/junior 0.183.0 → 0.185.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 (122) hide show
  1. package/dist/{agent-hooks-SNRBZYAU.js → agent-hooks-NGZQLL5O.js} +13 -11
  2. package/dist/api/acp/auth.d.ts +46 -0
  3. package/dist/api/acp/authorization-page.d.ts +11 -0
  4. package/dist/api/acp/conversations.d.ts +70 -0
  5. package/dist/api/acp/errors.d.ts +6 -0
  6. package/dist/api/acp/route.d.ts +16 -0
  7. package/dist/api/acp/schema.d.ts +115 -0
  8. package/dist/api/acp/sleep.d.ts +2 -0
  9. package/dist/api/acp/state.d.ts +22 -0
  10. package/dist/api/acp/transport.d.ts +113 -0
  11. package/dist/api/code/overview.d.ts +39 -0
  12. package/dist/api/locations/detail.d.ts +1 -1
  13. package/dist/api/people/code.d.ts +22 -0
  14. package/dist/api/schema/code.d.ts +46 -0
  15. package/dist/api/schema/conversation.d.ts +100 -20
  16. package/dist/api/schema/location.d.ts +1 -1
  17. package/dist/api/schema/person.d.ts +1 -1
  18. package/dist/api/schema.d.ts +4 -4
  19. package/dist/api/schema.js +12 -1
  20. package/dist/api.js +358 -76
  21. package/dist/app.d.ts +0 -4
  22. package/dist/app.js +2450 -493
  23. package/dist/{catalog-runtime-3I3JOIIH.js → catalog-runtime-AAEAO2RL.js} +4 -4
  24. package/dist/chat/api-turns/accepted-reply.d.ts +11 -0
  25. package/dist/chat/api-turns/cancellation.d.ts +3 -5
  26. package/dist/chat/api-turns/work.d.ts +4 -9
  27. package/dist/chat/app/conversation-work.d.ts +1 -1
  28. package/dist/chat/app/factory.d.ts +4 -0
  29. package/dist/chat/app/services.d.ts +4 -3
  30. package/dist/chat/conversations/history.d.ts +83 -9
  31. package/dist/chat/conversations/turn-lifecycle.d.ts +4 -3
  32. package/dist/chat/providers/slack/input.d.ts +32 -0
  33. package/dist/chat/{runtime → providers/slack}/processing-reaction.d.ts +6 -8
  34. package/dist/chat/{runtime/slack-resume.d.ts → providers/slack/resume.d.ts} +16 -19
  35. package/dist/chat/providers/slack/runtime.d.ts +1 -10
  36. package/dist/chat/providers/slack/turn.d.ts +11 -24
  37. package/dist/chat/resource-events/store.d.ts +1 -1
  38. package/dist/chat/resource-events/tool-support.d.ts +0 -5
  39. package/dist/chat/runtime/turn-execution.d.ts +7 -2
  40. package/dist/chat/sandbox/egress/credentials.d.ts +3 -3
  41. package/dist/chat/sandbox/egress/session.d.ts +5 -6
  42. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  43. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  44. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  45. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  46. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  47. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  48. package/dist/chat/scheduled-tasks/types.d.ts +1 -1
  49. package/dist/chat/services/provider-retry.d.ts +2 -0
  50. package/dist/chat/services/turn-failure-response.d.ts +4 -0
  51. package/dist/chat/task-execution/paused-turn.d.ts +0 -2
  52. package/dist/chat/task-execution/state.d.ts +2 -2
  53. package/dist/chat/task-execution/store.d.ts +1 -1
  54. package/dist/chat/task-execution/turn-wake.d.ts +12 -0
  55. package/dist/chat/tool-support/action-review.d.ts +1 -1
  56. package/dist/chat/tools/resource-events.d.ts +1 -1
  57. package/dist/chat/tools/search-conversation-events.d.ts +1 -1
  58. package/dist/chat/tools/types.d.ts +1 -1
  59. package/dist/chat/tools/watch-resource-events.d.ts +2 -2
  60. package/dist/{chunk-2EPYYVJC.js → chunk-4HA36IWS.js} +9 -94
  61. package/dist/{chunk-WVIA7V3M.js → chunk-4K25ODAO.js} +27 -1
  62. package/dist/{chunk-V64JCZUV.js → chunk-4S47VHJR.js} +3 -3
  63. package/dist/{chunk-QZV76CM4.js → chunk-6DOAUXLD.js} +231 -198
  64. package/dist/{chunk-M3QYTBQ6.js → chunk-6Q7YZLCL.js} +2 -1
  65. package/dist/{chunk-W46NIB5H.js → chunk-7GAP2XG4.js} +2 -3
  66. package/dist/{chunk-EENQGEIP.js → chunk-7MHNIGHP.js} +1 -1
  67. package/dist/{chunk-AX3UDIMA.js → chunk-ABAX75GG.js} +10 -384
  68. package/dist/{chunk-KTXOGLNE.js → chunk-ATUWN6Z3.js} +2 -2
  69. package/dist/{chunk-3HJ4F4L7.js → chunk-C6TS47CQ.js} +4 -4
  70. package/dist/chunk-D4YV5TDV.js +45 -0
  71. package/dist/{chunk-HUK5XIZL.js → chunk-D63IUQQE.js} +4 -4
  72. package/dist/{chunk-WOKJRLM2.js → chunk-DAPC2HTM.js} +24 -6
  73. package/dist/{chunk-2ADFGSUS.js → chunk-EIJCPJOY.js} +1 -1
  74. package/dist/{chunk-X52ESOBP.js → chunk-EJYP3NQ4.js} +4 -4
  75. package/dist/{chunk-35PM4YY5.js → chunk-EWEH2PJW.js} +6 -6
  76. package/dist/{chunk-W5JMPEBO.js → chunk-FFHXG2FX.js} +13 -11
  77. package/dist/{chunk-FJMTMGBC.js → chunk-FTVMXDNN.js} +1 -1
  78. package/dist/chunk-GFBR4S3Q.js +406 -0
  79. package/dist/{chunk-E3OBGRHN.js → chunk-GJTIZQGJ.js} +1 -1
  80. package/dist/{chunk-IO6KFXYE.js → chunk-HJPC32TQ.js} +12 -50
  81. package/dist/{chunk-RF6J3DIO.js → chunk-INQHUE72.js} +84 -1
  82. package/dist/{chunk-LMNMMESF.js → chunk-LRI5IRGY.js} +19 -44
  83. package/dist/{chunk-6UXDTPEN.js → chunk-LVK4WJKM.js} +1 -1
  84. package/dist/{chunk-AYW7J6K4.js → chunk-MWOH6CIK.js} +178 -148
  85. package/dist/{chunk-LZBPGX5A.js → chunk-OMQ2J6F6.js} +1 -1
  86. package/dist/{chunk-G2B2XZQW.js → chunk-PNYJOEVV.js} +1 -1
  87. package/dist/{chunk-37IQ6DGU.js → chunk-QCT4226Q.js} +3 -3
  88. package/dist/{chunk-R767Y7QV.js → chunk-QZKHHG3B.js} +34 -8
  89. package/dist/{chunk-VOV3SCM3.js → chunk-TVVAMOLS.js} +2 -2
  90. package/dist/{chunk-56XONJPF.js → chunk-U4CTAVWN.js} +1 -1
  91. package/dist/{chunk-4WGFWHWP.js → chunk-UUULGCKK.js} +1 -1
  92. package/dist/{chunk-AHMWPFIW.js → chunk-VH4IU3UC.js} +928 -984
  93. package/dist/chunk-W7GCLNIV.js +58 -0
  94. package/dist/{chunk-24OJZ5OU.js → chunk-WLKWN4CD.js} +22 -12
  95. package/dist/{chunk-T5MTGX2E.js → chunk-X2SS6BF7.js} +201 -210
  96. package/dist/{chunk-SQHFVIMZ.js → chunk-X7NCJQAA.js} +1 -1
  97. package/dist/{chunk-PLZT45VC.js → chunk-Y5G23T2D.js} +5 -5
  98. package/dist/{chunk-3L7CWDQQ.js → chunk-ZIF2S464.js} +1 -1
  99. package/dist/{chunk-QI5JW5AG.js → chunk-ZKKGFUWM.js} +6 -6
  100. package/dist/cli/chat.js +35 -33
  101. package/dist/cli/check.js +5 -5
  102. package/dist/cli/plugins.js +14 -12
  103. package/dist/cli/snapshot-create.js +10 -9
  104. package/dist/cli/upgrade.js +7 -6
  105. package/dist/{db-K2VMNLHQ.js → db-TRJ77FAU.js} +8 -6
  106. package/dist/instrumentation.js +3 -3
  107. package/dist/nitro.js +7 -6
  108. package/dist/{oauth-callback-server-DZZMTKSS.js → oauth-callback-server-XD5SV3MS.js} +30 -27
  109. package/dist/{runner-IIAKWDXI.js → runner-KFX7NG6D.js} +24 -19
  110. package/dist/{skills-EXIQ7BP5.js → skills-J2AH26CW.js} +5 -5
  111. package/dist/spawn-VU3CSRI6.js +41 -0
  112. package/dist/task-runner-5JG3VIIY.js +34 -0
  113. package/dist/{validation-XUGIWBJF.js → validation-O5J4VOPF.js} +5 -5
  114. package/dist/version.js +1 -1
  115. package/dist/{work-G4RLJ634.js → work-ECPQTG3U.js} +24 -19
  116. package/dist/worker-RS3DXUDO.js +24 -0
  117. package/package.json +5 -6
  118. package/dist/app-adapter.d.ts +0 -85
  119. package/dist/chat/api-turns/adapter-conversations.d.ts +0 -15
  120. package/dist/spawn-ZR6OOEAN.js +0 -37
  121. package/dist/task-runner-C52HYCVD.js +0 -32
  122. package/dist/worker-R2JEHK67.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-4K25ODAO.js";
46
47
  import {
47
48
  appendAndEnqueueApiConversationMessage,
48
49
  conversationArchiveTimes,
@@ -60,12 +61,13 @@ import {
60
61
  resolveSlackConversationContextFromThreadId,
61
62
  viewerConversationMembership,
62
63
  webActorFromEmail
63
- } from "./chunk-AYW7J6K4.js";
64
+ } from "./chunk-MWOH6CIK.js";
64
65
  import {
65
66
  buildSentryConversationUrl,
66
67
  getWebAuthorization
67
- } from "./chunk-4WGFWHWP.js";
68
- import "./chunk-WOKJRLM2.js";
68
+ } from "./chunk-UUULGCKK.js";
69
+ import "./chunk-W7GCLNIV.js";
70
+ import "./chunk-DAPC2HTM.js";
69
71
  import {
70
72
  readTaskExecutionByConversationId,
71
73
  readTaskExecutionDays,
@@ -73,9 +75,9 @@ import {
73
75
  readTaskExecutionSummaries,
74
76
  readTaskExecutions,
75
77
  readTaskRuns
76
- } from "./chunk-W5JMPEBO.js";
77
- import "./chunk-M3QYTBQ6.js";
78
- import "./chunk-PLZT45VC.js";
78
+ } from "./chunk-FFHXG2FX.js";
79
+ import "./chunk-6Q7YZLCL.js";
80
+ import "./chunk-Y5G23T2D.js";
79
81
  import {
80
82
  healthReportSchema,
81
83
  pluginOperationalReportFeedSchema,
@@ -83,7 +85,7 @@ import {
83
85
  runtimeInfoReportSchema,
84
86
  skillReportsSchema
85
87
  } from "./chunk-UTL3MHPC.js";
86
- import "./chunk-V64JCZUV.js";
88
+ import "./chunk-4S47VHJR.js";
87
89
  import {
88
90
  deleteEventTask,
89
91
  eventTaskBelongsToUser,
@@ -100,31 +102,33 @@ import {
100
102
  readScheduledTask,
101
103
  readStats,
102
104
  saveScheduledTask
103
- } from "./chunk-R767Y7QV.js";
105
+ } from "./chunk-QZKHHG3B.js";
104
106
  import {
105
107
  parseSlackThreadId
106
- } from "./chunk-SQHFVIMZ.js";
108
+ } from "./chunk-X7NCJQAA.js";
109
+ import "./chunk-INQHUE72.js";
107
110
  import {
108
111
  cancelHumanFacingPendingMessages,
109
112
  getConversation
110
- } from "./chunk-HUK5XIZL.js";
111
- import "./chunk-56XONJPF.js";
113
+ } from "./chunk-D63IUQQE.js";
114
+ import "./chunk-U4CTAVWN.js";
112
115
  import {
113
116
  agentTurnUsageSchema,
114
117
  fallbackShortTitle,
115
118
  hasAgentTurnUsage,
116
119
  renderJuniorNativeConversationEvent
117
- } from "./chunk-24OJZ5OU.js";
120
+ } from "./chunk-WLKWN4CD.js";
121
+ import "./chunk-2LYHWMR4.js";
118
122
  import {
119
123
  getCachedSnapshot
120
- } from "./chunk-37IQ6DGU.js";
124
+ } from "./chunk-QCT4226Q.js";
121
125
  import "./chunk-PDO5BLNM.js";
122
126
  import {
123
127
  getVercelConversationWorkQueue
124
128
  } from "./chunk-DKIJ2RP5.js";
125
129
  import "./chunk-JAL3CH4G.js";
126
130
  import "./chunk-7H2VZTWN.js";
127
- import "./chunk-FJMTMGBC.js";
131
+ import "./chunk-FTVMXDNN.js";
128
132
  import {
129
133
  SANDBOX_RUNTIME,
130
134
  WorkspaceValidationError,
@@ -144,17 +148,16 @@ import {
144
148
  updateWorkspace,
145
149
  withSlackRetries,
146
150
  workspaceRepoCheckoutPath
147
- } from "./chunk-AHMWPFIW.js";
151
+ } from "./chunk-VH4IU3UC.js";
148
152
  import "./chunk-3Y47VMHV.js";
149
153
  import {
150
- decodeStoredConversationEvent,
151
154
  getConversationStore,
152
155
  getDb,
153
156
  getSqlExecutor,
154
157
  locationFromRow,
155
158
  parseSessionSource,
156
159
  resolveRootConversationId
157
- } from "./chunk-AX3UDIMA.js";
160
+ } from "./chunk-ABAX75GG.js";
158
161
  import {
159
162
  conversationReadColumns,
160
163
  juniorApiTokens,
@@ -166,19 +169,23 @@ import {
166
169
  juniorIdentities,
167
170
  juniorSchedulerTasks,
168
171
  juniorUsers
169
- } from "./chunk-6UXDTPEN.js";
172
+ } from "./chunk-LVK4WJKM.js";
170
173
  import {
171
174
  create,
172
175
  hash
173
- } from "./chunk-X52ESOBP.js";
174
- import "./chunk-G2B2XZQW.js";
176
+ } from "./chunk-EJYP3NQ4.js";
177
+ import "./chunk-PNYJOEVV.js";
178
+ import {
179
+ decodeStoredConversationEvent
180
+ } from "./chunk-GFBR4S3Q.js";
175
181
  import {
176
182
  juniorDestinations
177
183
  } from "./chunk-VFUK3X5B.js";
178
- import "./chunk-LMNMMESF.js";
179
- import "./chunk-2ADFGSUS.js";
180
- import "./chunk-E3OBGRHN.js";
181
- import "./chunk-EENQGEIP.js";
184
+ import "./chunk-LRI5IRGY.js";
185
+ import "./chunk-D4YV5TDV.js";
186
+ import "./chunk-EIJCPJOY.js";
187
+ import "./chunk-GJTIZQGJ.js";
188
+ import "./chunk-7MHNIGHP.js";
182
189
  import "./chunk-CEMMLV5L.js";
183
190
  import {
184
191
  parseSlackChannelId,
@@ -190,9 +197,9 @@ import "./chunk-TXKUKMVO.js";
190
197
  import {
191
198
  logException,
192
199
  logWarn
193
- } from "./chunk-LZBPGX5A.js";
200
+ } from "./chunk-OMQ2J6F6.js";
194
201
  import "./chunk-PEQXYSSE.js";
195
- import "./chunk-W46NIB5H.js";
202
+ import "./chunk-7GAP2XG4.js";
196
203
 
197
204
  // src/api.ts
198
205
  import { Hono as Hono9 } from "hono";
@@ -1002,7 +1009,8 @@ function reportEventData(args) {
1002
1009
  type: "turn_lifecycle",
1003
1010
  turnId: data.turnId,
1004
1011
  state: "failed",
1005
- failureKind: data.failureCode === "delivery_failed" ? "delivery" : "agent"
1012
+ failureCode: data.failureCode,
1013
+ ...data.failureReason ? { failureReason: data.failureReason } : void 0
1006
1014
  };
1007
1015
  case "compaction":
1008
1016
  return {
@@ -3500,17 +3508,40 @@ import { desc as desc4, eq as eq11, gte as gte2, or as or5, sql as sql11 } from
3500
3508
  import { z as z5 } from "zod";
3501
3509
  var DAY_MS = 24 * 60 * 60 * 1e3;
3502
3510
  var WINDOW_DAYS2 = 30;
3511
+ var ACTIVITY_DAYS = 90;
3503
3512
  var summaryRowSchema = z5.object({
3504
3513
  closed: z5.number().int().nonnegative(),
3514
+ costUsd: z5.number().nonnegative().nullable(),
3505
3515
  created: z5.number().int().nonnegative(),
3516
+ medianCostUsd: z5.number().nonnegative().nullable(),
3517
+ medianMergeTimeMs: z5.number().nonnegative().nullable(),
3506
3518
  merged: z5.number().int().nonnegative(),
3507
3519
  open: z5.number().int().nonnegative()
3508
- }).strict();
3509
- var repositoryRowSchema = summaryRowSchema.extend({
3520
+ }).strict().transform((row) => ({
3521
+ ...row,
3522
+ costUsd: row.costUsd ?? void 0,
3523
+ medianCostUsd: row.medianCostUsd ?? void 0,
3524
+ medianMergeTimeMs: row.medianMergeTimeMs ?? void 0
3525
+ }));
3526
+ var repositoryRowSchema = z5.object({
3527
+ closed: z5.number().int().nonnegative(),
3528
+ created: z5.number().int().nonnegative(),
3510
3529
  id: z5.string().min(1),
3530
+ medianCostUsd: z5.number().nonnegative().nullable(),
3531
+ merged: z5.number().int().nonnegative(),
3511
3532
  name: z5.string().min(1),
3533
+ open: z5.number().int().nonnegative(),
3512
3534
  provider: z5.string().min(1),
3513
3535
  url: z5.string().nullable()
3536
+ }).strict().transform((row) => ({
3537
+ ...row,
3538
+ medianCostUsd: row.medianCostUsd ?? void 0
3539
+ }));
3540
+ var activityDaySchema = z5.object({
3541
+ closed: z5.number().int().nonnegative(),
3542
+ created: z5.number().int().nonnegative(),
3543
+ date: z5.string().date(),
3544
+ merged: z5.number().int().nonnegative()
3514
3545
  }).strict();
3515
3546
  function queryRows(result) {
3516
3547
  if (typeof result !== "object" || result === null || !("rows" in result) || !Array.isArray(result.rows)) {
@@ -3518,41 +3549,252 @@ function queryRows(result) {
3518
3549
  }
3519
3550
  return result.rows;
3520
3551
  }
3521
- async function readCodeOverview(nowMs = Date.now()) {
3552
+ function startOfUtcDay(timestampMs) {
3553
+ const date = new Date(timestampMs);
3554
+ date.setUTCHours(0, 0, 0, 0);
3555
+ return date;
3556
+ }
3557
+ function mergeRate(merged, closed) {
3558
+ const completed = merged + closed;
3559
+ return completed > 0 ? merged / completed : void 0;
3560
+ }
3561
+ function conversationTreeCostExpr() {
3562
+ return sql11`
3563
+ coalesce((
3564
+ SELECT sum(
3565
+ CASE
3566
+ WHEN conversations.usage_json->'cost'->>'total' IS NOT NULL
3567
+ THEN (conversations.usage_json->'cost'->>'total')::double precision
3568
+ WHEN coalesce(
3569
+ conversations.usage_json->'cost'->>'input',
3570
+ conversations.usage_json->'cost'->>'output',
3571
+ conversations.usage_json->'cost'->>'cacheRead',
3572
+ conversations.usage_json->'cost'->>'cacheWrite'
3573
+ ) IS NOT NULL
3574
+ THEN coalesce((conversations.usage_json->'cost'->>'input')::double precision, 0)
3575
+ + coalesce((conversations.usage_json->'cost'->>'output')::double precision, 0)
3576
+ + coalesce((conversations.usage_json->'cost'->>'cacheRead')::double precision, 0)
3577
+ + coalesce((conversations.usage_json->'cost'->>'cacheWrite')::double precision, 0)
3578
+ ELSE 0
3579
+ END
3580
+ )
3581
+ FROM junior_conversations AS conversations
3582
+ WHERE conversations.root_conversation_id IN (
3583
+ SELECT coalesce(roots.root_conversation_id, roots.conversation_id)
3584
+ FROM junior_conversations AS roots
3585
+ WHERE roots.conversation_id = ANY (conversation_ids.ids)
3586
+ )
3587
+ ), 0::double precision)
3588
+ `;
3589
+ }
3590
+ function ownedByUserSql(conversationIds, userId) {
3591
+ return sql11`EXISTS (
3592
+ SELECT 1
3593
+ FROM unnest(${conversationIds}) AS linked(conversation_id)
3594
+ INNER JOIN junior_conversations AS conversations
3595
+ ON conversations.conversation_id = linked.conversation_id
3596
+ INNER JOIN junior_identities AS identities
3597
+ ON identities.id = conversations.actor_identity_id
3598
+ WHERE identities.user_id = ${userId}
3599
+ )`;
3600
+ }
3601
+ function ownershipFilter(userId) {
3602
+ if (!userId) return void 0;
3603
+ return ownedByUserSql(sql11`${juniorCodeChanges.conversationIds}`, userId);
3604
+ }
3605
+ async function readCodeWindows(args) {
3522
3606
  const db = getDb();
3523
- const windowEnd = new Date(nowMs);
3524
- const windowStart = new Date(nowMs - WINDOW_DAYS2 * DAY_MS);
3607
+ const windowEnd = new Date(args.nowMs);
3608
+ const windowStart = new Date(args.nowMs - WINDOW_DAYS2 * DAY_MS);
3609
+ const activityStart = startOfUtcDay(args.nowMs - (ACTIVITY_DAYS - 1) * DAY_MS);
3525
3610
  const changes = juniorCodeChanges;
3526
- const repositories = juniorCodeRepositories;
3527
- const [summaryResult, repositoryResult, recentChanges] = await Promise.all([
3611
+ const ownership = ownershipFilter(args.userId);
3612
+ const conversationTreeCost = conversationTreeCostExpr();
3613
+ const [summaryResult, activityResult] = await Promise.all([
3528
3614
  db.execute(sql11`
3615
+ WITH recent_changes AS (
3616
+ SELECT
3617
+ ${changes.id},
3618
+ ${changes.openedAt},
3619
+ ${changes.mergedAt},
3620
+ ${changes.closedAt},
3621
+ ${changes.state},
3622
+ ${changes.conversationIds} AS conversation_ids
3623
+ FROM ${changes}
3624
+ ${ownership ? sql11`WHERE ${ownership}` : sql11``}
3625
+ ), change_costs AS (
3626
+ SELECT
3627
+ recent_changes.id,
3628
+ recent_changes.opened_at,
3629
+ conversation_ids.ids AS ids,
3630
+ ${conversationTreeCost} AS cost_usd
3631
+ FROM recent_changes
3632
+ CROSS JOIN LATERAL (
3633
+ SELECT recent_changes.conversation_ids AS ids
3634
+ ) AS conversation_ids
3635
+ WHERE recent_changes.opened_at >= ${windowStart}
3636
+ )
3529
3637
  SELECT
3530
- count(*) FILTER (WHERE ${changes.openedAt} >= ${windowStart})::integer AS "created",
3531
- count(*) FILTER (WHERE ${changes.mergedAt} >= ${windowStart})::integer AS "merged",
3638
+ count(*) FILTER (WHERE recent_changes.opened_at >= ${windowStart})::integer AS "created",
3639
+ count(*) FILTER (WHERE recent_changes.merged_at >= ${windowStart})::integer AS "merged",
3532
3640
  count(*) FILTER (
3533
- WHERE ${changes.state} = 'closed' AND ${changes.closedAt} >= ${windowStart}
3641
+ WHERE recent_changes.state = 'closed'
3642
+ AND recent_changes.closed_at >= ${windowStart}
3534
3643
  )::integer AS "closed",
3535
- count(*) FILTER (WHERE ${changes.state} = 'open')::integer AS "open"
3536
- FROM ${changes}
3644
+ count(*) FILTER (WHERE recent_changes.state = 'open')::integer AS "open",
3645
+ (
3646
+ percentile_cont(0.5) WITHIN GROUP (
3647
+ ORDER BY extract(
3648
+ epoch FROM (recent_changes.merged_at - recent_changes.opened_at)
3649
+ ) * 1000
3650
+ ) FILTER (
3651
+ WHERE recent_changes.state = 'merged'
3652
+ AND recent_changes.merged_at >= ${windowStart}
3653
+ )
3654
+ )::double precision AS "medianMergeTimeMs",
3655
+ coalesce((
3656
+ SELECT ${conversationTreeCost}
3657
+ FROM (
3658
+ SELECT ARRAY(
3659
+ SELECT DISTINCT unnest(change_costs.ids)
3660
+ FROM change_costs
3661
+ ) AS ids
3662
+ ) AS conversation_ids
3663
+ ), 0)::double precision AS "costUsd",
3664
+ (
3665
+ SELECT percentile_cont(0.5) WITHIN GROUP (
3666
+ ORDER BY change_costs.cost_usd
3667
+ )
3668
+ FROM change_costs
3669
+ WHERE change_costs.cost_usd > 0
3670
+ )::double precision AS "medianCostUsd"
3671
+ FROM recent_changes
3537
3672
  `),
3538
3673
  db.execute(sql11`
3674
+ WITH days AS (
3675
+ SELECT generate_series(
3676
+ date_trunc('day', ${activityStart}::timestamptz AT TIME ZONE 'UTC'),
3677
+ date_trunc('day', ${windowEnd}::timestamptz AT TIME ZONE 'UTC'),
3678
+ interval '1 day'
3679
+ ) AS day
3680
+ ), daily AS (
3681
+ SELECT
3682
+ date_trunc('day', event_at AT TIME ZONE 'UTC') AS day,
3683
+ count(*) FILTER (WHERE kind = 'created')::integer AS created,
3684
+ count(*) FILTER (WHERE kind = 'merged')::integer AS merged,
3685
+ count(*) FILTER (WHERE kind = 'closed')::integer AS closed
3686
+ FROM (
3687
+ SELECT ${changes.openedAt} AS event_at, 'created'::text AS kind
3688
+ FROM ${changes}
3689
+ WHERE ${changes.openedAt} >= ${activityStart}
3690
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3691
+ UNION ALL
3692
+ SELECT ${changes.mergedAt} AS event_at, 'merged'::text AS kind
3693
+ FROM ${changes}
3694
+ WHERE ${changes.mergedAt} >= ${activityStart}
3695
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3696
+ UNION ALL
3697
+ SELECT ${changes.closedAt} AS event_at, 'closed'::text AS kind
3698
+ FROM ${changes}
3699
+ WHERE ${changes.state} = 'closed'
3700
+ AND ${changes.closedAt} >= ${activityStart}
3701
+ ${ownership ? sql11`AND ${ownership}` : sql11``}
3702
+ ) AS events
3703
+ GROUP BY date_trunc('day', event_at AT TIME ZONE 'UTC')
3704
+ )
3705
+ SELECT
3706
+ to_char(days.day, 'YYYY-MM-DD') AS "date",
3707
+ coalesce(daily.created, 0)::integer AS "created",
3708
+ coalesce(daily.merged, 0)::integer AS "merged",
3709
+ coalesce(daily.closed, 0)::integer AS "closed"
3710
+ FROM days
3711
+ LEFT JOIN daily ON daily.day = days.day
3712
+ ORDER BY days.day
3713
+ `)
3714
+ ]);
3715
+ const summary = summaryRowSchema.parse(
3716
+ queryRows(summaryResult)[0] ?? {
3717
+ closed: 0,
3718
+ costUsd: 0,
3719
+ created: 0,
3720
+ medianCostUsd: null,
3721
+ medianMergeTimeMs: null,
3722
+ merged: 0,
3723
+ open: 0
3724
+ }
3725
+ );
3726
+ return {
3727
+ activityDays: z5.array(activityDaySchema).parse(queryRows(activityResult)),
3728
+ summary: {
3729
+ ...summary,
3730
+ mergeRate: mergeRate(summary.merged, summary.closed)
3731
+ },
3732
+ windowEnd,
3733
+ windowStart
3734
+ };
3735
+ }
3736
+ async function readCodeOverview(nowMs = Date.now()) {
3737
+ const db = getDb();
3738
+ const changes = juniorCodeChanges;
3739
+ const repositories = juniorCodeRepositories;
3740
+ const windows = await readCodeWindows({ nowMs });
3741
+ const conversationTreeCost = conversationTreeCostExpr();
3742
+ const [repositoryResult, recentChanges] = await Promise.all([
3743
+ db.execute(sql11`
3744
+ WITH recent_changes AS (
3745
+ SELECT
3746
+ ${changes.id},
3747
+ ${changes.repositoryId},
3748
+ ${changes.openedAt},
3749
+ ${changes.mergedAt},
3750
+ ${changes.closedAt},
3751
+ ${changes.state},
3752
+ ${changes.conversationIds} AS conversation_ids
3753
+ FROM ${changes}
3754
+ WHERE ${changes.openedAt} >= ${windows.windowStart}
3755
+ OR ${changes.mergedAt} >= ${windows.windowStart}
3756
+ OR ${changes.closedAt} >= ${windows.windowStart}
3757
+ OR ${changes.state} = 'open'
3758
+ ), change_costs AS (
3759
+ SELECT
3760
+ recent_changes.repository_id,
3761
+ conversation_ids.ids AS ids,
3762
+ ${conversationTreeCost} AS cost_usd
3763
+ FROM recent_changes
3764
+ CROSS JOIN LATERAL (
3765
+ SELECT recent_changes.conversation_ids AS ids
3766
+ ) AS conversation_ids
3767
+ WHERE recent_changes.opened_at >= ${windows.windowStart}
3768
+ )
3539
3769
  SELECT
3540
3770
  ${repositories.id} AS "id",
3541
3771
  ${repositories.name} AS "name",
3542
3772
  ${repositories.provider} AS "provider",
3543
3773
  ${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}
3774
+ count(recent_changes.id) FILTER (
3775
+ WHERE recent_changes.opened_at >= ${windows.windowStart}
3776
+ )::integer AS "created",
3777
+ count(recent_changes.id) FILTER (
3778
+ WHERE recent_changes.merged_at >= ${windows.windowStart}
3779
+ )::integer AS "merged",
3780
+ count(recent_changes.id) FILTER (
3781
+ WHERE recent_changes.state = 'closed'
3782
+ AND recent_changes.closed_at >= ${windows.windowStart}
3548
3783
  )::integer AS "closed",
3549
- count(${changes.id}) FILTER (WHERE ${changes.state} = 'open')::integer AS "open"
3784
+ count(recent_changes.id) FILTER (
3785
+ WHERE recent_changes.state = 'open'
3786
+ )::integer AS "open",
3787
+ (
3788
+ SELECT percentile_cont(0.5) WITHIN GROUP (
3789
+ ORDER BY change_costs.cost_usd
3790
+ )
3791
+ FROM change_costs
3792
+ WHERE change_costs.repository_id = ${repositories.id}
3793
+ AND change_costs.cost_usd > 0
3794
+ )::double precision AS "medianCostUsd"
3550
3795
  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'
3796
+ INNER JOIN recent_changes
3797
+ ON recent_changes.repository_id = ${repositories.id}
3556
3798
  GROUP BY ${repositories.id}
3557
3799
  ORDER BY "merged" DESC, "created" DESC, ${repositories.name} ASC
3558
3800
  LIMIT 25
@@ -3570,16 +3812,15 @@ async function readCodeOverview(nowMs = Date.now()) {
3570
3812
  url: changes.url
3571
3813
  }).from(changes).innerJoin(repositories, eq11(changes.repositoryId, repositories.id)).where(
3572
3814
  or5(
3573
- gte2(changes.openedAt, windowStart),
3574
- gte2(changes.mergedAt, windowStart),
3575
- gte2(changes.closedAt, windowStart),
3815
+ gte2(changes.openedAt, windows.windowStart),
3816
+ gte2(changes.mergedAt, windows.windowStart),
3817
+ gte2(changes.closedAt, windows.windowStart),
3576
3818
  eq11(changes.state, "open")
3577
3819
  )
3578
3820
  ).orderBy(desc4(changes.updatedAt)).limit(25)
3579
3821
  ]);
3580
- const summary = summaryRowSchema.parse(queryRows(summaryResult)[0]);
3581
- const completed = summary.merged + summary.closed;
3582
3822
  return codeOverviewReportSchema.parse({
3823
+ activityDays: windows.activityDays,
3583
3824
  changes: recentChanges.map((change) => ({
3584
3825
  ...change,
3585
3826
  closedAt: change.closedAt?.toISOString(),
@@ -3588,17 +3829,29 @@ async function readCodeOverview(nowMs = Date.now()) {
3588
3829
  title: change.title ?? void 0,
3589
3830
  url: change.url ?? void 0
3590
3831
  })),
3591
- generatedAt: windowEnd.toISOString(),
3832
+ generatedAt: windows.windowEnd.toISOString(),
3592
3833
  repositories: z5.array(repositoryRowSchema).parse(queryRows(repositoryResult)).map((repository) => ({
3593
3834
  ...repository,
3835
+ mergeRate: mergeRate(repository.merged, repository.closed),
3594
3836
  url: repository.url ?? void 0
3595
3837
  })),
3596
- summary: {
3597
- ...summary,
3598
- mergeRate: completed > 0 ? summary.merged / completed : void 0
3599
- },
3600
- windowEnd: windowEnd.toISOString(),
3601
- windowStart: windowStart.toISOString()
3838
+ summary: windows.summary,
3839
+ windowEnd: windows.windowEnd.toISOString(),
3840
+ windowStart: windows.windowStart.toISOString()
3841
+ });
3842
+ }
3843
+ async function readPersonCodeOverview(args) {
3844
+ const nowMs = args.nowMs ?? Date.now();
3845
+ const windows = await readCodeWindows({
3846
+ nowMs,
3847
+ userId: args.userId
3848
+ });
3849
+ return codePersonReportSchema.parse({
3850
+ activityDays: windows.activityDays,
3851
+ generatedAt: windows.windowEnd.toISOString(),
3852
+ summary: windows.summary,
3853
+ windowEnd: windows.windowEnd.toISOString(),
3854
+ windowStart: windows.windowStart.toISOString()
3602
3855
  });
3603
3856
  }
3604
3857
 
@@ -3677,7 +3930,7 @@ function summaryFromRow(row, options = {}) {
3677
3930
 
3678
3931
  // src/api/locations/query.ts
3679
3932
  var RECENT_LIMIT = 25;
3680
- var ACTIVITY_DAYS = 90;
3933
+ var ACTIVITY_DAYS2 = 90;
3681
3934
  var treeConversation2 = alias4(
3682
3935
  juniorConversations,
3683
3936
  "location_tree_conversation"
@@ -3819,7 +4072,7 @@ function directoryActivityDays(rows, nowMs) {
3819
4072
  const end = new Date(nowMs);
3820
4073
  end.setUTCHours(0, 0, 0, 0);
3821
4074
  const start = new Date(end);
3822
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4075
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3823
4076
  const activity = [];
3824
4077
  for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
3825
4078
  const date = cursor.toISOString().slice(0, 10);
@@ -3838,7 +4091,7 @@ async function readLocationDirectoryFromSql() {
3838
4091
  const end = new Date(nowMs);
3839
4092
  end.setUTCHours(0, 0, 0, 0);
3840
4093
  const start = new Date(end);
3841
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4094
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3842
4095
  const [rows, activityRows] = await Promise.all([
3843
4096
  directoryRows(getDb()),
3844
4097
  directoryActivityRows(getDb(), start)
@@ -3906,7 +4159,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
3906
4159
  const end = new Date(nowMs);
3907
4160
  end.setUTCHours(0, 0, 0, 0);
3908
4161
  const start = new Date(end);
3909
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS - 1));
4162
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
3910
4163
  const where = publicLocationWhere(locationId);
3911
4164
  const activityDate = sql12`TO_CHAR(
3912
4165
  ${juniorConversations.lastActivityAt} AT TIME ZONE 'UTC',
@@ -4000,7 +4253,7 @@ async function readLocationDetailFromSql(locationId, options = {}) {
4000
4253
  addMetrics(item, row);
4001
4254
  actors.push(item);
4002
4255
  }
4003
- const activity = activityDays(days, nowMs, ACTIVITY_DAYS);
4256
+ const activity = activityDays(days, nowMs, ACTIVITY_DAYS2);
4004
4257
  const recentConversationIds = recentRows.map((row) => row.conversationId);
4005
4258
  const [accessByConversation, metricsByRoot] = await Promise.all([
4006
4259
  readConversationAccessFromSql(
@@ -4073,6 +4326,27 @@ function createLocationRoutes() {
4073
4326
  // src/api/people/routes.ts
4074
4327
  import { Hono as Hono4 } from "hono";
4075
4328
 
4329
+ // src/api/people/code.ts
4330
+ async function readPeopleCode(email) {
4331
+ const subject = await findUserByEmail(email);
4332
+ if (!subject) {
4333
+ const now = /* @__PURE__ */ new Date();
4334
+ return codePersonReportSchema.parse({
4335
+ activityDays: [],
4336
+ generatedAt: now.toISOString(),
4337
+ summary: {
4338
+ closed: 0,
4339
+ created: 0,
4340
+ merged: 0,
4341
+ open: 0
4342
+ },
4343
+ windowEnd: now.toISOString(),
4344
+ windowStart: new Date(now.getTime() - 30 * 24 * 60 * 60 * 1e3).toISOString()
4345
+ });
4346
+ }
4347
+ return await readPersonCodeOverview({ userId: subject.id });
4348
+ }
4349
+
4076
4350
  // src/api/people/list.query.ts
4077
4351
  import { and as and13, eq as eq14, gte as gte4, sql as sql14 } from "drizzle-orm";
4078
4352
 
@@ -4080,7 +4354,7 @@ import { and as and13, eq as eq14, gte as gte4, sql as sql14 } from "drizzle-orm
4080
4354
  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
4355
  import { alias as alias5 } from "drizzle-orm/pg-core";
4082
4356
  var RECENT_LIMIT2 = 25;
4083
- var ACTIVITY_DAYS2 = 365;
4357
+ var ACTIVITY_DAYS3 = 365;
4084
4358
  var peopleTreeConversation = alias5(
4085
4359
  juniorConversations,
4086
4360
  "people_tree_conversation"
@@ -4163,7 +4437,7 @@ function activityDays2(days, nowMs) {
4163
4437
  const end = new Date(nowMs);
4164
4438
  end.setUTCHours(0, 0, 0, 0);
4165
4439
  const start = new Date(end);
4166
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4440
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4167
4441
  for (const cursor = new Date(start); cursor.getTime() <= end.getTime(); cursor.setUTCDate(cursor.getUTCDate() + 1)) {
4168
4442
  const date = cursor.toISOString().slice(0, 10);
4169
4443
  items.push(days.get(date) ?? emptyActivityDay(date));
@@ -4314,7 +4588,7 @@ async function readPeoplePluginReports(args) {
4314
4588
  source: "plugins"
4315
4589
  });
4316
4590
  }
4317
- const { getPluginProfileReports } = await import("./agent-hooks-SNRBZYAU.js");
4591
+ const { getPluginProfileReports } = await import("./agent-hooks-NGZQLL5O.js");
4318
4592
  return pluginOperationalReportFeedSchema.parse({
4319
4593
  generatedAt: new Date(nowMs).toISOString(),
4320
4594
  reports: await getPluginProfileReports({
@@ -4332,7 +4606,7 @@ function emptyProfile(email, nowMs) {
4332
4606
  const end = new Date(nowMs);
4333
4607
  end.setUTCHours(0, 0, 0, 0);
4334
4608
  const start = new Date(end);
4335
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4609
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4336
4610
  return {
4337
4611
  activityDays: activityDays2(/* @__PURE__ */ new Map(), nowMs),
4338
4612
  generatedAt: new Date(nowMs).toISOString(),
@@ -4387,7 +4661,7 @@ async function readPeopleProfileFromSql(email, options = {}) {
4387
4661
  const end = new Date(nowMs);
4388
4662
  end.setUTCHours(0, 0, 0, 0);
4389
4663
  const start = new Date(end);
4390
- start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS2 - 1));
4664
+ start.setUTCDate(start.getUTCDate() - (ACTIVITY_DAYS3 - 1));
4391
4665
  const where = verifiedActorWhere(normalizedEmail);
4392
4666
  const metricsJoin = peopleTreeMetricsJoin();
4393
4667
  const surface = surfaceExpression();
@@ -4626,6 +4900,14 @@ function createPeopleRoutes() {
4626
4900
  );
4627
4901
  }
4628
4902
  );
4903
+ app.get(
4904
+ "/:email/code",
4905
+ validateRequest("param", personParamsSchema, "Invalid route parameters."),
4906
+ async (context) => {
4907
+ const { email } = context.req.valid("param");
4908
+ return jsonResponse(codePersonReportSchema, await readPeopleCode(email));
4909
+ }
4910
+ );
4629
4911
  app.get(
4630
4912
  "/:email",
4631
4913
  validateRequest("param", personParamsSchema, "Invalid route parameters."),
@@ -5110,7 +5392,7 @@ function readDescriptionText(home) {
5110
5392
  }
5111
5393
  }
5112
5394
  async function readSkillReports() {
5113
- const { discoverSkills } = await import("./skills-EXIQ7BP5.js");
5395
+ const { discoverSkills } = await import("./skills-J2AH26CW.js");
5114
5396
  const skills = await discoverSkills();
5115
5397
  return skillReportsSchema.parse(
5116
5398
  skills.map((skill) => ({
@@ -5120,7 +5402,7 @@ async function readSkillReports() {
5120
5402
  );
5121
5403
  }
5122
5404
  async function readPlugins() {
5123
- const { pluginCatalogRuntime } = await import("./catalog-runtime-3I3JOIIH.js");
5405
+ const { pluginCatalogRuntime } = await import("./catalog-runtime-AAEAO2RL.js");
5124
5406
  return pluginsSchema.parse(
5125
5407
  pluginCatalogRuntime.getProviders().map((plugin) => ({
5126
5408
  configKeys: plugin.manifest.configKeys ?? [],
@@ -5133,7 +5415,7 @@ async function readPlugins() {
5133
5415
  async function readRuntimeInfoReport() {
5134
5416
  const [{ homeDir }, { pluginCatalogRuntime }, plugins, skills] = await Promise.all([
5135
5417
  import("./discovery-DWVDM7UK.js"),
5136
- import("./catalog-runtime-3I3JOIIH.js"),
5418
+ import("./catalog-runtime-AAEAO2RL.js"),
5137
5419
  readPlugins(),
5138
5420
  readSkillReports()
5139
5421
  ]);
@@ -5149,7 +5431,7 @@ async function readRuntimeInfoReport() {
5149
5431
  }
5150
5432
  async function readPluginOperationalReportFeed() {
5151
5433
  const nowMs = Date.now();
5152
- const { getPluginOperationalReports } = await import("./agent-hooks-SNRBZYAU.js");
5434
+ const { getPluginOperationalReports } = await import("./agent-hooks-NGZQLL5O.js");
5153
5435
  return pluginOperationalReportFeedSchema.parse({
5154
5436
  source: "plugins",
5155
5437
  generatedAt: new Date(nowMs).toISOString(),