@sentry/junior 0.162.0 → 0.164.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 (96) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-hooks-TL2SGC5W.js → agent-hooks-WTBPNRTC.js} +10 -10
  3. package/dist/api/acp/route.d.ts +13 -0
  4. package/dist/api/schema/workspace.d.ts +47 -0
  5. package/dist/api/schema.d.ts +2 -0
  6. package/dist/api/schema.js +15 -3
  7. package/dist/api/workspaces/routes.d.ts +4 -0
  8. package/dist/api.js +321 -408
  9. package/dist/app.d.ts +2 -2
  10. package/dist/app.js +482 -38
  11. package/dist/{catalog-runtime-IGSMIADJ.js → catalog-runtime-CZ2SQINY.js} +5 -5
  12. package/dist/chat/agent/sandbox.d.ts +4 -0
  13. package/dist/chat/api-turns/cancellation.d.ts +27 -0
  14. package/dist/chat/api-turns/routing.d.ts +31 -0
  15. package/dist/chat/api-turns/work.d.ts +12 -29
  16. package/dist/chat/app/conversation-work.d.ts +6 -1
  17. package/dist/chat/app/production.d.ts +2 -2
  18. package/dist/chat/conversations/history.d.ts +3 -0
  19. package/dist/chat/conversations/turn-lifecycle.d.ts +1 -1
  20. package/dist/chat/experimental.d.ts +1 -1
  21. package/dist/chat/plugins/agent-hooks.d.ts +4 -0
  22. package/dist/chat/repository-instructions.d.ts +1 -1
  23. package/dist/chat/sandbox/paths.d.ts +1 -0
  24. package/dist/chat/sandbox/prepare-workspace.d.ts +11 -0
  25. package/dist/chat/sandbox/ref.d.ts +1 -0
  26. package/dist/chat/sandbox/sandbox.d.ts +4 -0
  27. package/dist/chat/sandbox/session.d.ts +4 -0
  28. package/dist/chat/sandbox/snapshot/profile.d.ts +4 -3
  29. package/dist/chat/sandbox/snapshot/resolve.d.ts +4 -0
  30. package/dist/chat/sandbox/workspace.d.ts +4 -0
  31. package/dist/chat/tools/types.d.ts +5 -0
  32. package/dist/chat/workspaces/checkout-path.d.ts +4 -0
  33. package/dist/chat/workspaces/store.d.ts +37 -0
  34. package/dist/chat/workspaces/tools.d.ts +4 -0
  35. package/dist/chat/workspaces/types.d.ts +13 -0
  36. package/dist/chat/workspaces/validation.d.ts +21 -0
  37. package/dist/{chunk-THXMSSFZ.js → chunk-26JYGL2L.js} +1 -1
  38. package/dist/{chunk-OPNJRQDP.js → chunk-2K25MRZK.js} +9 -6
  39. package/dist/{chunk-C3KUKA27.js → chunk-3L23VTY7.js} +17 -17
  40. package/dist/{chunk-JJ3VXFDI.js → chunk-4GTOFUQF.js} +1 -1
  41. package/dist/{chunk-RDV4BQRZ.js → chunk-5FWQ73UZ.js} +1 -1
  42. package/dist/{chunk-BPZQWAZJ.js → chunk-5GLGQHO3.js} +4 -1
  43. package/dist/{chunk-K7IV6UV3.js → chunk-6BXV63YX.js} +5 -5
  44. package/dist/{chunk-4WYGQ47U.js → chunk-6ENAACWN.js} +5 -5
  45. package/dist/{chunk-EHQLTK3X.js → chunk-7WKWMMYQ.js} +323 -127
  46. package/dist/chunk-BVREIWBD.js +131 -0
  47. package/dist/{chunk-DZC3F2UI.js → chunk-BXBZDV3C.js} +1 -1
  48. package/dist/{chunk-HW4BW3PC.js → chunk-CGCNFEAU.js} +4 -4
  49. package/dist/{chunk-JRVURR3H.js → chunk-FKDY7NTC.js} +543 -164
  50. package/dist/{chunk-MNCEGXEH.js → chunk-FX4PX3SA.js} +2 -2
  51. package/dist/{chunk-OBFRMQBY.js → chunk-H36GJVJV.js} +9 -9
  52. package/dist/{chunk-V7QL4QMW.js → chunk-HHW6ZVIZ.js} +12 -7
  53. package/dist/{chunk-O6P7DZ6O.js → chunk-JW23XD4I.js} +6 -6
  54. package/dist/{chunk-DDQ43S5Q.js → chunk-LPLVNMVN.js} +4 -4
  55. package/dist/{chunk-5NEBOHRG.js → chunk-NAGI2XH3.js} +4 -4
  56. package/dist/{chunk-FEWT5KPP.js → chunk-NLTQRYNP.js} +2 -2
  57. package/dist/{chunk-SA6BEEAB.js → chunk-PU45BSBS.js} +108 -74
  58. package/dist/{chunk-7DTPFFQX.js → chunk-RBV4JHCP.js} +1 -1
  59. package/dist/{chunk-6BJFIWEV.js → chunk-RJHZNPDH.js} +46 -87
  60. package/dist/{chunk-FESUWAYU.js → chunk-RULIISEI.js} +1 -1
  61. package/dist/{chunk-MMBMDSRK.js → chunk-RYYEHWIL.js} +1 -1
  62. package/dist/{chunk-Y75ZDQB5.js → chunk-VHSEMJTS.js} +1 -1
  63. package/dist/{chunk-5IDFPCON.js → chunk-VRS6WDK5.js} +1 -1
  64. package/dist/{chunk-EBEHJPFF.js → chunk-VV2DN24T.js} +1 -1
  65. package/dist/{chunk-WHAJ3QVY.js → chunk-WJUKESU2.js} +1 -1
  66. package/dist/{chunk-H2KW7AZC.js → chunk-XAIRI54J.js} +1 -1
  67. package/dist/{chunk-AJ7J2HWY.js → chunk-Y2B6AXZQ.js} +6 -6
  68. package/dist/{chunk-QHCIZLIX.js → chunk-YHCTPDXS.js} +356 -13
  69. package/dist/{chunk-CZ6EHOU5.js → chunk-ZSNABC2E.js} +40 -2
  70. package/dist/{chunk-KQSO4DJ4.js → chunk-ZUTAGCPH.js} +9 -9
  71. package/dist/cli/chat.js +32 -32
  72. package/dist/cli/check.js +6 -6
  73. package/dist/cli/plugins.js +13 -13
  74. package/dist/cli/snapshot-create.js +10 -10
  75. package/dist/cli/upgrade.js +7 -7
  76. package/dist/db/schema/workspaces.d.ts +169 -0
  77. package/dist/db/schema.d.ts +169 -1
  78. package/dist/{db-66XZ4HHQ.js → db-32KRFS6L.js} +7 -7
  79. package/dist/{experimental-HH6OM6UH.js → experimental-XVDOSRBS.js} +1 -1
  80. package/dist/instrumentation.js +4 -4
  81. package/dist/nitro.js +2 -2
  82. package/dist/{oauth-callback-server-FMLKDUUF.js → oauth-callback-server-CGZP2Z6U.js} +24 -24
  83. package/dist/{runner-YZONUMNY.js → runner-S5N4Q2XL.js} +32 -34
  84. package/dist/{skills-3UDJJXSO.js → skills-KYPSHSQR.js} +6 -6
  85. package/dist/spawn-EGOB3CLV.js +37 -0
  86. package/dist/{validation-3HTDBONC.js → validation-SZBE7JNF.js} +6 -6
  87. package/dist/version.js +1 -1
  88. package/dist/{work-O4YTIS54.js → work-GCZKHEZX.js} +18 -18
  89. package/dist/worker-6MY5Q4A2.js +24 -0
  90. package/migrations/0029_dizzy_barracuda.sql +19 -0
  91. package/migrations/meta/0029_snapshot.json +2709 -0
  92. package/migrations/meta/_journal.json +8 -1
  93. package/package.json +7 -4
  94. package/dist/chunk-EM225ITJ.js +0 -56
  95. package/dist/spawn-TBPVA5S2.js +0 -37
  96. package/dist/worker-WHURUEYX.js +0 -24
