@sentry/junior 0.138.0 → 0.140.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 (81) hide show
  1. package/dist/{agent-hooks-SCR74Q3E.js → agent-hooks-BFTDRFUO.js} +10 -10
  2. package/dist/api/schema/task.d.ts +104 -1
  3. package/dist/api/schema.d.ts +2 -2
  4. package/dist/api/schema.js +9 -1
  5. package/dist/api.js +198 -54
  6. package/dist/app.js +374 -607
  7. package/dist/{catalog-runtime-IU3QF4GG.js → catalog-runtime-RK664S5T.js} +6 -6
  8. package/dist/chat/agent/request.d.ts +12 -2
  9. package/dist/chat/credentials/context.d.ts +14 -0
  10. package/dist/chat/resource-events/actor.d.ts +23 -0
  11. package/dist/chat/scheduled-tasks/telemetry.d.ts +7 -0
  12. package/dist/chat/scheduled-tasks/tool-support.d.ts +10 -2
  13. package/dist/chat/scheduled-tasks/tools/create.d.ts +4 -2
  14. package/dist/chat/scheduled-tasks/tools/delete.d.ts +4 -2
  15. package/dist/chat/scheduled-tasks/tools/list.d.ts +4 -2
  16. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +4 -2
  17. package/dist/chat/scheduled-tasks/tools/update.d.ts +4 -2
  18. package/dist/chat/scheduled-tasks/types.d.ts +1 -1
  19. package/dist/chat/slack/dashboard-link.d.ts +2 -0
  20. package/dist/chat/state/turn-session-keys.d.ts +2 -2
  21. package/dist/chat/state/turn-session.d.ts +16 -12
  22. package/dist/chat/tasks/execution-stats.d.ts +34 -3
  23. package/dist/chat/tasks/read.d.ts +3 -1
  24. package/dist/chunk-3B7O3GNU.js +207 -0
  25. package/dist/{chunk-N7WARMC6.js → chunk-3CUBAGA3.js} +7 -7
  26. package/dist/{chunk-5MXWHTCK.js → chunk-4PIT6O7A.js} +17 -8
  27. package/dist/{chunk-DCLLQGS6.js → chunk-557HLSNH.js} +8 -3
  28. package/dist/{chunk-SD3OFRWY.js → chunk-5KAZMR5U.js} +6 -6
  29. package/dist/{chunk-ZBSMCXKV.js → chunk-5YAWB2AX.js} +7 -6
  30. package/dist/{chunk-FEUNPTGV.js → chunk-A6BI25J3.js} +434 -12
  31. package/dist/{chunk-5LTJ4ITU.js → chunk-AA2EPMUF.js} +4 -4
  32. package/dist/{chunk-TUUDXEXU.js → chunk-BMFRFSTM.js} +68 -79
  33. package/dist/{chunk-C7ZVPO4Z.js → chunk-CPPTBHQN.js} +1 -1
  34. package/dist/{chunk-ZXNTJJIE.js → chunk-D3X2PI3D.js} +2 -2
  35. package/dist/{chunk-LPVUNO2W.js → chunk-DMDOKRPC.js} +7 -7
  36. package/dist/{chunk-TCS76X5D.js → chunk-HEFBGCCU.js} +2 -2
  37. package/dist/{chunk-EPBBYMNE.js → chunk-HUO3UTAQ.js} +1 -1
  38. package/dist/{chunk-KFRHRSCJ.js → chunk-I3OZR3HG.js} +2 -2
  39. package/dist/{chunk-BOLJOV5T.js → chunk-J5H65GY2.js} +1 -1
  40. package/dist/{chunk-RZIBSCYC.js → chunk-MYVCHWDG.js} +39 -23
  41. package/dist/{chunk-QS527AV5.js → chunk-N2I7W2VS.js} +1 -1
  42. package/dist/{chunk-JNNJZURV.js → chunk-NPL66ZWL.js} +1 -1
  43. package/dist/{chunk-A7WCHXZV.js → chunk-OGNL2QY7.js} +4 -4
  44. package/dist/{chunk-77VDLF3Z.js → chunk-RUIXF3YQ.js} +10 -0
  45. package/dist/{chunk-BYW4B4PR.js → chunk-S3FAOKQU.js} +2 -2
  46. package/dist/chunk-TGIKZJ3Y.js +115 -0
  47. package/dist/{chunk-YU5QUF67.js → chunk-TKTZ4L4Z.js} +1 -1
  48. package/dist/{chunk-VPZFVJND.js → chunk-UXMRN2ZX.js} +1 -1
  49. package/dist/{chunk-HIWJWWMT.js → chunk-VRDHAITB.js} +3 -3
  50. package/dist/{chunk-SIPKWSXK.js → chunk-WSWM6A3R.js} +3 -3
  51. package/dist/{chunk-FNLBTB4K.js → chunk-WTGLUX4K.js} +1 -1
  52. package/dist/{chunk-PB3CXNVL.js → chunk-XROWZSOY.js} +1 -1
  53. package/dist/{chunk-TDJCLNDH.js → chunk-XX2QNQN4.js} +147 -86
  54. package/dist/{chunk-KDCHVT25.js → chunk-YB363QGA.js} +9 -2
  55. package/dist/{chunk-HCIVH3JS.js → chunk-YWM6MJT4.js} +29 -1
  56. package/dist/{chunk-E5UYAIKJ.js → chunk-Z2A7BDHT.js} +17 -19
  57. package/dist/cli/chat.js +33 -33
  58. package/dist/cli/check.js +7 -7
  59. package/dist/cli/plugins.js +14 -14
  60. package/dist/cli/snapshot-create.js +10 -10
  61. package/dist/cli/upgrade.js +8 -8
  62. package/dist/db/schema/task-executions.d.ts +23 -2
  63. package/dist/db/schema.d.ts +20 -1
  64. package/dist/{db-UO2IWXGA.js → db-QOGYDDQM.js} +7 -7
  65. package/dist/instrumentation.js +4 -4
  66. package/dist/nitro.js +2 -2
  67. package/dist/{oauth-callback-server-MAQI43LX.js → oauth-callback-server-CT4RMGCP.js} +22 -21
  68. package/dist/{runner-PIUQEXKE.js → runner-PHMLHNLX.js} +18 -16
  69. package/dist/{sandbox-egress-signals-A5HWIQH4.js → sandbox-egress-signals-EKPAVWQL.js} +2 -2
  70. package/dist/{skills-4ARJADHQ.js → skills-AYRM4QGF.js} +7 -7
  71. package/dist/spawn-OGRQFUNK.js +38 -0
  72. package/dist/{validation-S3EE3HWL.js → validation-M5JNGRGL.js} +7 -7
  73. package/dist/version.js +1 -1
  74. package/dist/{work-UARRUAW3.js → work-LZWE6HK4.js} +18 -18
  75. package/dist/{worker-DOCIRVD3.js → worker-KOGPXFYK.js} +10 -10
  76. package/migrations/0022_task_execution_status.sql +3 -0
  77. package/migrations/meta/0022_snapshot.json +2186 -0
  78. package/migrations/meta/_journal.json +7 -0
  79. package/package.json +5 -5
  80. package/dist/chunk-ELKW5FIB.js +0 -233
  81. package/dist/spawn-G2AWPMCT.js +0 -38
