@sentry/junior 0.246.0 → 0.247.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 (68) hide show
  1. package/dist/{admin-Bh-MYw-j.js → admin-BGwaqDMs.js} +1 -1
  2. package/dist/{agent-hooks-DpklSWZg.js → agent-hooks-C72ByH0C.js} +11 -11
  3. package/dist/{agent-invocations-BO6ddZax.js → agent-invocations-D_YoNnAn.js} +8 -2
  4. package/dist/{agent-runner-DSKpHjHW.js → agent-runner-DsAFAKKs.js} +451 -2412
  5. package/dist/api/schema.js +2 -2
  6. package/dist/api.js +24 -21
  7. package/dist/{app-BYB7szGb.d.ts → app-4t4mPTdS.d.ts} +8 -1
  8. package/dist/app.d.ts +1 -1
  9. package/dist/app.js +30 -27
  10. package/dist/assistant-reply-ChOzcTBS.js +45 -0
  11. package/dist/{briefs-cZ5vWqP_.js → briefs-Dc7qZrhc.js} +4 -4
  12. package/dist/{catalog-runtime-CTaQXYqS.js → catalog-runtime-DS1k7fUG.js} +1 -1
  13. package/dist/cli/chat.js +22 -16
  14. package/dist/cli/check.js +3 -3
  15. package/dist/cli/main.js +2 -2
  16. package/dist/cli/plugins.js +4 -4
  17. package/dist/cli/snapshot-create.js +3 -3
  18. package/dist/cli/upgrade.js +3 -3
  19. package/dist/{client--xtsAxKP.js → client-BVnfNd3D.js} +2 -2
  20. package/dist/{config-BK53Ip5C.js → config-CgWvImQL.js} +2 -2
  21. package/dist/{conversation-CvBG57Io.js → conversation-CYsbVFLx.js} +71 -1
  22. package/dist/{conversation-privacy-BDBFw6Ws.js → conversation-privacy-D3TdVM9H.js} +1 -1
  23. package/dist/{credential-sync-BiuhC9Ds.js → credential-sync-DLw3jkv-.js} +14 -14
  24. package/dist/{executor-bmmuRY-w.js → executor-DQazjeGA.js} +3 -3
  25. package/dist/{images-DZliDtSH.js → images-C3e5E-p_.js} +4 -21
  26. package/dist/instrumentation.js +2 -2
  27. package/dist/{job-queue-Cz3M7gg4.js → job-queue-BEUqRhjC.js} +1 -1
  28. package/dist/{logging-DVtMYOdD.js → logging-CZEc8_v4.js} +1 -1
  29. package/dist/message-BIJa7lKh.js +59 -0
  30. package/dist/nitro.d.ts +1 -1
  31. package/dist/nitro.js +3 -3
  32. package/dist/{oauth-authorization-By_MVRDC.js → oauth-authorization-BA4Dt6a7.js} +1 -45
  33. package/dist/{oauth-callback-server-BrUo6w1e.js → oauth-callback-server-R9OSdLP1.js} +2 -2
  34. package/dist/{pending-auth-BuQLZ6dK.js → pending-auth-Oo5AQhaf.js} +2 -2
  35. package/dist/{profile-BevMNSlr.js → profile-aVLDAirh.js} +4 -4
  36. package/dist/{prompt-C9Q9cjD3.js → prompt-ZG1UCxjF.js} +1 -1
  37. package/dist/{registry-C2rSEio1.js → registry-CtaDOOM5.js} +1 -1
  38. package/dist/{resolve-qPhrznTf.js → resolve-Bdw4hu7s.js} +2 -2
  39. package/dist/result-DNgmkASh.js +463 -0
  40. package/dist/{runner-CNh5eMWx.js → runner-BUSScEu5.js} +11 -10
  41. package/dist/{sandbox-egress-signals-BnDEKu8d.js → sandbox-egress-signals-E9r8bf9G.js} +1 -1
  42. package/dist/{schema-BWMRg8JS.js → schema-tyd6hALh.js} +2 -2
  43. package/dist/session-stop-CAhBfBUT.js +782 -0
  44. package/dist/{skills-C6zJbudq.js → skills-DQy-mmBC.js} +2 -2
  45. package/dist/{spawn-C3efZSx3.js → spawn-DQ_MVWZ6.js} +3 -2
  46. package/dist/store-DjLA8GKs.js +1417 -0
  47. package/dist/{task-queue-BX4vG6uA.js → task-queue-BT2h_t8I.js} +3 -3
  48. package/dist/{task-runner-T51kll0U.js → task-runner-CiPepjIB.js} +11 -11
  49. package/dist/{tasks-B9OIjl2K.js → tasks-DMmRrIVl.js} +9 -9
  50. package/dist/{thread-state-AGZtVyXp.js → thread-state-ehZYGpX9.js} +7 -7
  51. package/dist/{turn-failure-response-DJ8KU2bo.js → turn-failure-response-D7J10z7l.js} +9 -9
  52. package/dist/{turn-lifecycle-DZJkJ95q.js → turn-lifecycle-Buswqs90.js} +1 -1
  53. package/dist/{turn-session-routing-mRqLbMZW.js → turn-session-routing-Bw-yiSb4.js} +1 -1
  54. package/dist/{validation-Dg0Ihxzo.js → validation-DTguegIe.js} +1 -1
  55. package/dist/{version-Deub4try.js → version-BrZSE5du.js} +1 -1
  56. package/dist/version.js +1 -1
  57. package/dist/{viewer-D5hbx6TE.js → viewer-BlAozHer.js} +4 -4
  58. package/dist/{web-authorization-DCxixz5S.js → web-authorization-DwyTlkAg.js} +6 -6
  59. package/dist/{web-input-0sorYVLh.js → web-input-DSYxI7Wi.js} +43 -208
  60. package/dist/{work-rm2X7jj4.js → work-CRBXkJoU.js} +89 -13
  61. package/dist/{worker-bnGsAOP3.js → worker-BVJKM9Ja.js} +7 -7
  62. package/migrations/0053_conversation_stopped_at.sql +1 -0
  63. package/migrations/0054_flowery_rage.sql +1 -0
  64. package/migrations/meta/0053_snapshot.json +3771 -0
  65. package/migrations/meta/0054_snapshot.json +3777 -0
  66. package/migrations/meta/_journal.json +14 -0
  67. package/package.json +6 -5
  68. package/dist/message-BSN-Ma5f.js +0 -317