package/dist/app.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  processConversationWork
3
- } from "./chunk-HW4BW3PC.js";
3
+ } from "./chunk-CGCNFEAU.js";
4
4
  import {
5
5
  bindSpawnAgent
6
- } from "./chunk-H2KW7AZC.js";
6
+ } from "./chunk-XAIRI54J.js";
7
7
  import {
8
8
  createAgentInvocationWorker,
9
9
  enqueueAgentInvocation,
10
10
  listPendingAgentInvocationMailboxAppends,
11
11
  routeAgentInvocationWork
12
- } from "./chunk-AJ7J2HWY.js";
12
+ } from "./chunk-Y2B6AXZQ.js";
13
13
  import {
14
14
  GET as GET2,
15
15
  GET2 as GET3,
@@ -36,23 +36,30 @@ import {
36
36
  startSlackProcessingReaction,
37
37
  stripLeadingBotMention,
38
38
  wakePausedTurn
39
- } from "./chunk-C3KUKA27.js";
39
+ } from "./chunk-3L23VTY7.js";
40
40
  import "./chunk-FA6POGLH.js";
41
41
  import {
42
42
  getTurnRequestDeadline,
43
43
  runWithTurnRequestDeadline
44
- } from "./chunk-MMBMDSRK.js";
44
+ } from "./chunk-RYYEHWIL.js";
45
45
  import {
46
46
  GET,
47
+ apiTurnIdForMessage,
48
+ appendAndEnqueueApiConversationMessage,
49
+ authenticatePersonalToken,
47
50
  conversationVisibilityFromSlackChannelType,
51
+ createApiTurnCancellation,
48
52
  createApiTurnWorker,
49
53
  createVercelAttachmentStorage,
50
54
  lookupSlackUser,
55
+ readConversationAccessFromSql,
56
+ recordApiConversationActivity,
51
57
  resolveSlackChannelTypeFromMessage,
52
58
  resolveSlackConversationContext,
53
- routeApiTurnWork
54
- } from "./chunk-JRVURR3H.js";
55
- import "./chunk-RDV4BQRZ.js";
59
+ routeApiTurnWork,
60
+ webActorFromEmail
61
+ } from "./chunk-FKDY7NTC.js";
62
+ import "./chunk-5FWQ73UZ.js";
56
63
  import {
57
64
  CONVERSATION_WORK_STALE_ENQUEUE_MS,
58
65
  appendAndEnqueueInboundMessage,
@@ -63,7 +70,7 @@ import {
63
70
  isInvalidConversationRecordError,
64
71
  listActiveConversationIds,
65
72
  removeActiveConversation
66
- } from "./chunk-5NEBOHRG.js";
73
+ } from "./chunk-NAGI2XH3.js";
67
74
  import {
68
75
  buildDeliveredTurnStatePatch,
69
76
  claimDispatchMailboxAppend,
@@ -94,7 +101,7 @@ import {
94
101
  requireTurnFailureEventId,
95
102
  resolveTurnSessionRouting,
96
103
  scheduleSessionCompletedPluginTasks
97
- } from "./chunk-KQSO4DJ4.js";
104
+ } from "./chunk-ZUTAGCPH.js";
98
105
  import {
99
106
  ConversationTurnLifecycleService,
100
107
  coerceThreadConversationState,
@@ -104,7 +111,7 @@ import {
104
111
  persistThreadRuntimeState,
105
112
  persistThreadState,
106
113
  persistThreadStateById
107
- } from "./chunk-OPNJRQDP.js";
114
+ } from "./chunk-2K25MRZK.js";
108
115
  import "./chunk-UTL3MHPC.js";