@@ -1,3 +1,6 @@
1
+ import {
2
+ recordTaskExecution
3
+ } from "./chunk-3B7O3GNU.js";
1
4
  import {
2
5
  pluginTaskId,
3
6
  pluginTaskParamsSchema,
@@ -22,7 +25,10 @@ import {
22
25
  persistThreadStateById,
23
26
  stripRuntimeTurnContext,
24
27
  updateConversationStats
25
- } from "./chunk-TUUDXEXU.js";
28
+ } from "./chunk-BMFRFSTM.js";
29
+ import {
30
+ JUNIOR_THREAD_STATE_TTL_MS
31
+ } from "./chunk-3Y47VMHV.js";
26
32
  import {
27
33
  createPluginConversationEvents,
28
34
  createPluginEmbedder,
@@ -30,26 +36,30 @@ import {
30
36
  createPluginModel,
31
37
  createPluginState,
32
38
  getPlugins
33
- } from "./chunk-5MXWHTCK.js";
39
+ } from "./chunk-4PIT6O7A.js";
34
40
  import {
41
+ getConversationStore,
35
42
  getDb,
36
43
  sameActorIdentity
37
- } from "./chunk-SIPKWSXK.js";
44
+ } from "./chunk-WSWM6A3R.js";
38
45
  import {
39
46
  getStateAdapter
40
- } from "./chunk-BOLJOV5T.js";
47
+ } from "./chunk-J5H65GY2.js";
41
48
  import {
42
49
  ProviderError,
43
50
  findProviderError,
44
51
  getProviderErrorAttributes,
45
52
  getProviderErrorUserMessage
46
- } from "./chunk-BYW4B4PR.js";
53
+ } from "./chunk-S3FAOKQU.js";
54
+ import {
55
+ credentialSubjectSchema
56
+ } from "./chunk-RUIXF3YQ.js";
47
57
  import {
48
58
  parseSlackMessageTs
49
59
  } from "./chunk-F3B2YJ76.js";