@@ -0,0 +1,463 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
+ import { $ as getAgentInvocation, X as createAgentInvocationFromLatest, ct as isTerminalAgentInvocation, et as getAgentInvocationIdForEvents, ft as markAgentInvocationParentMailboxAppended, it as getAgentInvocationResultMessageId, nt as getAgentInvocationIdForResult, st as getLatestAgentInvocationForConversation, tt as getAgentInvocationIdForMessage } from "./agent-runner-DsAFAKKs.js";
3
+ import { t as createActor } from "./actor-BTtLaeWy.js";
4
+ import { d as credentialContextForActor } from "./registry-CtaDOOM5.js";
5
+ import { $ as listTurnSummaries, Q as getTurnRecord } from "./tasks-DMmRrIVl.js";
6
+ import { a as EVENT_WAIT_MS, c as timerWatchCredentialSubject, s as isEventMailboxMetadata } from "./session-stop-CAhBfBUT.js";
7
+ import { a as EVENT_SYSTEM_ACTOR, i as EVENT_MESSAGE_AUTHOR, o as isEventConversationMessage } from "./prompt-ZG1UCxjF.js";
8
+ import { r as appendAndEnqueueInboundMessage } from "./store-DjLA8GKs.js";
9
+ import { r as enqueueAgentInvocation } from "./work-CRBXkJoU.js";
10
+ import { z } from "zod";
11
+ import { createEventSource, createLocalSource, createWebSource } from "@sentry/junior-plugin-api";
12
+ //#region src/chat/task-execution/mailbox-input.ts
13
+ /** Join non-empty mailbox Message text for one Turn. */
14
+ function joinMailboxText(messages) {
15
+ return messages.map((message) => message.input.text.trim()).filter(Boolean).join("\n\n");
16
+ }
17
+ //#endregion
18
+ //#region src/chat/conversations/web-mailbox.ts
19
+ /** Parse web mailbox metadata written before Source and Actor were complete. */
20
+ const legacyWebMailboxMetadataSchema = z.object({
21
+ authorEmail: z.string().email(),
22
+ authorFullName: z.string().min(1).optional(),
23
+ authorUserId: z.string().min(1),
24
+ authorUserName: z.string().min(1).optional(),
25
+ kind: z.literal("api_turn"),
26
+ messageId: z.string().min(1)
27
+ }).strict();
28
+ //#endregion
29
+ //#region src/chat/task-execution/mailbox-turn.ts
30
+ /**
31
+ * Restore Turn input facts, with saved Message fallback for legacy cursors.
32
+ *
33
+ * TODO(dcramer): Remove Actor and Source reconstruction from the saved Message
34
+ * after no deployed Turn cursor can omit either field.
35
+ */
36
+ function turnInputFactsFromConversationMessage(message, args) {
37
+ if (isEventConversationMessage(message)) return {
38
+ actor: args.savedActor ?? EVENT_SYSTEM_ACTOR,
39
+ author: message.author ?? EVENT_MESSAGE_AUTHOR,
40
+ source: args.savedSource ?? (args.location ? createWebSource(args.conversationId, args.visibility === "private" ? "private" : "public") : createLocalSource(args.conversationId))
41
+ };
42
+ const actor = args.savedActor ?? createActor(message.author, {
43
+ platform: "web",
44
+ userId: message.author?.userId
45
+ });
46
+ if (actor?.platform !== "web") return;
47
+ return {
48
+ actor,
49
+ author: message.author ?? { userId: actor.userId },
50
+ source: args.savedSource?.kind === "web" ? args.savedSource : createWebSource(args.conversationId, args.visibility === "private" ? "private" : "public")
51
+ };
52
+ }
53
+ /** Normalize the selected mailbox Source with current Conversation visibility. */
54
+ function sourceFromTurnInput(args) {
55
+ if (args.source.kind === "web") return createWebSource(args.conversationId, args.visibility === "private" ? "private" : "public");
56
+ return args.source;
57
+ }
58
+ function hasEventActor(actors) {
59
+ return Boolean(actors?.some((actor) => actor.platform === "system" && actor.name === EVENT_SYSTEM_ACTOR.name));
60
+ }
61
+ /** Return the active web Turn for one Conversation, if it has one. */
62
+ async function getActiveConversationTurnId(conversationId) {
63
+ const active = (await listTurnSummaries(conversationId)).filter((summary) => summary.surface === "api" && !summary.dispatchId && (summary.state === "paused" || summary.state === "running"));
64
+ if (active.length > 1) throw new Error(`Conversation ${conversationId} has multiple active web turns`);
65
+ const turnId = active[0]?.turnId;
66
+ if (!turnId) return void 0;
67
+ const record = await getTurnRecord(conversationId, turnId);
68
+ return record && (record.source?.kind === "web" || !record.source && record.surface === "api") && !record.dispatchId && (record.state === "paused" || record.state === "running") ? turnId : void 0;
69
+ }
70
+ /** Return the active web or event mailbox Turn. */
71
+ async function getActiveMailboxTurnId(conversationId) {
72
+ const summaries = await listTurnSummaries(conversationId);
73
+ const active = (await Promise.all(summaries.filter((summary) => !summary.dispatchId && (summary.state === "paused" || summary.state === "running")).map((summary) => getTurnRecord(conversationId, summary.turnId)))).filter((record) => record && !record.dispatchId && (record.source?.kind === "web" || record.source?.kind === "event" || !record.source && (record.surface === "api" || hasEventActor(record.actors))) && (record.state === "paused" || record.state === "running"));
74
+ if (active.length > 1) throw new Error(`Conversation ${conversationId} has multiple active mailbox Turns`);
75
+ return active[0]?.turnId;
76
+ }
77
+ /** Return the idle auth-paused web Turn for one Conversation. */
78
+ async function getAuthPausedConversationTurnId(conversationId) {
79
+ const turnId = await getActiveConversationTurnId(conversationId);
80
+ if (!turnId) return void 0;
81
+ const record = await getTurnRecord(conversationId, turnId);
82
+ return record?.state === "paused" && record.resumeReason === "auth" ? turnId : void 0;
83
+ }
84
+ function parseWebMessages(messages) {
85
+ if (messages.length === 0) return [];
86
+ const parsed = messages.map((message) => ({
87
+ message,
88
+ metadata: legacyWebMailboxMetadataSchema.safeParse(message.input.metadata)
89
+ }));
90
+ if (parsed.every((entry) => !entry.metadata.success)) return [];
91
+ if (parsed.some((entry) => !entry.metadata.success)) throw new Error("A Turn cannot combine different kinds of input");
92
+ return parsed.map((entry) => {
93
+ if (!entry.metadata.success) throw new Error("User message has invalid metadata");
94
+ const metadata = entry.metadata.data;
95
+ const actor = {
96
+ platform: "web",
97
+ userId: metadata.authorUserId,
98
+ email: metadata.authorEmail.trim().toLowerCase(),
99
+ ...metadata.authorFullName ? { fullName: metadata.authorFullName } : void 0,
100
+ ...metadata.authorUserName ? { userName: metadata.authorUserName } : void 0
101
+ };
102
+ return {
103
+ actor,
104
+ author: {
105
+ email: actor.email,
106
+ ...actor.fullName ? { fullName: actor.fullName } : void 0,
107
+ userId: actor.userId,
108
+ ...actor.userName ? { userName: actor.userName } : void 0
109
+ },
110
+ message: entry.message,
111
+ source: createWebSource(entry.message.conversationId)
112
+ };
113
+ });
114
+ }
115
+ /** Extra debounce time added per additional event already batched. */
116
+ const EVENT_WAIT_PER_EXTRA_MESSAGE_MS = 5e3;
117
+ /** Upper bound on the debounce window regardless of batch size. */
118
+ const EVENT_MAX_WAIT_MS = 6e4;
119
+ /**
120
+ * Return how long an event batch must wait before its Turn starts.
121
+ *
122
+ * A resource-event burst (e.g. several check runs on one PR) should produce
123
+ * one Turn instead of one per event. The window grows with batch size up to
124
+ * EVENT_MAX_WAIT_MS, so later events in the same burst land in this batch.
125
+ * Returns 0 when the batch can run now or does not start with an event.
126
+ */
127
+ function getEventBatchDelayMs(messages, nowMs = Date.now()) {
128
+ const first = messages[0];
129
+ if (!first || !isEventMailboxMetadata(first.input.metadata)) return 0;
130
+ const waitMs = Math.min(EVENT_MAX_WAIT_MS, EVENT_WAIT_MS + EVENT_WAIT_PER_EXTRA_MESSAGE_MS * (messages.length - 1));
131
+ return Math.max(0, first.receivedAtMs + waitMs - nowMs);
132
+ }
133
+ /** Read an event batch as Turn input, or return [] when it is not only events. */
134
+ function parseEventMessages(messages) {
135
+ if (messages.length === 0) return [];
136
+ if (!messages.every((message) => message.source === "event" && isEventMailboxMetadata(message.input.metadata))) return [];
137
+ return messages.map((message) => {
138
+ if (!isEventMailboxMetadata(message.input.metadata)) throw new Error("Event has invalid metadata");
139
+ return {
140
+ actor: EVENT_SYSTEM_ACTOR,
141
+ author: EVENT_MESSAGE_AUTHOR,
142
+ message,
143
+ source: createEventSource({
144
+ eventKey: message.input.metadata.event.eventKey,
145
+ eventType: message.input.metadata.event.eventType,
146
+ identifier: message.input.metadata.event.identifier,
147
+ namespace: message.input.metadata.event.namespace
148
+ })
149
+ };
150
+ });
151
+ }
152
+ /**
153
+ * Select web or event work from new mailbox input or a saved Turn.
154
+ * Saved Actor data identifies a resumed event Turn.
155
+ */
156
+ async function resolveMailboxTurnWork(context) {
157
+ const batch = parseWebMessages(context.attempt.messages);
158
+ if (batch.length > 0) return {
159
+ kind: "mailbox",
160
+ batch
161
+ };
162
+ const resourceBatch = parseEventMessages(context.attempt.messages);
163
+ if (resourceBatch.length > 0) return {
164
+ kind: "mailbox",
165
+ batch: resourceBatch
166
+ };
167
+ if (context.attempt.messages.length > 0) return;
168
+ const turnId = await getActiveMailboxTurnId(context.conversationId);
169
+ return turnId ? {
170
+ kind: "resume",
171
+ turnId
172
+ } : void 0;
173
+ }
174
+ //#endregion
175
+ //#region src/chat/agent-invocations/result.ts
176
+ var result_exports = /* @__PURE__ */ __exportAll({
177
+ enqueueAgentInvocationResult: () => enqueueAgentInvocationResult,
178
+ enqueueAgentMessageToParent: () => enqueueAgentMessageToParent,
179
+ startAgentInvocationsForChildInput: () => startAgentInvocationsForChildInput,
180
+ withAgentInvocationResultActors: () => withAgentInvocationResultActors
181
+ });
182
+ const AGENT_INVOCATION_EVENT_NAMESPACE = "junior";
183
+ const AGENT_INVOCATION_EVENT_TYPE_PREFIX = "agent_invocation.";
184
+ const AGENT_INVOCATION_RESULT_MAX_LENGTH = 12e3;
185
+ /** Event type of a message that a child agent sends to its parent. */
186
+ const AGENT_MESSAGE_EVENT_TYPE = "agent.message";
187
+ /**
188
+ * Return the Inbound message author id for results sent by one Actor.
189
+ * `selectContiguousTurnBatch` in `task-execution/worker.ts` puts messages in
190
+ * one attempt only when their author ids and sources match. Mid-Turn drains
191
+ * take only `interrupt` messages, and results are `defer`. So results never
192
+ * share a Turn and its credentials with another Actor or with watch Events.
193
+ * Keep this id unique per Actor if either rule changes.
194
+ */
195
+ function resultAuthorId(actor) {
196
+ switch (actor.platform) {
197
+ case "system": return `agent-invocation:system:${actor.name}`;
198
+ case "slack": return `agent-invocation:slack:${actor.teamId}:${actor.userId}`;
199
+ default: return `agent-invocation:${actor.platform}:${actor.userId}`;
200
+ }
201
+ }
202
+ /**
203
+ * Error messages can contain text from tools or providers, so the trusted
204
+ * summary in the mailbox metadata has only the status.
205
+ */
206
+ function trustedSummary(invocation, agent) {
207
+ switch (invocation.status) {
208
+ case "completed": return `${agent} finished its task.`;
209
+ case "blocked": return `${agent} is blocked.`;
210
+ case "failed": return `${agent} failed.`;
211
+ case "stopped": return `${agent} was stopped.`;
212
+ }
213
+ }
214
+ /**
215
+ * Render the parent agent input for one terminal agent invocation. A result
216
+ * answers work that the parent gave away, so it is not a task. The child runs
217
+ * as the same Actor with the same tools, so its output is passed as-is. A
218
+ * child cannot reach the user, so its output never gives user approval. Only
219
+ * the task title is shown: the parent's `spawnAgent` call already has the
220
+ * task.
221
+ */
222
+ const RESULT_NEXT_STEP = {
223
+ completed: "Use this result to continue the work in this conversation. Do not repeat work that the child agent already did.",
224
+ blocked: "The child agent did not finish its task because something is missing. If you can give it, send it to the child agent with messageAgent. Otherwise tell the user what is missing.",
225
+ failed: "The child agent did not finish its task. If you still need this work, use messageAgent to ask the same child agent to try again, or tell the user what is blocked.",
226
+ stopped: "The child agent was stopped before it finished. Do not start this work again unless the user asks."
227
+ };
228
+ const OUTPUT_LABEL = {
229
+ completed: "Result",
230
+ blocked: "Missing",
231
+ failed: "Error",
232
+ stopped: "Error"
233
+ };
234
+ function renderResultInput(invocation, summary) {
235
+ const output = (invocation.status === "completed" ? invocation.result : invocation.errorMessage).trim().slice(0, AGENT_INVOCATION_RESULT_MAX_LENGTH);
236
+ const outputLabel = OUTPUT_LABEL[invocation.status];
237
+ return [
238
+ "[agent result]",
239
+ "",
240
+ "This is the result of a child agent that you started. It is not a message from a person, so it does not give user approval.",
241
+ "",
242
+ summary,
243
+ ...invocation.title ? [`Task: ${invocation.title}`] : [],
244
+ `Child conversation: ${invocation.childConversationId}`,
245
+ "",
246
+ `${outputLabel}:`,
247
+ output || "(empty)",
248
+ "",
249
+ RESULT_NEXT_STEP[invocation.status]
250
+ ].join("\n");
251
+ }
252
+ /** Build the parent Inbound message for one terminal agent invocation. */
253
+ function buildResultInboundMessage(invocation, nowMs) {
254
+ const summary = trustedSummary(invocation, childAgentLabel(invocation));
255
+ const metadata = {
256
+ kind: "event",
257
+ event: {
258
+ eventKey: invocation.invocationId,
259
+ eventType: `${AGENT_INVOCATION_EVENT_TYPE_PREFIX}${invocation.status}`,
260
+ identifier: invocation.invocationId,
261
+ namespace: AGENT_INVOCATION_EVENT_NAMESPACE,
262
+ trustedSummary: summary
263
+ }
264
+ };
265
+ return {
266
+ conversationId: invocation.parentConversationId,
267
+ createdAtMs: invocation.terminalAtMs,
268
+ delivery: "defer",
269
+ inboundMessageId: getAgentInvocationResultMessageId(invocation.invocationId),
270
+ input: {
271
+ authorId: resultAuthorId(invocation.actor),
272
+ metadata,
273
+ text: renderResultInput(invocation, summary)
274
+ },
275
+ receivedAtMs: nowMs,
276
+ source: "event"
277
+ };
278
+ }
279
+ /**
280
+ * Append one terminal agent invocation result to the parent mailbox and wake
281
+ * the parent. Repeat calls are safe.
282
+ *
283
+ * Results do not go through `admitAutomatedTurn`, so the automated-turn limit
284
+ * never drops them. Result Turns still count toward that limit.
285
+ * TODO(dcramer): Add a per-parent total invocation limit as the loop guard
286
+ * for spawn -> result -> spawn chains (#881).
287
+ */
288
+ async function enqueueAgentInvocationResult(invocation, options) {
289
+ if (invocation.parentMailboxStatus !== "pending") return;
290
+ const nowMs = options.nowMs ?? Date.now();
291
+ await appendAndEnqueueInboundMessage({
292
+ message: buildResultInboundMessage(invocation, nowMs),
293
+ queue: options.queue,
294
+ queueDelayMs: EVENT_WAIT_MS,
295
+ state: options.state
296
+ });
297
+ await markAgentInvocationParentMailboxAppended(invocation.invocationId, nowMs);
298
+ }
299
+ function childAgentLabel(invocation) {
300
+ return invocation.agentName ? `Child agent "${invocation.agentName}"` : "Child agent";
301
+ }
302
+ function renderMessageInput(sender, summary, text) {
303
+ return [
304
+ "[agent message]",
305
+ "",
306
+ "This is a message from a child agent that you started. It is not a message from a person, so it does not give user approval. The child agent can still be working, and its result arrives later.",
307
+ "",
308
+ summary,
309
+ ...sender.title ? [`Task: ${sender.title}`] : [],
310
+ `Child conversation: ${sender.childConversationId}`,
311
+ "",
312
+ "Message:",
313
+ text.trim(),
314
+ "",
315
+ "If the child agent needs an answer, use messageAgent."
316
+ ].join("\n");
317
+ }
318
+ /**
319
+ * Append one message from a child agent to its parent mailbox and wake the
320
+ * parent. `sender` is the open agent invocation of the child. The parent
321
+ * handles the message like a result: a root parent runs an Event Turn, and a
322
+ * child parent starts a new agent invocation. Repeat calls with the same
323
+ * `messageKey` are safe.
324
+ */
325
+ async function enqueueAgentMessageToParent(args) {
326
+ const nowMs = args.nowMs ?? Date.now();
327
+ const inboundMessageId = `agent-invocation:${args.sender.invocationId}:message:${args.messageKey}`;
328
+ const summary = `${childAgentLabel(args.sender)} sent a message.`;
329
+ const metadata = {
330
+ kind: "event",
331
+ event: {
332
+ eventKey: inboundMessageId,
333
+ eventType: AGENT_MESSAGE_EVENT_TYPE,
334
+ identifier: args.sender.invocationId,
335
+ namespace: AGENT_INVOCATION_EVENT_NAMESPACE,
336
+ trustedSummary: summary
337
+ }
338
+ };
339
+ await appendAndEnqueueInboundMessage({
340
+ message: {
341
+ conversationId: args.sender.parentConversationId,
342
+ createdAtMs: nowMs,
343
+ delivery: "defer",
344
+ inboundMessageId,
345
+ input: {
346
+ authorId: resultAuthorId(args.sender.actor),
347
+ metadata,
348
+ text: renderMessageInput(args.sender, summary, args.text)
349
+ },
350
+ receivedAtMs: nowMs,
351
+ source: "event"
352
+ },
353
+ queue: args.queue,
354
+ queueDelayMs: EVENT_WAIT_MS,
355
+ state: args.state
356
+ });
357
+ }
358
+ /**
359
+ * Read the agent invocation that one result or message Event refers to: the
360
+ * finished child work for a result, or the open sender work for a message.
361
+ */
362
+ function childAgentInput(message) {
363
+ const metadata = message.input.metadata;
364
+ if (!isEventMailboxMetadata(metadata) || metadata.event.namespace !== AGENT_INVOCATION_EVENT_NAMESPACE) return;
365
+ if (metadata.event.eventType === AGENT_MESSAGE_EVENT_TYPE) return {
366
+ invocationId: metadata.event.identifier,
367
+ kind: "message"
368
+ };
369
+ return metadata.event.eventType.startsWith(AGENT_INVOCATION_EVENT_TYPE_PREFIX) ? {
370
+ invocationId: metadata.event.identifier,
371
+ kind: "result"
372
+ } : void 0;
373
+ }
374
+ /**
375
+ * Read the agent invocation of one result or message and check that it
376
+ * belongs to a child of the Conversation that received it.
377
+ */
378
+ async function getChildAgentInputInvocation(message, input) {
379
+ const invocation = await getAgentInvocation(input.invocationId);
380
+ if (!invocation || input.kind === "result" && !isTerminalAgentInvocation(invocation) || invocation.parentConversationId !== message.conversationId) throw new Error(`Agent invocation ${input.kind} ${input.invocationId} does not belong to ${message.conversationId}`);
381
+ return invocation;
382
+ }
383
+ /**
384
+ * Give mailbox input that reaches a child Conversation to that child: results
385
+ * and messages from its own child agents, and its own watch events. A child
386
+ * Conversation has no Location, so it cannot run an Event Turn. Each input
387
+ * starts a new agent invocation on the child instead, and that invocation
388
+ * reports to the child's parent. Returns undefined for work that is not
389
+ * mailbox input for a child Conversation.
390
+ */
391
+ async function startAgentInvocationsForChildInput(context, options) {
392
+ const messages = context.attempt.messages;
393
+ const inputs = messages.map(childAgentInput);
394
+ const isAgentInput = inputs.every((input) => input !== void 0);
395
+ const events = isAgentInput ? [] : parseEventMessages(messages);
396
+ if (messages.length === 0 || !isAgentInput && events.length === 0) return;
397
+ const latest = await getLatestAgentInvocationForConversation(context.conversationId);
398
+ if (!latest) return;
399
+ if (latest.status === "stopped" || isTerminalAgentInvocation(latest) && latest.parentMailboxStatus === "skipped") {
400
+ await context.attempt.ack();
401
+ return { status: "completed" };
402
+ }
403
+ if (!isAgentInput) {
404
+ const delayMs = getEventBatchDelayMs(messages);
405
+ if (delayMs > 0) return {
406
+ status: "deferred",
407
+ delayMs
408
+ };
409
+ const { actor, source } = events[0];
410
+ const invocation = await createAgentInvocationFromLatest({
411
+ input: joinMailboxText(messages),
412
+ invocationId: getAgentInvocationIdForEvents(messages[0].inboundMessageId),
413
+ latest,
414
+ runAs: {
415
+ actor,
416
+ credentialContext: credentialContextForActor(actor, timerWatchCredentialSubject(context.conversationId, messages)),
417
+ source
418
+ }
419
+ });
420
+ await enqueueAgentInvocation(invocation, options);
421
+ await context.attempt.ack();
422
+ return { status: "completed" };
423
+ }
424
+ for (const [index, message] of messages.entries()) {
425
+ const input = inputs[index];
426
+ const source = await getChildAgentInputInvocation(message, input);
427
+ if (input.kind === "result" && isTerminalAgentInvocation(source) && source.parentMailboxStatus === "skipped") continue;
428
+ const invocation = await createAgentInvocationFromLatest({
429
+ input: message.input.text.trim(),
430
+ invocationId: input.kind === "result" ? getAgentInvocationIdForResult(source.invocationId) : getAgentInvocationIdForMessage(message.inboundMessageId),
431
+ latest
432
+ });
433
+ await enqueueAgentInvocation(invocation, options);
434
+ }
435
+ await context.attempt.ack();
436
+ return { status: "completed" };
437
+ }
438
+ /**
439
+ * Run each agent invocation result or message as the Actor that triggered the
440
+ * child, user or system. This is on purpose: the result continues that
441
+ * Actor's work. The Actor comes from SQL, not from mailbox metadata, so the
442
+ * parent Turn has the same authority it had at spawn time.
443
+ *
444
+ * TODO(dcramer): Pass the delegated credential subject of a system Actor, such
445
+ * as a timer watch Turn, when system Turns can spawn children that need it.
446
+ */
447
+ async function withAgentInvocationResultActors(work) {
448
+ if (work.kind !== "mailbox") return work;
449
+ return {
450
+ kind: "mailbox",
451
+ batch: await Promise.all(work.batch.map(async (entry) => {
452
+ const input = childAgentInput(entry.message);
453
+ if (!input) return entry;
454
+ const invocation = await getChildAgentInputInvocation(entry.message, input);
455
+ return {
456
+ ...entry,
457
+ actor: invocation.actor
458
+ };
459
+ }))
460
+ };
461
+ }
462
+ //#endregion
463
+ export { withAgentInvocationResultActors as a, resolveMailboxTurnWork as c, legacyWebMailboxMetadataSchema as d, joinMailboxText as f, startAgentInvocationsForChildInput as i, sourceFromTurnInput as l, enqueueAgentMessageToParent as n, getAuthPausedConversationTurnId as o, result_exports as r, getEventBatchDelayMs as s, enqueueAgentInvocationResult as t, turnInputFactsFromConversationMessage as u };
@@ -1,13 +1,14 @@
1
- import { C as withLogContext, b as setTags, d as logException } from "./logging-DVtMYOdD.js";
2
- import { c as getConversationEventStore, u as getConversationStore } from "./viewer-D5hbx6TE.js";
3
- import { d as credentialContextForActor } from "./registry-C2rSEio1.js";
4
- import { Dt as loadProjection, Ht as hydrateConversationMessages, J as saveTurnCheckpoint, Pt as recordAuthorizationCompleted, St as commitAcceptedReply, Ut as persistConversationMessages, Zt as buildConversationContext, in as recordDeliveredAssistantMessage, nn as markConversationMessage, on as upsertConversationMessage, rn as normalizeConversationText, tt as persistWithRetry } from "./tasks-B9OIjl2K.js";
5
- import { W as messageCardText } from "./conversation-CvBG57Io.js";
6
- import { _ as markTurnFailed, r as getAssistantReplyText, t as authorizationId, v as startActiveTurn } from "./oauth-authorization-By_MVRDC.js";
7
- import { n as scheduleSessionCompletedPluginTasks, t as processPluginTask } from "./task-runner-T51kll0U.js";
8
- import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-DZJkJ95q.js";
9
- import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-AGZtVyXp.js";
10
- import { d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, t as finalizeFailedTurnReplyWithEvent } from "./turn-failure-response-DJ8KU2bo.js";
1
+ import { C as withLogContext, b as setTags, d as logException } from "./logging-CZEc8_v4.js";
2
+ import { c as getConversationEventStore, u as getConversationStore } from "./viewer-BlAozHer.js";
3
+ import { d as credentialContextForActor } from "./registry-CtaDOOM5.js";
4
+ import { Dt as loadProjection, Ht as hydrateConversationMessages, J as saveTurnCheckpoint, Pt as recordAuthorizationCompleted, St as commitAcceptedReply, Ut as persistConversationMessages, Zt as buildConversationContext, in as recordDeliveredAssistantMessage, nn as markConversationMessage, on as upsertConversationMessage, rn as normalizeConversationText, tt as persistWithRetry } from "./tasks-DMmRrIVl.js";
5
+ import { G as messageCardText } from "./conversation-CYsbVFLx.js";
6
+ import { f as markTurnFailed, p as startActiveTurn, t as authorizationId } from "./oauth-authorization-BA4Dt6a7.js";
7
+ import { n as getAssistantReplyText } from "./assistant-reply-ChOzcTBS.js";
8
+ import { n as scheduleSessionCompletedPluginTasks, t as processPluginTask } from "./task-runner-CiPepjIB.js";
9
+ import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-Buswqs90.js";
10
+ import { n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-ehZYGpX9.js";
11
+ import { d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, t as finalizeFailedTurnReplyWithEvent } from "./turn-failure-response-D7J10z7l.js";
11
12
  import { randomUUID } from "node:crypto";
12
13
  import { createLocalSource, localDestinationSchema } from "@sentry/junior-plugin-api";
13
14
  //#region src/chat/local/runner.ts
@@ -1,4 +1,4 @@
1
- import { _n as sandboxEgressSignalsResponseSchema } from "./agent-runner-DSKpHjHW.js";
1
+ import { At as sandboxEgressSignalsResponseSchema } from "./agent-runner-DsAFAKKs.js";
2
2
  //#region src/chat/local/sandbox-egress-signals.ts
3
3
  const SANDBOX_EGRESS_SIGNALS_PATH = "/api/internal/sandbox-egress/signals";
4
4
  const LOCAL_DEV_SERVER_TIMEOUT_MS = 1e3;
@@ -1,5 +1,5 @@
1
- import { r as AGENT_INVOCATION_STATUSES, u as juniorDestinationKindSchema } from "./agent-invocations-BO6ddZax.js";
2
- import { D as conversationUsageSchema, E as conversationSummaryReportSchema, l as conversationAuxiliaryCostsSchema, n as actorIdentitySchema, w as conversationStatsItemSchema } from "./conversation-CvBG57Io.js";
1
+ import { r as AGENT_INVOCATION_STATUSES, u as juniorDestinationKindSchema } from "./agent-invocations-D_YoNnAn.js";
2
+ import { D as conversationUsageSchema, E as conversationSummaryReportSchema, l as conversationAuxiliaryCostsSchema, n as actorIdentitySchema, w as conversationStatsItemSchema } from "./conversation-CYsbVFLx.js";
3
3
  import "./automation-gznOMaQ2.js";
4
4
  import { a as fillUtcHours, c as sumUtcHoursIntoSixHours, i as fillUtcDays } from "./web-message-id-xPI_EFSC.js";
5
5
  import { z } from "zod";