109
116
  import {
110
117
  getVercelConversationWorkQueue,
@@ -123,7 +130,7 @@ import {
123
130
  import {
124
131
  validatePluginEgressCredentialHooks,
125
132
  validatePluginRegistrations
126
- } from "./chunk-JJ3VXFDI.js";
133
+ } from "./chunk-4GTOFUQF.js";
127
134
  import {
128
135
  defineJuniorPlugins,
129
136
  pluginCatalogConfigFromEnv,
@@ -158,11 +165,11 @@ import {
158
165
  shouldEmitDevAgentTrace,
159
166
  shouldPropagateSandboxEgressTrace,
160
167
  unpublishedArtifactHeaders
161
- } from "./chunk-EHQLTK3X.js";
168
+ } from "./chunk-7WKWMMYQ.js";
162
169
  import {
163
170
  AuthorizationFlowDisabledError,
164
171
  PluginCredentialFailureError
165
- } from "./chunk-K7IV6UV3.js";
172
+ } from "./chunk-6BXV63YX.js";
166
173
  import "./chunk-DIB4GX3G.js";
167
174
  import {
168
175
  createAgentRunner,
@@ -170,7 +177,7 @@ import {
170
177
  deleteMcpAuthSessionsForUserProvider,
171
178
  deleteMcpServerSessionId,
172
179
  deleteMcpStoredOAuthCredentials
173
- } from "./chunk-OBFRMQBY.js";
180
+ } from "./chunk-H36GJVJV.js";
174
181
  import {
175
182
  RetryableDeliveryError
176
183
  } from "./chunk-RF6J3DIO.js";
@@ -178,16 +185,17 @@ import {
178
185
  formatProviderLabel,
179
186
  postSlackMessage,
180
187
  startOAuthFlow
181
- } from "./chunk-O6P7DZ6O.js";
188
+ } from "./chunk-JW23XD4I.js";
182
189
  import "./chunk-7MA4HV4W.js";