50
60
  import {
51
61
  buildTurnFailureResponse
52
- } from "./chunk-JNNJZURV.js";
62
+ } from "./chunk-NPL66ZWL.js";
53
63
 
54
64
  // src/chat/runtime/delivered-turn-state.ts
55
65
  function buildDeliveredTurnStatePatch(args) {
@@ -85,7 +95,7 @@ import {
85
95
 
86
96
  // src/chat/services/turn-session-routing.ts
87
97
  async function resolveTurnSessionRouting(args) {
88
- const conversationStore = args.conversationStore ?? (await import("./db-UO2IWXGA.js")).getConversationStore();
98
+ const conversationStore = args.conversationStore ?? (await import("./db-QOGYDDQM.js")).getConversationStore();
89
99
  const conversation = await conversationStore.get({
90
100
  conversationId: args.conversationId
91
101
  });
@@ -100,6 +110,397 @@ async function resolveTurnSessionRouting(args) {
100
110
  };
101
111
  }
102
112
 
113
+ // src/chat/agent-dispatch/store.ts
114
+ import { createHash } from "crypto";
115
+ import {
116
+ destinationSchema,
117
+ destinationVisibilitySchema,
118
+ isSlackDestination,
119
+ replyAttributionSchema,
120
+ sourceSchema
121
+ } from "@sentry/junior-plugin-api";
122
+ import { z } from "zod";
123
+ var DISPATCH_PREFIX = "junior:agent_dispatch";
124
+ var DISPATCH_LOCK_TTL_MS = 10 * 60 * 1e3;
125
+ var DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS = 1e4;
126
+ var DISPATCH_MAILBOX_APPEND_INDEX_MAX_LENGTH = 1e4;
127
+ var nonEmptyExactStringSchema = z.string().min(1).refine(
128
+ (value) => value === value.trim() && value.toLowerCase() !== "unknown"
129
+ );
130
+ var pendingDispatchMailboxAppendIndexSchema = z.array(
131
+ nonEmptyExactStringSchema
132
+ );
133
+ var dispatchStatusSchema = z.enum([
134
+ "pending",
135
+ "running",
136
+ "awaiting_resume",
137
+ "completed",
138
+ "failed",
139
+ "blocked"
140
+ ]);
141
+ var dispatchActorSchema = z.object({
142
+ platform: z.literal("system"),
143
+ name: nonEmptyExactStringSchema
144
+ }).strict();
145
+ var dispatchRecordSchema = z.object({
146
+ actor: dispatchActorSchema,
147
+ createdAtMs: z.number().finite(),
148
+ credentialSubject: credentialSubjectSchema.optional(),
149
+ destination: destinationSchema,
150
+ destinationVisibility: destinationVisibilitySchema,
151
+ errorMessage: z.string().optional(),
152
+ id: nonEmptyExactStringSchema,
153
+ idempotencyKey: z.string().min(1),
154
+ input: z.string().min(1),
155
+ metadata: z.record(z.string(), z.string()).optional(),
156
+ plugin: nonEmptyExactStringSchema,
157
+ replyAttribution: replyAttributionSchema.optional(),
158
+ resultMessageTs: z.string().optional(),
159
+ source: sourceSchema,
160
+ status: dispatchStatusSchema,
161
+ updatedAtMs: z.number().finite()
162
+ }).strict().superRefine((record, ctx) => {
163
+ if (!isSlackDestination(record.destination)) {
164
+ ctx.addIssue({
165
+ code: z.ZodIssueCode.custom,
166
+ message: "Dispatch destination platform must be slack",
167
+ path: ["destination"]
168
+ });
169
+ return;
170
+ }
171
+ const subject = record.credentialSubject;
172
+ if (!subject) {
173
+ return;
174
+ }
175
+ if (subject.allowedWhen === "private-direct-conversation") {
176
+ if (!record.destination.channelId.startsWith("D")) {
177
+ ctx.addIssue({
178
+ code: z.ZodIssueCode.custom,
179
+ message: "Dispatch credentialSubject requires a private direct Slack destination",
180
+ path: ["credentialSubject"]
181
+ });
182
+ return;
183
+ }
184
+ if (subject.binding.type !== "slack-direct-conversation" || subject.binding.teamId !== record.destination.teamId || subject.binding.channelId !== record.destination.channelId) {
185
+ ctx.addIssue({
186
+ code: z.ZodIssueCode.custom,
187
+ message: "Dispatch credentialSubject binding must match destination",
188
+ path: ["credentialSubject", "binding"]
189
+ });
190
+ }
191
+ return;
192
+ }
193
+ if (subject.binding.type !== subject.allowedWhen || subject.binding.plugin !== record.plugin || subject.binding.taskId !== subject.taskId) {
194
+ ctx.addIssue({
195
+ code: z.ZodIssueCode.custom,
196
+ message: "Dispatch credentialSubject binding must match task",
197
+ path: ["credentialSubject", "binding"]
198
+ });
199
+ }
200
+ });
201
+ var storedDispatchRecordSchema = dispatchRecordSchema.extend({
202
+ attempt: z.number().int().nonnegative().optional(),
203
+ lastCallbackAtMs: z.number().finite().optional(),
204
+ leaseExpiresAtMs: z.number().finite().optional(),
205
+ maxAttempts: z.number().int().positive().optional(),
206
+ version: z.number().int().positive().optional()
207
+ });
208
+ function getDispatchStorageKey(id) {
209
+ return `${DISPATCH_PREFIX}:record:${id}`;
210
+ }
211
+ function dispatchLockKey(id) {
212
+ return `${DISPATCH_PREFIX}:lock:${id}`;
213
+ }
214
+ function pendingDispatchMailboxAppendIndexKey() {
215
+ return `${DISPATCH_PREFIX}:incomplete`;
216
+ }
217
+ function pendingDispatchMailboxAppendIndexLockKey() {
218
+ return `${DISPATCH_PREFIX}:incomplete:lock`;
219
+ }
220
+ function normalizeMetadata(metadata) {
221
+ if (!metadata) {
222
+ return void 0;
223
+ }
224
+ const entries = Object.entries(metadata).filter(
225
+ (entry) => typeof entry[0] === "string" && typeof entry[1] === "string"
226
+ );
227
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
228
+ }
229
+ function buildDispatchId(plugin, idempotencyKey) {
230
+ const digest = createHash("sha256").update(plugin).update("\0").update(idempotencyKey).digest("hex").slice(0, 32);
231
+ return `dispatch_${digest}`;
232
+ }
233
+ function parseDispatchRecord(value) {
234
+ const parsed = parseStoredDispatchRecord(value);
235
+ return parsed?.record;
236
+ }
237
+ function parseStoredDispatchRecord(value) {
238
+ const parsed = storedDispatchRecordSchema.safeParse(value);
239
+ if (!parsed.success) {
240
+ return void 0;
241
+ }
242
+ const {
243
+ attempt: _attempt,
244
+ lastCallbackAtMs: _lastCallbackAtMs,
245
+ leaseExpiresAtMs: _leaseExpiresAtMs,
246
+ maxAttempts: _maxAttempts,
247
+ version: _version,
248
+ ...record
249
+ } = parsed.data;
250
+ return {
251
+ ...typeof parsed.data.leaseExpiresAtMs === "number" ? { legacyLeaseExpiresAtMs: parsed.data.leaseExpiresAtMs } : {},
252
+ record
253
+ };
254
+ }
255
+ function getDispatchConversationId(dispatch) {
256
+ return `agent-dispatch:${dispatch.id}`;
257
+ }
258
+ function getDispatchInputMessageId(dispatchId) {
259
+ return `agent-dispatch:${dispatchId}`;
260
+ }
261
+ function getLegacyDispatchInputMessageId(dispatchId) {
262
+ return `dispatch:${dispatchId}:user`;
263
+ }
264
+ function getDispatchInputMessageIds(dispatchId) {
265
+ return [
266
+ getDispatchInputMessageId(dispatchId),
267
+ getLegacyDispatchInputMessageId(dispatchId)
268
+ ];
269
+ }
270
+ function getDispatchTurnId(dispatchId) {
271
+ return `dispatch:${dispatchId}`;
272
+ }
273
+ function toDispatchProjection(record) {
274
+ return {
275
+ id: record.id,
276
+ status: record.status,
277
+ ...record.resultMessageTs ? { resultMessageTs: record.resultMessageTs } : {},
278
+ ...record.errorMessage ? { errorMessage: record.errorMessage } : {}
279
+ };
280
+ }
281
+ function isTerminalDispatchStatus(status) {
282
+ return status === "completed" || status === "failed" || status === "blocked";
283
+ }
284
+ async function withDispatchLock(dispatchId, task) {
285
+ const state = getStateAdapter();
286
+ await state.connect();
287
+ const lock = await state.acquireLock(
288
+ dispatchLockKey(dispatchId),
289
+ DISPATCH_LOCK_TTL_MS
290
+ );
291
+ if (!lock) {
292
+ throw new Error(`Could not acquire dispatch lock for ${dispatchId}`);
293
+ }
294
+ try {
295
+ return await task(state);
296
+ } finally {
297
+ await state.releaseLock(lock);
298
+ }
299
+ }
300
+ async function putRecord(state, record) {
301
+ const parsed = dispatchRecordSchema.safeParse(record);
302
+ if (!parsed.success) {
303
+ throw new Error(
304
+ `Dispatch record is invalid: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"} ${issue.message}`).join("; ")}`
305
+ );
306
+ }
307
+ const next = parsed.data;
308
+ await state.set(
309
+ getDispatchStorageKey(next.id),
310
+ next,
311
+ JUNIOR_THREAD_STATE_TTL_MS
312
+ );
313
+ }
314
+ async function updatePendingDispatchMailboxAppendIndex(state, update) {
315
+ const lock = await state.acquireLock(
316
+ pendingDispatchMailboxAppendIndexLockKey(),
317
+ DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS
318
+ );
319
+ if (!lock) {
320
+ throw new Error("Could not acquire dispatch mailbox append index lock");
321
+ }
322
+ try {
323
+ const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
324
+ const ids = stored === void 0 || stored === null ? [] : pendingDispatchMailboxAppendIndexSchema.parse(stored);
325
+ const next = [...new Set(update([...new Set(ids)]))].slice(
326
+ -DISPATCH_MAILBOX_APPEND_INDEX_MAX_LENGTH
327
+ );
328
+ if (next.length === ids.length && next.every((id, index) => id === ids[index])) {
329
+ return;
330
+ }
331
+ await state.set(
332
+ pendingDispatchMailboxAppendIndexKey(),
333
+ next,
334
+ JUNIOR_THREAD_STATE_TTL_MS
335
+ );
336
+ } finally {
337
+ await state.releaseLock(lock);
338
+ }
339
+ }
340
+ async function getDispatchRecord(id) {
341
+ const state = getStateAdapter();
342
+ await state.connect();
343
+ return parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
344
+ }
345
+ async function createOrGetDispatch(args) {
346
+ const id = buildDispatchId(args.plugin, args.options.idempotencyKey);
347
+ return await withDispatchLock(id, async (state) => {
348
+ const existing = parseDispatchRecord(
349
+ await state.get(getDispatchStorageKey(id))
350
+ );
351
+ if (existing) {
352
+ return { record: existing, status: "already_exists" };
353
+ }
354
+ const metadata = normalizeMetadata(args.options.metadata);
355
+ const record = {
356
+ actor: { platform: "system", name: args.plugin },
357
+ createdAtMs: args.nowMs,
358
+ ...args.options.credentialSubject ? { credentialSubject: args.options.credentialSubject } : {},
359
+ destination: args.options.destination,
360
+ destinationVisibility: args.options.destinationVisibility,
361
+ id,
362
+ idempotencyKey: args.options.idempotencyKey,
363
+ input: args.options.input,
364
+ ...metadata ? { metadata } : {},
365
+ plugin: args.plugin,
366
+ ...args.options.replyAttribution ? { replyAttribution: args.options.replyAttribution } : {},
367
+ status: "pending",
368
+ source: args.options.source,
369
+ updatedAtMs: args.nowMs
370
+ };
371
+ await updatePendingDispatchMailboxAppendIndex(
372
+ state,
373
+ (ids) => ids.includes(id) ? ids : [...ids, id]
374
+ );
375
+ await putRecord(state, record);
376
+ return { record, status: "created" };
377
+ });
378
+ }
379
+ async function transitionDispatch(id, transition) {
380
+ return await withDispatchLock(id, async (state) => {
381
+ const current = parseDispatchRecord(
382
+ await state.get(getDispatchStorageKey(id))
383
+ );
384
+ if (!current) {
385
+ return void 0;
386
+ }
387
+ const transitioned = transition(current);
388
+ if (transitioned === current) {
389
+ return current;
390
+ }
391
+ const next = {
392
+ ...transitioned,
393
+ updatedAtMs: Date.now()
394
+ };
395
+ await putRecord(state, next);
396
+ return next;
397
+ });
398
+ }
399
+ async function markDispatchRunning(id) {
400
+ return await transitionDispatch(
401
+ id,
402
+ (record) => isTerminalDispatchStatus(record.status) ? record : { ...record, status: "running", errorMessage: void 0 }
403
+ );
404
+ }
405
+ async function markDispatchAwaitingResume(id) {
406
+ return await transitionDispatch(
407
+ id,
408
+ (record) => isTerminalDispatchStatus(record.status) ? record : {
409
+ ...record,
410
+ status: "awaiting_resume"
411
+ }
412
+ );
413
+ }
414
+ async function recordEventTaskExecution(previous, next, status) {
415
+ if (!next || next.status !== status || previous?.status === status) return;
416
+ if (next.plugin !== "junior") return;
417
+ const eventTaskId = next.metadata?.eventTaskId;
418
+ if (!eventTaskId) return;
419
+ const conversationId = getDispatchConversationId(next);
420
+ const conversation = await getConversationStore().get({ conversationId });
421
+ await recordTaskExecution("event", eventTaskId, {
422
+ ...conversation ? { conversationId } : {},
423
+ executionId: next.id,
424
+ nowMs: next.updatedAtMs,
425
+ status
426
+ });
427
+ }
428
+ async function markDispatchBlocked(id, errorMessage, resultMessageTs) {
429
+ const previous = await getDispatchRecord(id);
430
+ const next = await transitionDispatch(
431
+ id,
432
+ (record) => isTerminalDispatchStatus(record.status) ? record : {
433
+ ...record,
434
+ errorMessage,
435
+ ...resultMessageTs ? { resultMessageTs } : {},
436
+ status: "blocked"
437
+ }
438
+ );
439
+ await recordEventTaskExecution(previous, next, "blocked");
440
+ return next;
441
+ }
442
+ async function markDispatchCompleted(id, resultMessageTs) {
443
+ const previous = await getDispatchRecord(id);
444
+ const next = await transitionDispatch(
445
+ id,
446
+ (record) => isTerminalDispatchStatus(record.status) ? record : {
447
+ ...record,
448
+ errorMessage: void 0,
449
+ ...resultMessageTs ? { resultMessageTs } : {},
450
+ status: "completed"
451
+ }
452
+ );
453
+ await recordEventTaskExecution(previous, next, "completed");
454
+ return next;
455
+ }
456
+ async function markDispatchFailed(id, errorMessage, resultMessageTs) {
457
+ const previous = await getDispatchRecord(id);
458
+ const next = await transitionDispatch(
459
+ id,
460
+ (record) => isTerminalDispatchStatus(record.status) ? record : {
461
+ ...record,
462
+ errorMessage,
463
+ ...resultMessageTs ? { resultMessageTs } : {},
464
+ status: "failed"
465
+ }
466
+ );
467
+ await recordEventTaskExecution(previous, next, "failed");
468
+ return next;
469
+ }
470
+ async function confirmDispatchMailboxAppend(id) {
471
+ const state = getStateAdapter();
472
+ await state.connect();
473
+ await updatePendingDispatchMailboxAppendIndex(
474
+ state,
475
+ (ids) => ids.filter((candidate) => candidate !== id)
476
+ );
477
+ }
478
+ async function listPendingDispatchMailboxAppends() {
479
+ const state = getStateAdapter();
480
+ await state.connect();
481
+ const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
482
+ return stored === void 0 || stored === null ? [] : [...new Set(pendingDispatchMailboxAppendIndexSchema.parse(stored))];
483
+ }
484
+ async function claimDispatchMailboxAppend(id, nowMs) {
485
+ return await withDispatchLock(id, async (state) => {
486
+ const stored = parseStoredDispatchRecord(
487
+ await state.get(getDispatchStorageKey(id))
488
+ );
489
+ if (!stored || stored.legacyLeaseExpiresAtMs && stored.legacyLeaseExpiresAtMs > nowMs) {
490
+ return void 0;
491
+ }
492
+ await putRecord(state, stored.record);
493
+ return stored.record;
494
+ });
495
+ }
496
+ async function getPluginDispatchProjection(args) {
497
+ const record = await getDispatchRecord(args.id);
498
+ if (!record || record.plugin !== args.plugin) {
499
+ return void 0;
500
+ }
501
+ return toDispatchProjection(record);
502
+ }
503
+
103
504
  // src/chat/plugins/task-runner.ts
104
505
  var PLUGIN_TASK_LOCK_TTL_MS = 5 * 60 * 1e3;
105
506
  function isRecord(value) {
@@ -229,7 +630,7 @@ function messageExistedAtRunCompletion(message, completedAtMs) {
229
630
  const messageTimestampMs = conversationMessageTimestampMs(message);
230
631
  return messageTimestampMs !== void 0 && messageTimestampMs <= completedAtMs;
231
632
  }
232
- async function loadConversationContextTranscriptEntries(record, source) {
633
+ async function loadConversationContextTranscriptEntries(record, source, runActor) {
233
634
  if (source.platform !== "slack" || isPrivateSource(source)) {
234
635
  return [];
235
636
  }
@@ -260,7 +661,7 @@ async function loadConversationContextTranscriptEntries(record, source) {
260
661
  authority: "context",
261
662
  ...author ? { actor: author } : {}
262
663
  },
263
- isRunActor: sameActorIdentity(author, record.actor)
664
+ isRunActor: sameActorIdentity(author, runActor)
264
665
  });
265
666
  }
266
667
  return entries;
@@ -295,13 +696,18 @@ async function loadPluginRun(params) {
295
696
  const routing = await resolveTurnSessionRouting({
296
697
  conversationId: params.conversationId
297
698
  });
699
+ const runActor = record.actors[0] ?? (record.dispatchId ? (await getDispatchRecord(record.dispatchId))?.actor : void 0);
298
700
  const runEntries = turnMessagesWithProvenance(record).map(
299
- ({ message, provenance }) => runTranscriptEntry(message, provenance, record.actor)
701
+ ({ message, provenance }) => runTranscriptEntry(message, provenance, runActor)
300
702
  ).filter((entry) => Boolean(entry));
301
703
  const runMessageTexts = new Set(
302
704
  runEntries.filter((entry) => entry.type === "message").map((entry) => entry.text)
303
705
  );
304
- const contextEntries = (await loadConversationContextTranscriptEntries(record, routing.source)).filter(
706
+ const contextEntries = (await loadConversationContextTranscriptEntries(
707
+ record,
708
+ routing.source,
709
+ runActor
710
+ )).filter(
305
711
  (entry) => entry.type !== "message" || !runMessageTexts.has(entry.text)
306
712
  );
307
713
  return pluginRunContextSchema.parse({
@@ -311,7 +717,7 @@ async function loadPluginRun(params) {
311
717
  // Derived from the full run provenance on the record, not the sliced or
312
718
  // stripped transcript, so it reflects every committed instruction actor.
313
719
  actors: record.actors,
314
- ...record.actor ? { actor: record.actor } : {},
720
+ ...runActor ? { actor: runActor } : {},
315
721
  runId: record.sessionId,
316
722
  source: routing.source,
317
723
  transcript: [...contextEntries, ...runEntries]
@@ -548,6 +954,22 @@ async function persistAuthPauseTurnState(args) {
548
954
  }
549
955
 
550
956
  export {
957
+ getDispatchConversationId,
958
+ getDispatchInputMessageId,
959
+ getDispatchInputMessageIds,
960
+ getDispatchTurnId,
961
+ isTerminalDispatchStatus,
962
+ getDispatchRecord,
963
+ createOrGetDispatch,
964
+ markDispatchRunning,
965
+ markDispatchAwaitingResume,
966
+ markDispatchBlocked,
967
+ markDispatchCompleted,
968
+ markDispatchFailed,
969
+ confirmDispatchMailboxAppend,
970
+ listPendingDispatchMailboxAppends,
971
+ claimDispatchMailboxAppend,
972
+ getPluginDispatchProjection,
551
973
  buildDeliveredTurnStatePatch,
552
974
  getTurnUserMessage,
553
975
  getTurnUserMessageId,
@@ -5,14 +5,14 @@ import {
5
5
  getConversationStore,
6
6
  parseDestination,
7
7
  sameDestination
8
- } from "./chunk-SIPKWSXK.js";
8
+ } from "./chunk-WSWM6A3R.js";
9
9
  import {
10
10
  getDefaultRedisStateAdapterFor,
11
11
  getStateAdapter
12
- } from "./chunk-BOLJOV5T.js";
12
+ } from "./chunk-J5H65GY2.js";
13
13
  import {
14
14
  getChatConfig
15
- } from "./chunk-BYW4B4PR.js";
15
+ } from "./chunk-S3FAOKQU.js";
16
16
  import {
17
17
  parseStoredSlackActor
18
18
  } from "./chunk-HZF5P5UN.js";
@@ -20,7 +20,7 @@ import {
20
20
  isRecord,
21
21
  toOptionalNumber,
22
22
  toOptionalString
23
- } from "./chunk-JNNJZURV.js";
23
+ } from "./chunk-NPL66ZWL.js";
24
24
 
25
25
  // src/chat/task-execution/state.ts
26
26
  import { randomUUID } from "crypto";