183
190
  import {
184
191
  getExperimentalFeatures,
192
+ isExperimentalFeatureEnabled,
185
193
  setExperimentalFeatures
186
- } from "./chunk-5IDFPCON.js";
194
+ } from "./chunk-VRS6WDK5.js";
187
195
  import {
188
196
  getSandboxResourceConfig,
189
197
  setSandboxResourceConfig
190
- } from "./chunk-6BJFIWEV.js";
198
+ } from "./chunk-RJHZNPDH.js";
191
199
  import {
192
200
  collectAttachmentGarbage,
193
201
  createSchedulerSqlStore,
@@ -195,10 +203,10 @@ import {
195
203
  logScheduledTaskRunSkipped,
196
204
  requestAttachmentDeletion,
197
205
  scheduledTaskRunAttributes
198
- } from "./chunk-4WYGQ47U.js";
206
+ } from "./chunk-6ENAACWN.js";
199
207
  import {
200
208
  parseSlackThreadId
201
- } from "./chunk-WHAJ3QVY.js";
209
+ } from "./chunk-WJUKESU2.js";
202
210
  import {
203
211
  ConversationTurnBoundaryError,
204
212
  CooperativeTurnYieldError,
@@ -238,6 +246,7 @@ import {
238
246
  persistConversationMessageSummaries,
239
247
  persistConversationMessages,
240
248
  persistWithRetry,
249
+ projectConversationMessages,
241
250
  recordDeliveredAssistantMessage,
242
251
  recordSubscribedReplyRoute,
243
252
  recordTurnSummary,
@@ -248,7 +257,7 @@ import {
248
257
  trimTrailingAssistantMessages,
249
258
  turnHasReply,
250
259
  upsertConversationMessage
251
- } from "./chunk-V7QL4QMW.js";
260
+ } from "./chunk-HHW6ZVIZ.js";
252
261
  import "./chunk-3Y47VMHV.js";
253
262
  import "./chunk-PDO5BLNM.js";
254
263
  import "./chunk-UIE3R5XU.js";
@@ -268,6 +277,7 @@ import {
268
277
  getWorkspaceTeamId,
269
278
  isDmChannel,
270
279
  normalizeSlackConversationId,
280
+ resolveViewerUser,
271
281
  runWithSlackInstallationToken,
272
282
  runWithWorkspaceTeamId,
273
283
  setDashboardConversationLinkOptions,
@@ -276,7 +286,7 @@ import {
276
286
  verifyEventTaskCredentialSubject,
277
287
  verifyScheduledTaskCredentialSubject,
278
288
  verifySlackDirectCredentialSubject
279
- } from "./chunk-QHCIZLIX.js";
289
+ } from "./chunk-YHCTPDXS.js";
280
290
  import {
281
291
  contextProvenance,
282
292
  createSlackDestination,
@@ -289,19 +299,21 @@ import {
289
299
  requireSlackDestination,
290
300
  withConversationEventLock,
291
301
  withConversationMutationLock
292
- } from "./chunk-DDQ43S5Q.js";
302
+ } from "./chunk-LPLVNMVN.js";
293
303
  import {
294
304
  juniorAgentBindings,
295
305
  juniorAgentInvocations,
296
306
  juniorConversationEvents,
297
307
  juniorConversations
298
- } from "./chunk-CZ6EHOU5.js";
299
- import "./chunk-EM225ITJ.js";
308
+ } from "./chunk-ZSNABC2E.js";
309
+ import {
310
+ sleep
311
+ } from "./chunk-BVREIWBD.js";
300
312
  import {
301
313
  acquireActiveLock,
302
314
  getStateAdapter,
303
315
  withActiveLock
304
- } from "./chunk-DZC3F2UI.js";
316
+ } from "./chunk-BXBZDV3C.js";
305
317
  import {
306
318
  STANDARD_MODEL_PROFILE,
307
319
  botConfig,
@@ -319,16 +331,16 @@ import {
319
331
  modelIdForProfile,
320
332
  setSlackReactionConfig,
321
333
  standardModelId
322
- } from "./chunk-MNCEGXEH.js";
334
+ } from "./chunk-FX4PX3SA.js";
323
335
  import {
324
336
  juniorDestinations
325
337
  } from "./chunk-VFUK3X5B.js";
326
- import "./chunk-7DTPFFQX.js";
327
- import "./chunk-FEWT5KPP.js";
338
+ import "./chunk-RBV4JHCP.js";
339
+ import "./chunk-NLTQRYNP.js";
328
340
  import {
329
341
  pluginCatalogRuntime
330
- } from "./chunk-FESUWAYU.js";
331
- import "./chunk-EBEHJPFF.js";
342
+ } from "./chunk-RULIISEI.js";
343
+ import "./chunk-VV2DN24T.js";
332
344
  import {
333
345
  credentialContextForActor
334
346
  } from "./chunk-BDUENQD3.js";
@@ -360,7 +372,7 @@ import {
360
372
  withContext,
361
373
  withLogContext,
362
374
  withSpan
363
- } from "./chunk-Y75ZDQB5.js";
375
+ } from "./chunk-VHSEMJTS.js";
364
376
  import {
365
377
  continueTrace
366
378
  } from "./chunk-6GWA276C.js";
@@ -368,10 +380,10 @@ import "./chunk-OOUNJNMR.js";
368
380
  import "./chunk-H4HBKISO.js";
369
381
  import {
370
382
  JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE
371
- } from "./chunk-THXMSSFZ.js";
383
+ } from "./chunk-26JYGL2L.js";
372
384
  import {
373
385
  JUNIOR_VERSION
374
- } from "./chunk-BPZQWAZJ.js";
386
+ } from "./chunk-5GLGQHO3.js";
375
387
  import "./chunk-MLKGABMK.js";
376
388
 
377
389
  // src/app.ts
@@ -8402,6 +8414,7 @@ async function runNextPausedTurnInContext(conversationId, options, runOptions) {
8402
8414
 
8403
8415
  // src/chat/app/conversation-work.ts
8404
8416
  function createConversationWork(options) {
8417
+ const apiTurnCancellation = createApiTurnCancellation();
8405
8418
  const services = {
8406
8419
  ...options.services,
8407
8420
  replyExecutor: {
@@ -8457,11 +8470,13 @@ function createConversationWork(options) {
8457
8470
  fallbackWorker: slackWorker
8458
8471
  });
8459
8472
  return {
8473
+ apiTurnCancellation,
8460
8474
  conversationStore: options.conversationStore,
8461
8475
  queue: options.queue,
8462
8476
  run: routeApiTurnWork({
8463
8477
  apiTurnWorker: createApiTurnWorker({
8464
- agentRunner: options.agentRunner
8478
+ agentRunner: options.agentRunner,
8479
+ cancellation: apiTurnCancellation
8465
8480
  }),
8466
8481
  fallbackWorker: routeAgentInvocationWork({
8467
8482
  invocationWorker: createAgentInvocationWorker({
@@ -8774,6 +8789,415 @@ async function ingestEventTasks(input, options) {
8774
8789
  return { dispatched };
8775
8790
  }
8776
8791
 
8792
+ // src/api/acp/route.ts
8793
+ import { randomUUID } from "crypto";
8794
+ import * as acp from "@agentclientprotocol/sdk";
8795
+ import { AcpServer } from "@agentclientprotocol/sdk/experimental/server";
8796
+ var ACP_CONNECTION_ID_HEADER = "Acp-Connection-Id";
8797
+ var ACP_CONVERSATION_PREFIX = "local:acp:";
8798
+ var EVENT_PAGE_SIZE = 50;
8799
+ var EVENT_POLL_INTERVAL_MS = 25;
8800
+ var MAX_PROMPT_TEXT_LENGTH = 32e3;
8801
+ function bearerToken(request) {
8802
+ const authorization = request.headers.get("Authorization");
8803
+ const match = authorization?.match(/^Bearer ([^\s]+)$/);
8804
+ return match?.[1];
8805
+ }
8806
+ async function authenticateRequest(request) {
8807
+ const token = bearerToken(request);
8808
+ if (!token) return void 0;
8809
+ const email = await authenticatePersonalToken(token);
8810
+ if (!email) return void 0;
8811
+ const user = await resolveViewerUser(email);
8812
+ if (!user) return void 0;
8813
+ return {
8814
+ actor: webActorFromEmail(
8815
+ user.email ?? email,
8816
+ user.displayName ? { fullName: user.displayName } : void 0
8817
+ ),
8818
+ user
8819
+ };
8820
+ }
8821
+ function rejectUnsupportedMcpServers(mcpServers) {
8822
+ if (mcpServers.length > 0) {
8823
+ throw acp.RequestError.invalidParams(
8824
+ { field: "mcpServers" },
8825
+ "Junior does not accept client MCP servers"
8826
+ );
8827
+ }
8828
+ }
8829
+ async function requireOwnedSession(sessionId, user) {
8830
+ if (!sessionId.startsWith(ACP_CONVERSATION_PREFIX)) {
8831
+ throw acp.RequestError.resourceNotFound(sessionId);
8832
+ }
8833
+ const access = (await readConversationAccessFromSql(getDb(), [sessionId], user)).get(sessionId);
8834
+ if (!access?.isParticipant) {
8835
+ throw acp.RequestError.resourceNotFound(sessionId);
8836
+ }
8837
+ }
8838
+ function promptText(prompt) {
8839
+ if (prompt.length === 0) {
8840
+ throw acp.RequestError.invalidParams(
8841
+ { field: "prompt" },
8842
+ "Junior accepts one or more text blocks only"
8843
+ );
8844
+ }
8845
+ const blocks = [];
8846
+ for (const block of prompt) {
8847
+ if (block.type !== "text") {
8848
+ throw acp.RequestError.invalidParams(
8849
+ { field: "prompt" },
8850
+ "Junior accepts one or more text blocks only"
8851
+ );
8852
+ }
8853
+ if (!block.text.trim()) {
8854
+ throw acp.RequestError.invalidParams(
8855
+ { field: "prompt" },
8856
+ "Junior accepts non-empty text blocks only"
8857
+ );
8858
+ }
8859
+ blocks.push(block.text);
8860
+ }
8861
+ const text = blocks.join("\n");
8862
+ if (text.length > MAX_PROMPT_TEXT_LENGTH) {
8863
+ throw acp.RequestError.invalidParams(
8864
+ { field: "prompt" },
8865
+ `Junior accepts at most ${MAX_PROMPT_TEXT_LENGTH} prompt characters`
8866
+ );
8867
+ }
8868
+ return text;
8869
+ }
8870
+ function requestIdKey(requestId) {
8871
+ if (requestId === null) return "null";
8872
+ return `${typeof requestId}:${requestId}`;
8873
+ }
8874
+ async function latestEventSeq(eventStore, conversationId) {
8875
+ const page = await eventStore.query(conversationId, { limit: 1 });
8876
+ return page.events.at(-1)?.seq ?? 0;
8877
+ }
8878
+ async function replaySession(eventStore, sessionId, client) {
8879
+ const history = await eventStore.loadMessageHistory(sessionId);
8880
+ for (const message of projectConversationMessages(history)) {
8881
+ if (message.role === "system") continue;
8882
+ await client.notify(acp.methods.client.session.update, {
8883
+ sessionId,
8884
+ update: {
8885
+ sessionUpdate: message.role === "user" ? "user_message_chunk" : "agent_message_chunk",
8886
+ content: { type: "text", text: message.text },
8887
+ messageId: message.id
8888
+ }
8889
+ });
8890
+ }
8891
+ }
8892
+ async function waitForTurn(args) {
8893
+ let cursor = args.afterSeq;
8894
+ const assistantPrefix = `${args.turnId}:assistant:`;
8895
+ const sentMessageIds = /* @__PURE__ */ new Set();
8896
+ while (true) {
8897
+ if (args.signal.aborted) {
8898
+ throw acp.RequestError.requestCancelled();
8899
+ }
8900
+ const page = await args.eventStore.query(args.sessionId, {
8901
+ afterSeq: cursor,
8902
+ limit: EVENT_PAGE_SIZE,
8903
+ types: ["message", "turn_completed", "turn_failed"]
8904
+ });
8905
+ if (page.events.length === 0) {
8906
+ try {
8907
+ await sleep(EVENT_POLL_INTERVAL_MS, args.signal);
8908
+ } catch (error) {
8909
+ if (args.signal.aborted) {
8910
+ throw acp.RequestError.requestCancelled();
8911
+ }
8912
+ throw error;
8913
+ }
8914
+ continue;
8915
+ }
8916
+ for (const event of page.events) {
8917
+ cursor = event.seq;
8918
+ const data = event.data;
8919
+ if (data.type === "message" && data.role === "assistant" && data.messageId.startsWith(assistantPrefix) && !sentMessageIds.has(data.messageId)) {
8920
+ sentMessageIds.add(data.messageId);
8921
+ await args.client.notify(acp.methods.client.session.update, {
8922
+ sessionId: args.sessionId,
8923
+ update: {
8924
+ sessionUpdate: "agent_message_chunk",
8925
+ content: { type: "text", text: data.text },
8926
+ messageId: data.messageId
8927
+ }
8928
+ });
8929
+ continue;
8930
+ }
8931
+ if (data.type === "turn_completed" && data.turnId === args.turnId) {
8932
+ args.cancellation.finish(args.sessionId, args.cancellationSignal);
8933
+ return {
8934
+ stopReason: data.outcome === "cancelled" ? "cancelled" : "end_turn"
8935
+ };
8936
+ }
8937
+ if (data.type === "turn_failed" && data.turnId === args.turnId) {
8938
+ args.cancellation.finish(args.sessionId, args.cancellationSignal);
8939
+ throw acp.RequestError.internalError(
8940
+ { failureCode: data.failureCode },
8941
+ "Junior Turn failed"
8942
+ );
8943
+ }
8944
+ }
8945
+ }
8946
+ }
8947
+ async function runAcpOperation(authenticated, operation, conversationId, callback) {
8948
+ const name = `acp.${operation}`;
8949
+ return await withSpan(
8950
+ name,
8951
+ name,
8952
+ {
8953
+ actorId: authenticated.actor.userId,
8954
+ conversationId,
8955
+ platform: "acp",
8956
+ userId: authenticated.actor.userId
8957
+ },
8958
+ async () => {
8959
+ try {
8960
+ return await callback();
8961
+ } catch (error) {
8962
+ if (!(error instanceof acp.RequestError)) {
8963
+ logException(error, `${name}.failed`);
8964
+ }
8965
+ throw error;
8966
+ }
8967
+ }
8968
+ );
8969
+ }
8970
+ function createActorAgent(authenticated, connectionNonce, options) {
8971
+ const conversationStore = options.conversationStore ?? getConversationStore();
8972
+ const eventStore = getConversationEventStore();
8973
+ return acp.agent({ name: "junior" }).onRequest(
8974
+ acp.methods.agent.initialize,
8975
+ () => runAcpOperation(authenticated, "initialize", void 0, () => ({
8976
+ protocolVersion: acp.PROTOCOL_VERSION,
8977
+ agentCapabilities: {
8978
+ loadSession: true,
8979
+ promptCapabilities: {
8980
+ image: false,
8981
+ audio: false,
8982
+ embeddedContext: false
8983
+ }
8984
+ },
8985
+ authMethods: [],
8986
+ agentInfo: { name: "junior", version: JUNIOR_VERSION }
8987
+ }))
8988
+ ).onRequest(acp.methods.agent.session.new, async (context) => {
8989
+ return await runAcpOperation(
8990
+ authenticated,
8991
+ "session_new",
8992
+ void 0,
8993
+ async () => {
8994
+ rejectUnsupportedMcpServers(context.params.mcpServers);
8995
+ const sessionId = `${ACP_CONVERSATION_PREFIX}${randomUUID()}`;
8996
+ await recordApiConversationActivity({
8997
+ actor: authenticated.actor,
8998
+ conversationId: sessionId,
8999
+ conversationStore,
9000
+ nowMs: Date.now(),
9001
+ rootVisibility: "private"
9002
+ });
9003
+ return { sessionId };
9004
+ }
9005
+ );
9006
+ }).onRequest(acp.methods.agent.session.load, async (context) => {
9007
+ return await runAcpOperation(
9008
+ authenticated,
9009
+ "session_load",
9010
+ context.params.sessionId,
9011
+ async () => {
9012
+ rejectUnsupportedMcpServers(context.params.mcpServers);
9013
+ await requireOwnedSession(
9014
+ context.params.sessionId,
9015
+ authenticated.user
9016
+ );
9017
+ await replaySession(
9018
+ eventStore,
9019
+ context.params.sessionId,
9020
+ context.client
9021
+ );
9022
+ return {};
9023
+ }
9024
+ );
9025
+ }).onRequest(acp.methods.agent.session.prompt, async (context) => {
9026
+ return await runAcpOperation(
9027
+ authenticated,
9028
+ "session_prompt",
9029
+ context.params.sessionId,
9030
+ async () => {
9031
+ await requireOwnedSession(
9032
+ context.params.sessionId,
9033
+ authenticated.user
9034
+ );
9035
+ const text = promptText(context.params.prompt);
9036
+ const currentSeq = await latestEventSeq(
9037
+ eventStore,
9038
+ context.params.sessionId
9039
+ );
9040
+ const cancellationSignal = options.cancellation.begin(
9041
+ context.params.sessionId
9042
+ );
9043
+ if (!cancellationSignal) {
9044
+ throw acp.RequestError.invalidParams(
9045
+ { field: "sessionId" },
9046
+ "This ACP session already has an active prompt"
9047
+ );
9048
+ }
9049
+ const recordDisconnect = () => options.cancellation.disconnect(
9050
+ context.params.sessionId,
9051
+ cancellationSignal
9052
+ );
9053
+ if (context.signal.aborted) {
9054
+ recordDisconnect();
9055
+ } else {
9056
+ context.signal.addEventListener("abort", recordDisconnect, {
9057
+ once: true
9058
+ });
9059
+ }
9060
+ let accepted;
9061
+ try {
9062
+ accepted = await appendAndEnqueueApiConversationMessage(
9063
+ {
9064
+ actor: authenticated.actor,
9065
+ conversationId: context.params.sessionId,
9066
+ idempotencyKey: `${connectionNonce}:${requestIdKey(context.requestId)}`,
9067
+ message: text
9068
+ },
9069
+ {
9070
+ conversationStore,
9071
+ queue: options.queue,
9072
+ state: options.state
9073
+ }
9074
+ );
9075
+ } catch (error) {
9076
+ options.cancellation.finish(
9077
+ context.params.sessionId,
9078
+ cancellationSignal
9079
+ );
9080
+ throw error;
9081
+ }
9082
+ const afterSeq = accepted.status === "duplicate" ? 0 : currentSeq;
9083
+ return await waitForTurn({
9084
+ afterSeq,
9085
+ cancellation: options.cancellation,
9086
+ cancellationSignal,
9087
+ client: context.client,
9088
+ eventStore,
9089
+ sessionId: context.params.sessionId,
9090
+ signal: context.signal,
9091
+ turnId: apiTurnIdForMessage(accepted.messageId)
9092
+ });
9093
+ }
9094
+ );
9095
+ }).onNotification(acp.methods.agent.session.cancel, async (context) => {
9096
+ await runAcpOperation(
9097
+ authenticated,
9098
+ "session_cancel",
9099
+ context.params.sessionId,
9100
+ async () => {
9101
+ await requireOwnedSession(
9102
+ context.params.sessionId,
9103
+ authenticated.user
9104
+ );
9105
+ if (!options.cancellation.cancel(context.params.sessionId)) {
9106
+ return;
9107
+ }
9108
+ const nowMs = Date.now();
9109
+ await ensureConversationWake({
9110
+ conversationId: context.params.sessionId,
9111
+ conversationStore,
9112
+ idempotencyKey: `acp-cancel:${context.params.sessionId}:${nowMs}`,
9113
+ nowMs,
9114
+ queue: options.queue,
9115
+ replaceExistingWake: true,
9116
+ state: options.state
9117
+ });
9118
+ }
9119
+ );
9120
+ });
9121
+ }
9122
+ function isJsonRpcId(value) {
9123
+ return value === null || typeof value === "string" || typeof value === "number" && Number.isFinite(value);
9124
+ }
9125
+ async function hasSafeAcpEnvelope(request) {
9126
+ if (request.method !== "POST") return true;
9127
+ let value;
9128
+ try {
9129
+ value = await request.clone().json();
9130
+ } catch {
9131
+ return true;
9132
+ }
9133
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
9134
+ return true;
9135
+ }
9136
+ if (!("jsonrpc" in value) || value.jsonrpc !== "2.0") {
9137
+ return false;
9138
+ }
9139
+ if (!("method" in value) || typeof value.method !== "string") {
9140
+ return false;
9141
+ }
9142
+ return !("id" in value) || isJsonRpcId(value.id);
9143
+ }
9144
+ async function initializationSucceeded(response) {
9145
+ try {
9146
+ const value = await response.clone().json();
9147
+ return typeof value === "object" && value !== null && "result" in value;
9148
+ } catch {
9149
+ return false;
9150
+ }
9151
+ }
9152
+ function createAcpHttpHandler(options) {
9153
+ const server = new AcpServer({ agent: acp.agent({ name: "junior" }) });
9154
+ const connectionActors = /* @__PURE__ */ new Map();
9155
+ return async (request) => {
9156
+ const authenticated = await authenticateRequest(request);
9157
+ if (!authenticated) {
9158
+ return new Response("Unauthorized", { status: 401 });
9159
+ }
9160
+ const connectionId = request.headers.get(ACP_CONNECTION_ID_HEADER);
9161
+ const connectionActorId = connectionId ? connectionActors.get(connectionId) : void 0;
9162
+ if (connectionId && connectionActorId === void 0) {
9163
+ return new Response("Unknown Acp-Connection-Id", { status: 404 });
9164
+ }
9165
+ if (connectionActorId !== void 0 && connectionActorId !== authenticated.actor.userId) {
9166
+ return new Response("Unknown Acp-Connection-Id", { status: 404 });
9167
+ }
9168
+ if (!await hasSafeAcpEnvelope(request)) {
9169
+ return new Response("Invalid JSON-RPC message", { status: 400 });
9170
+ }
9171
+ const response = await server.handleRequest(request, {
9172
+ agent: createActorAgent(authenticated, randomUUID(), options)
9173
+ });
9174
+ const responseConnectionId = response.headers.get(ACP_CONNECTION_ID_HEADER);
9175
+ if (response.ok && responseConnectionId && !connectionId) {
9176
+ if (await initializationSucceeded(response)) {
9177
+ connectionActors.set(responseConnectionId, authenticated.actor.userId);
9178
+ } else {
9179
+ await server.handleRequest(
9180
+ new Request(request.url, {
9181
+ method: "DELETE",
9182
+ headers: { [ACP_CONNECTION_ID_HEADER]: responseConnectionId }
9183
+ })
9184
+ );
9185
+ const headers = new Headers(response.headers);
9186
+ headers.delete(ACP_CONNECTION_ID_HEADER);
9187
+ return new Response(response.body, {
9188
+ headers,
9189
+ status: response.status,
9190
+ statusText: response.statusText
9191
+ });
9192
+ }
9193
+ }
9194
+ if (request.method === "DELETE" && response.ok && connectionId) {
9195
+ connectionActors.delete(connectionId);
9196
+ }
9197
+ return response;
9198
+ };
9199
+ }
9200
+
8777
9201
  // src/app.ts
8778
9202
  var DASHBOARD_PACKAGE_NAME = "@sentry/junior-dashboard";
8779
9203
  async function defaultWaitUntil() {
@@ -8975,6 +9399,8 @@ function dashboardHostRoutePaths(dashboard) {
8975
9399
  "/api/people/*",
8976
9400
  "/api/personal-tokens",
8977
9401
  "/api/personal-tokens/*",
9402
+ "/api/workspaces",
9403
+ "/api/workspaces/*",
8978
9404
  "/api/config",
8979
9405
  "/api/me",
8980
9406
  authPath,
@@ -9065,11 +9491,11 @@ function validateDashboardRouteOwnership(args) {
9065
9491
  function mountRoutes(app, routes) {
9066
9492
  for (const route of routes) {
9067
9493
  const handler = (c) => route.handler(c.req.raw);
9068
- const methods = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
9069
- const explicitMethods = methods.filter(
9494
+ const methods2 = Array.isArray(route.method) ? route.method : [route.method ?? "ALL"];
9495
+ const explicitMethods = methods2.filter(
9070
9496
  (method) => method !== "ALL"
9071
9497
  );
9072
- if (methods.includes("ALL")) {
9498
+ if (methods2.includes("ALL")) {
9073
9499
  app.all(route.path, handler);
9074
9500
  } else if (explicitMethods.length > 0) {
9075
9501
  app.on(explicitMethods, route.path, handler);
@@ -9245,6 +9671,24 @@ async function createApp(options) {
9245
9671
  });
9246
9672
  return conversationWorkOptions;
9247
9673
  };
9674
+ if (isExperimentalFeatureEnabled("acp")) {
9675
+ const work = getConversationWorkOptions();
9676
+ const cancellation = work.apiTurnCancellation;
9677
+ if (!cancellation) {
9678
+ throw new Error("Experimental ACP requires API Turn cancellation wiring");
9679
+ }
9680
+ const handleAcpRequest = createAcpHttpHandler({
9681
+ cancellation,
9682
+ conversationStore: work.conversationStore,
9683
+ queue: work.queue ?? getVercelConversationWorkQueue(),
9684
+ state: work.state
9685
+ });
9686
+ app.on(
9687
+ ["GET", "POST", "DELETE"],
9688
+ "/api/acp",
9689
+ (c) => handleAcpRequest(c.req.raw)
9690
+ );
9691
+ }
9248
9692
  if (process.env.NODE_ENV === "development") {
9249
9693
  registerVercelConversationWorkDevConsumer(getConversationWorkOptions());
9250
9694
  registerVercelPluginTaskDevConsumer();