@sentry/junior 0.203.0 → 0.205.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 (174) hide show
  1. package/dist/agent-D4nu8MQV.js +17521 -0
  2. package/dist/agent-hooks-DXHpTJ4m.js +4042 -0
  3. package/dist/agent-runner-C04colea.js +678 -0
  4. package/dist/api/schema.d.ts +2736 -2639
  5. package/dist/api/schema.js +4 -169
  6. package/dist/api.d.ts +23 -17
  7. package/dist/api.js +3881 -5326
  8. package/dist/app-Cb776lL8.d.ts +247 -0
  9. package/dist/app.d.ts +4 -520
  10. package/dist/app.js +9524 -11875
  11. package/dist/auth-pause-state-C2P3guS1.js +168 -0
  12. package/dist/catalog-runtime-CXw3asJD.js +7 -0
  13. package/dist/checkpoint-CrViJ8Ps.js +2450 -0
  14. package/dist/cli/chat.d.ts +20 -0
  15. package/dist/cli/chat.js +229 -336
  16. package/dist/cli/check.d.ts +8 -0
  17. package/dist/cli/check.js +513 -806
  18. package/dist/cli/env.d.ts +13 -0
  19. package/dist/cli/env.js +63 -12
  20. package/dist/cli/init.d.ts +3 -0
  21. package/dist/cli/init.js +90 -159
  22. package/dist/cli/main.d.ts +6 -0
  23. package/dist/cli/main.js +43 -64
  24. package/dist/cli/plugins.d.ts +13 -0
  25. package/dist/cli/plugins.js +148 -227
  26. package/dist/cli/run.d.ts +16 -0
  27. package/dist/cli/run.js +52 -8
  28. package/dist/cli/snapshot-create.d.ts +4 -0
  29. package/dist/cli/snapshot-create.js +71 -125
  30. package/dist/cli/upgrade.d.ts +15 -0
  31. package/dist/cli/upgrade.js +232 -335
  32. package/dist/config-BOtJQ7Pc.js +941 -0
  33. package/dist/context-B45_WqVX.js +298 -0
  34. package/dist/conversation-privacy-BZRA6Pes.js +182 -0
  35. package/dist/conversation-work-C6y3T5Hx.js +51 -0
  36. package/dist/credential-sync-Bep34IB_.js +2661 -0
  37. package/dist/db-BeyXKJOI.d.ts +5 -0
  38. package/dist/destinations-BeBiJeEM.js +29 -0
  39. package/dist/execution-stats-B6lgWxCK.js +373 -0
  40. package/dist/executor-BlUhFL7_.js +721 -0
  41. package/dist/experimental-CIXf7Uye.js +46 -0
  42. package/dist/history-ClE4toxe.d.ts +32 -0
  43. package/dist/history-CqTuSAdI.js +441 -0
  44. package/dist/instrumentation.d.ts +3 -3
  45. package/dist/instrumentation.js +130 -176
  46. package/dist/job-queue-DLMqIK74.js +63 -0
  47. package/dist/logging-BMJzdDlP.js +1210 -0
  48. package/dist/model-profile-CZmVoGdX.js +49 -0
  49. package/dist/nitro.d.ts +31 -36
  50. package/dist/nitro.js +275 -456
  51. package/dist/oauth-callback-server-WwG5_Zgm.js +130 -0
  52. package/dist/oauth-relay-CdCnu80W.js +62 -0
  53. package/dist/package-discovery-7a9YqLhE.js +455 -0
  54. package/dist/pending-auth-CUbXq06G.js +185 -0
  55. package/dist/plugin-module-BKGOXca2.js +81 -0
  56. package/dist/plugins-BI9A1vKA.d.ts +95 -0
  57. package/dist/plugins-egKWjgue.js +99 -0
  58. package/dist/private-trace-result-BLvBjy_e.js +16 -0
  59. package/dist/profile-T0tXE4zu.js +861 -0
  60. package/dist/registry-BA0__8ai.js +1419 -0
  61. package/dist/reporting-schema-DosTN__Q.js +105 -0
  62. package/dist/resolve-bvD1ORdv.js +546 -0
  63. package/dist/rolldown-runtime-B4FMCO8f.js +28 -0
  64. package/dist/runner-BXYxfIUL.js +392 -0
  65. package/dist/sandbox-egress-signals-ngEPjSEW.js +46 -0
  66. package/dist/schema-Rn8-XEaN.js +1140 -0
  67. package/dist/sentry-CD3SdJl-.js +26 -0
  68. package/dist/skills-DJOlYdBs.js +316 -0
  69. package/dist/spawn-h0R1bTEo.js +29 -0
  70. package/dist/stats-3YZn-Swi.js +2648 -0
  71. package/dist/store-C5qxF2ac.d.ts +208 -0
  72. package/dist/task-queue-CoXwd2eq.js +136 -0
  73. package/dist/task-runner-B2_mJZj0.js +639 -0
  74. package/dist/thread-state-CqZ1m-es.js +431 -0
  75. package/dist/turn-execution-fI6PZ0iI.js +47 -0
  76. package/dist/turn-lifecycle-CcnwrOcp.js +54 -0
  77. package/dist/usage-B5SfMb3H.js +296 -0
  78. package/dist/usage-schema-CKaDkH_j.js +20 -0
  79. package/dist/validation-DfWcvhuS.js +40 -0
  80. package/dist/vercel-queue-DIkVcfK4.js +167 -0
  81. package/dist/vercel.d.ts +6 -6
  82. package/dist/vercel.js +14 -6
  83. package/dist/version-CnXZ614E.d.ts +5 -0
  84. package/dist/version-oD-oPrbO.js +8 -0
  85. package/dist/version.d.ts +2 -4
  86. package/dist/version.js +2 -6
  87. package/dist/web-authorization-Crbzlmzo.js +76 -0
  88. package/dist/web-input-BOuq3oKK.js +494 -0
  89. package/dist/work-B91LQKHr.js +509 -0
  90. package/dist/worker-BcRRTe6k.js +625 -0
  91. package/package.json +9 -9
  92. package/dist/agent-hooks-GB2VPTNA.js +0 -51
  93. package/dist/catalog-runtime-UBLIWD3O.js +0 -14
  94. package/dist/chunk-2LYHWMR4.js +0 -46
  95. package/dist/chunk-352OWAZH.js +0 -320
  96. package/dist/chunk-3Y47VMHV.js +0 -6
  97. package/dist/chunk-3YJSKOWF.js +0 -164
  98. package/dist/chunk-4KSSKTQ5.js +0 -229
  99. package/dist/chunk-4UO6FK4G.js +0 -64
  100. package/dist/chunk-5AHX53NQ.js +0 -717
  101. package/dist/chunk-5J3E5FWA.js +0 -700
  102. package/dist/chunk-5PDAG6O3.js +0 -206
  103. package/dist/chunk-6Y5B5PEX.js +0 -80
  104. package/dist/chunk-7GWQ5S5R.js +0 -2086
  105. package/dist/chunk-7H2VZTWN.js +0 -103
  106. package/dist/chunk-7NL7T6VU.js +0 -406
  107. package/dist/chunk-A5L27LE4.js +0 -387
  108. package/dist/chunk-ADIAWL34.js +0 -81
  109. package/dist/chunk-ASTEMBKJ.js +0 -519
  110. package/dist/chunk-CEMMLV5L.js +0 -98
  111. package/dist/chunk-CM5HNV2W.js +0 -131
  112. package/dist/chunk-CRTRBBRI.js +0 -105
  113. package/dist/chunk-DA2KGZZI.js +0 -13
  114. package/dist/chunk-DIB4GX3G.js +0 -8
  115. package/dist/chunk-DKIJ2RP5.js +0 -68
  116. package/dist/chunk-DWPN6SCH.js +0 -172
  117. package/dist/chunk-EDKUFGPL.js +0 -64
  118. package/dist/chunk-ENY6SKIU.js +0 -1228
  119. package/dist/chunk-F4MGCZOX.js +0 -2943
  120. package/dist/chunk-FA6POGLH.js +0 -70
  121. package/dist/chunk-FSUR7GTG.js +0 -20475
  122. package/dist/chunk-GKEOHJL5.js +0 -1856
  123. package/dist/chunk-GMCB6W5Y.js +0 -333
  124. package/dist/chunk-IE2VPG7O.js +0 -1135
  125. package/dist/chunk-IZ5B5IHQ.js +0 -3656
  126. package/dist/chunk-JAL3CH4G.js +0 -27
  127. package/dist/chunk-KIIEUXD3.js +0 -1764
  128. package/dist/chunk-KXU4NIL7.js +0 -692
  129. package/dist/chunk-MTM5IFCY.js +0 -1190
  130. package/dist/chunk-N7TMSOZH.js +0 -235
  131. package/dist/chunk-NCEBK6PH.js +0 -36
  132. package/dist/chunk-NHJU2ZAL.js +0 -56
  133. package/dist/chunk-P6KTRNVC.js +0 -321
  134. package/dist/chunk-PDO5BLNM.js +0 -22
  135. package/dist/chunk-PDRNLLFT.js +0 -3295
  136. package/dist/chunk-PEQXYSSE.js +0 -45
  137. package/dist/chunk-PKEX2AQX.js +0 -198
  138. package/dist/chunk-PSJCGIGO.js +0 -1163
  139. package/dist/chunk-PZUSUL55.js +0 -506
  140. package/dist/chunk-R45M4TUS.js +0 -37
  141. package/dist/chunk-RC745GGZ.js +0 -122
  142. package/dist/chunk-SP3QKZXJ.js +0 -820
  143. package/dist/chunk-TXKUKMVO.js +0 -417
  144. package/dist/chunk-UE4GPWSD.js +0 -931
  145. package/dist/chunk-UIE3R5XU.js +0 -17
  146. package/dist/chunk-UXPG6ZIN.js +0 -84
  147. package/dist/chunk-VFUK3X5B.js +0 -50
  148. package/dist/chunk-VNMJWREK.js +0 -787
  149. package/dist/chunk-VZ7CSOX2.js +0 -706
  150. package/dist/chunk-WCNQJKFT.js +0 -10
  151. package/dist/chunk-WDZR3QQM.js +0 -135
  152. package/dist/chunk-WLYVJCHV.js +0 -1433
  153. package/dist/chunk-WS7NAFTM.js +0 -1520
  154. package/dist/chunk-X6GDDWQH.js +0 -60
  155. package/dist/chunk-XPDEYNQI.js +0 -102
  156. package/dist/chunk-XRW44VGM.js +0 -58
  157. package/dist/chunk-Y2CM7HXH.js +0 -111
  158. package/dist/chunk-Y7X25LFY.js +0 -15
  159. package/dist/conversation-work-VIOQ5ZF3.js +0 -54
  160. package/dist/db-NGDJV7YH.js +0 -26
  161. package/dist/discovery-DWVDM7UK.js +0 -44
  162. package/dist/experimental-P5FZM4CU.js +0 -12
  163. package/dist/oauth-callback-server-YFY7YEGK.js +0 -243
  164. package/dist/oauth-relay-5XPVRPYB.js +0 -8
  165. package/dist/plugins-YTYJMMJF.js +0 -14
  166. package/dist/runner-ORTNSB2Q.js +0 -522
  167. package/dist/sandbox-egress-signals-FIL7EGPR.js +0 -73
  168. package/dist/sentry-MB3NL3IR.js +0 -42
  169. package/dist/skills-H376WQOH.js +0 -27
  170. package/dist/spawn-T43WHLOS.js +0 -42
  171. package/dist/task-runner-LBZWF32V.js +0 -36
  172. package/dist/validation-REJLRKI6.js +0 -17
  173. package/dist/work-3KJ3RE76.js +0 -51
  174. package/dist/worker-QFEBU72E.js +0 -24
@@ -0,0 +1,639 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
+ import { _ as parseSlackMessageTs, r as credentialSubjectSchema } from "./context-B45_WqVX.js";
3
+ import { Et as getDb, Tt as getConversationStore, Y as readActorIdentity, _t as createPluginLogger, gt as createPluginModel, ht as createPluginEmbedder, mt as createPluginState, u as getPlugins, ut as JUNIOR_THREAD_STATE_TTL_MS, vt as createPluginConversationEvents } from "./agent-hooks-DXHpTJ4m.js";
4
+ import { f as sameActorIdentity } from "./history-CqTuSAdI.js";
5
+ import { Dt as isToolResultError, Ot as isToolResultMessage, Q as hydrateConversationMessages, bt as getPiMessageRole, jt as stripRuntimeTurnContext, kt as normalizeToolNameFromResult, s as getTurnRecord, wt as instructionTextForProjection } from "./checkpoint-CrViJ8Ps.js";
6
+ import { w as getStateAdapter } from "./profile-T0tXE4zu.js";
7
+ import { u as recordTaskExecution } from "./execution-stats-B6lgWxCK.js";
8
+ import { r as getPersistedThreadState, s as coerceThreadConversationState } from "./thread-state-CqZ1m-es.js";
9
+ import { i as pluginTaskParamsSchema, o as sendVercelPluginTask, r as pluginTaskId } from "./task-queue-CoXwd2eq.js";
10
+ import { z } from "zod";
11
+ import { createHash } from "node:crypto";
12
+ import { destinationSchema, destinationVisibilitySchema, isSlackDestination, pluginRunContextSchema, replyAttributionSchema, sourceSchema, taskOutcomeSchema } from "@sentry/junior-plugin-api";
13
+ //#region src/chat/agent-dispatch/store.ts
14
+ const DISPATCH_PREFIX = "junior:agent_dispatch";
15
+ const DISPATCH_LOCK_TTL_MS = 6e5;
16
+ const DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS = 1e4;
17
+ const nonEmptyExactStringSchema = z.string().min(1).refine((value) => value === value.trim() && value.toLowerCase() !== "unknown");
18
+ const pendingDispatchMailboxAppendIndexSchema = z.array(nonEmptyExactStringSchema);
19
+ const dispatchStatusSchema = z.enum([
20
+ "pending",
21
+ "running",
22
+ "awaiting_resume",
23
+ "completed",
24
+ "failed",
25
+ "blocked"
26
+ ]);
27
+ const dispatchActorSchema = z.object({
28
+ platform: z.literal("system"),
29
+ name: nonEmptyExactStringSchema
30
+ }).strict();
31
+ const dispatchRecordSchema = z.object({
32
+ actor: dispatchActorSchema,
33
+ createdAtMs: z.number().finite(),
34
+ credentialSubject: credentialSubjectSchema.optional(),
35
+ destination: destinationSchema,
36
+ destinationVisibility: destinationVisibilitySchema,
37
+ errorMessage: z.string().optional(),
38
+ id: nonEmptyExactStringSchema,
39
+ idempotencyKey: z.string().min(1),
40
+ input: z.string().min(1),
41
+ metadata: z.record(z.string(), z.string()).optional(),
42
+ plugin: nonEmptyExactStringSchema,
43
+ replyAttribution: replyAttributionSchema.optional(),
44
+ resultMessageTs: z.string().optional(),
45
+ source: sourceSchema,
46
+ outcomes: z.array(taskOutcomeSchema).max(5).optional(),
47
+ status: dispatchStatusSchema,
48
+ updatedAtMs: z.number().finite()
49
+ }).strict().superRefine((record, ctx) => {
50
+ if (!isSlackDestination(record.destination)) {
51
+ ctx.addIssue({
52
+ code: z.ZodIssueCode.custom,
53
+ message: "Dispatch destination platform must be slack",
54
+ path: ["destination"]
55
+ });
56
+ return;
57
+ }
58
+ const subject = record.credentialSubject;
59
+ if (!subject) return;
60
+ if (subject.allowedWhen === "private-direct-conversation") {
61
+ if (!record.destination.channelId.startsWith("D")) {
62
+ ctx.addIssue({
63
+ code: z.ZodIssueCode.custom,
64
+ message: "Dispatch credentialSubject requires a private direct Slack destination",
65
+ path: ["credentialSubject"]
66
+ });
67
+ return;
68
+ }
69
+ if (subject.binding.type !== "slack-direct-conversation" || subject.binding.teamId !== record.destination.teamId || subject.binding.channelId !== record.destination.channelId) ctx.addIssue({
70
+ code: z.ZodIssueCode.custom,
71
+ message: "Dispatch credentialSubject binding must match destination",
72
+ path: ["credentialSubject", "binding"]
73
+ });
74
+ return;
75
+ }
76
+ if (subject.binding.type !== subject.allowedWhen || subject.binding.plugin !== record.plugin || subject.binding.taskId !== subject.taskId) ctx.addIssue({
77
+ code: z.ZodIssueCode.custom,
78
+ message: "Dispatch credentialSubject binding must match task",
79
+ path: ["credentialSubject", "binding"]
80
+ });
81
+ });
82
+ /** Return the durable key for one plugin-facing dispatch projection. */
83
+ function getDispatchStorageKey(id) {
84
+ return `${DISPATCH_PREFIX}:record:${id}`;
85
+ }
86
+ function dispatchLockKey(id) {
87
+ return `${DISPATCH_PREFIX}:lock:${id}`;
88
+ }
89
+ function pendingDispatchMailboxAppendIndexKey() {
90
+ return `${DISPATCH_PREFIX}:incomplete`;
91
+ }
92
+ function pendingDispatchMailboxAppendIndexLockKey() {
93
+ return `${DISPATCH_PREFIX}:incomplete:lock`;
94
+ }
95
+ function normalizeMetadata(metadata) {
96
+ if (!metadata) return;
97
+ const entries = Object.entries(metadata).filter((entry) => typeof entry[0] === "string" && typeof entry[1] === "string");
98
+ return entries.length > 0 ? Object.fromEntries(entries) : void 0;
99
+ }
100
+ function buildDispatchId(plugin, idempotencyKey) {
101
+ return `dispatch_${createHash("sha256").update(plugin).update("\0").update(idempotencyKey).digest("hex").slice(0, 32)}`;
102
+ }
103
+ /** Parse one durable dispatch projection record. */
104
+ function parseDispatchRecord(value) {
105
+ const parsed = dispatchRecordSchema.safeParse(value);
106
+ return parsed.success ? parsed.data : void 0;
107
+ }
108
+ /** Return the isolated durable conversation id for one dispatch. */
109
+ function getDispatchConversationId(dispatch) {
110
+ return `agent-dispatch:${dispatch.id}`;
111
+ }
112
+ /** Return the stable synthetic input message id for one dispatch turn. */
113
+ function getDispatchInputMessageId(dispatchId) {
114
+ return `agent-dispatch:${dispatchId}`;
115
+ }
116
+ /** Return the stable turn id used by every run of one dispatch. */
117
+ function getDispatchTurnId(dispatchId) {
118
+ return `dispatch:${dispatchId}`;
119
+ }
120
+ function toDispatchProjection(record) {
121
+ return {
122
+ id: record.id,
123
+ status: record.status,
124
+ ...record.resultMessageTs ? { resultMessageTs: record.resultMessageTs } : void 0,
125
+ ...record.errorMessage ? { errorMessage: record.errorMessage } : void 0
126
+ };
127
+ }
128
+ /** Gate recovery to dispatches that can still make progress. */
129
+ function isTerminalDispatchStatus(status) {
130
+ return status === "completed" || status === "failed" || status === "blocked";
131
+ }
132
+ /** Serialize dispatch projection mutations. */
133
+ async function withDispatchLock(dispatchId, task) {
134
+ const state = getStateAdapter();
135
+ await state.connect();
136
+ const lock = await state.acquireLock(dispatchLockKey(dispatchId), DISPATCH_LOCK_TTL_MS);
137
+ if (!lock) throw new Error(`Could not acquire dispatch lock for ${dispatchId}`);
138
+ try {
139
+ return await task(state);
140
+ } finally {
141
+ await state.releaseLock(lock);
142
+ }
143
+ }
144
+ async function putRecord(state, record) {
145
+ const parsed = dispatchRecordSchema.safeParse(record);
146
+ if (!parsed.success) throw new Error(`Dispatch record is invalid: ${parsed.error.issues.map((issue) => `${issue.path.join(".") || "<root>"} ${issue.message}`).join("; ")}`);
147
+ const next = parsed.data;
148
+ await state.set(getDispatchStorageKey(next.id), next, JUNIOR_THREAD_STATE_TTL_MS);
149
+ }
150
+ async function updatePendingDispatchMailboxAppendIndex(state, update) {
151
+ const lock = await state.acquireLock(pendingDispatchMailboxAppendIndexLockKey(), DISPATCH_MAILBOX_APPEND_INDEX_LOCK_TTL_MS);
152
+ if (!lock) throw new Error("Could not acquire dispatch mailbox append index lock");
153
+ try {
154
+ const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
155
+ const ids = stored === void 0 || stored === null ? [] : pendingDispatchMailboxAppendIndexSchema.parse(stored);
156
+ const next = [...new Set(update([...new Set(ids)]))].slice(-1e4);
157
+ if (next.length === ids.length && next.every((id, index) => id === ids[index])) return;
158
+ await state.set(pendingDispatchMailboxAppendIndexKey(), next, JUNIOR_THREAD_STATE_TTL_MS);
159
+ } finally {
160
+ await state.releaseLock(lock);
161
+ }
162
+ }
163
+ /** Load dispatch state for conversation work and plugin projections. */
164
+ async function getDispatchRecord(id) {
165
+ const state = getStateAdapter();
166
+ await state.connect();
167
+ return parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
168
+ }
169
+ /** Create a dispatch idempotently within its owner namespace. */
170
+ async function createOrGetDispatch(args) {
171
+ const id = buildDispatchId(args.plugin, args.options.idempotencyKey);
172
+ return await withDispatchLock(id, async (state) => {
173
+ const existing = parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
174
+ if (existing) return {
175
+ record: existing,
176
+ status: "already_exists"
177
+ };
178
+ const metadata = normalizeMetadata(args.options.metadata);
179
+ const record = {
180
+ actor: {
181
+ platform: "system",
182
+ name: args.plugin
183
+ },
184
+ createdAtMs: args.nowMs,
185
+ ...args.options.credentialSubject ? { credentialSubject: args.options.credentialSubject } : void 0,
186
+ destination: args.options.destination,
187
+ destinationVisibility: args.options.destinationVisibility,
188
+ id,
189
+ idempotencyKey: args.options.idempotencyKey,
190
+ input: args.options.input,
191
+ ...metadata ? { metadata } : void 0,
192
+ plugin: args.plugin,
193
+ ...args.options.replyAttribution ? { replyAttribution: args.options.replyAttribution } : void 0,
194
+ ...args.options.outcomes ? { outcomes: args.options.outcomes } : void 0,
195
+ status: "pending",
196
+ source: args.options.source,
197
+ updatedAtMs: args.nowMs
198
+ };
199
+ await updatePendingDispatchMailboxAppendIndex(state, (ids) => ids.includes(id) ? ids : [...ids, id]);
200
+ await putRecord(state, record);
201
+ return {
202
+ record,
203
+ status: "created"
204
+ };
205
+ });
206
+ }
207
+ async function transitionDispatch(id, transition) {
208
+ return await withDispatchLock(id, async (state) => {
209
+ const current = parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
210
+ if (!current) return;
211
+ const transitioned = transition(current);
212
+ if (transitioned === current) return current;
213
+ const next = {
214
+ ...transitioned,
215
+ updatedAtMs: Date.now()
216
+ };
217
+ await putRecord(state, next);
218
+ return next;
219
+ });
220
+ }
221
+ /** Mark a dispatch projection as actively running. */
222
+ async function markDispatchRunning(id) {
223
+ return await transitionDispatch(id, (record) => isTerminalDispatchStatus(record.status) ? record : {
224
+ ...record,
225
+ status: "running",
226
+ errorMessage: void 0
227
+ });
228
+ }
229
+ /** Project a durable awaiting-resume turn state to the plugin API. */
230
+ async function markDispatchAwaitingResume(id) {
231
+ return await transitionDispatch(id, (record) => isTerminalDispatchStatus(record.status) ? record : {
232
+ ...record,
233
+ status: "awaiting_resume"
234
+ });
235
+ }
236
+ async function recordEventTaskExecution(previous, next, status) {
237
+ if (!next || next.status !== status || previous?.status === status) return;
238
+ if (next.plugin !== "junior") return;
239
+ const eventTaskId = next.metadata?.eventTaskId;
240
+ if (!eventTaskId) return;
241
+ const conversationId = getDispatchConversationId(next);
242
+ const conversation = await getConversationStore().get({ conversationId });
243
+ await recordTaskExecution("event", eventTaskId, {
244
+ ...conversation ? { conversationId } : void 0,
245
+ executionId: next.id,
246
+ nowMs: next.updatedAtMs,
247
+ status
248
+ });
249
+ }
250
+ /** Project a blocked turn to the plugin API. */
251
+ async function markDispatchBlocked(id, errorMessage, resultMessageTs) {
252
+ const previous = await getDispatchRecord(id);
253
+ const next = await transitionDispatch(id, (record) => isTerminalDispatchStatus(record.status) ? record : {
254
+ ...record,
255
+ errorMessage,
256
+ ...resultMessageTs ? { resultMessageTs } : void 0,
257
+ status: "blocked"
258
+ });
259
+ await recordEventTaskExecution(previous, next, "blocked");
260
+ return next;
261
+ }
262
+ /** Project a completed turn and its accepted Slack message to the plugin API. */
263
+ async function markDispatchCompleted(id, resultMessageTs) {
264
+ const previous = await getDispatchRecord(id);
265
+ const next = await transitionDispatch(id, (record) => isTerminalDispatchStatus(record.status) ? record : {
266
+ ...record,
267
+ errorMessage: void 0,
268
+ ...resultMessageTs ? { resultMessageTs } : void 0,
269
+ status: "completed"
270
+ });
271
+ await recordEventTaskExecution(previous, next, "completed");
272
+ return next;
273
+ }
274
+ /** Project a terminal conversation turn failure to the plugin API. */
275
+ async function markDispatchFailed(id, errorMessage, resultMessageTs) {
276
+ const previous = await getDispatchRecord(id);
277
+ const next = await transitionDispatch(id, (record) => isTerminalDispatchStatus(record.status) ? record : {
278
+ ...record,
279
+ errorMessage,
280
+ ...resultMessageTs ? { resultMessageTs } : void 0,
281
+ status: "failed"
282
+ });
283
+ await recordEventTaskExecution(previous, next, "failed");
284
+ return next;
285
+ }
286
+ /** Remove a dispatch after its durable mailbox append has succeeded. */
287
+ async function confirmDispatchMailboxAppend(id) {
288
+ const state = getStateAdapter();
289
+ await state.connect();
290
+ await updatePendingDispatchMailboxAppendIndex(state, (ids) => ids.filter((candidate) => candidate !== id));
291
+ }
292
+ /** List dispatches whose durable mailbox append may not have completed. */
293
+ async function listPendingDispatchMailboxAppends() {
294
+ const state = getStateAdapter();
295
+ await state.connect();
296
+ const stored = await state.get(pendingDispatchMailboxAppendIndexKey());
297
+ return stored === void 0 || stored === null ? [] : [...new Set(pendingDispatchMailboxAppendIndexSchema.parse(stored))];
298
+ }
299
+ /** Load the dispatch under lock before its mailbox append becomes visible. */
300
+ async function claimDispatchMailboxAppend(id) {
301
+ return await withDispatchLock(id, async (state) => {
302
+ return parseDispatchRecord(await state.get(getDispatchStorageKey(id)));
303
+ });
304
+ }
305
+ /** Return a plugin-scoped dispatch projection without exposing raw runtime state. */
306
+ async function getPluginDispatchProjection(args) {
307
+ const record = await getDispatchRecord(args.id);
308
+ if (!record || record.plugin !== args.plugin) return;
309
+ return toDispatchProjection(record);
310
+ }
311
+ //#endregion
312
+ //#region src/chat/services/turn-session-routing.ts
313
+ async function loadConversationChain(conversationId, conversationStore) {
314
+ const chain = [];
315
+ const seen = /* @__PURE__ */ new Set();
316
+ let cursor = conversationId;
317
+ while (cursor && !seen.has(cursor)) {
318
+ seen.add(cursor);
319
+ const conversation = await conversationStore.get({ conversationId: cursor });
320
+ if (!conversation) break;
321
+ chain.push(conversation);
322
+ cursor = conversation.parentConversationId;
323
+ }
324
+ return chain;
325
+ }
326
+ /**
327
+ * Normalize a stored Slack session source against the conversation destination.
328
+ *
329
+ * Does not create channel or thread values from the conversation id.
330
+ */
331
+ function slackSourceForDestination(args) {
332
+ if (args.source?.kind !== "slack" || args.source.channelId !== args.destination.channelId) return;
333
+ const threadTs = args.source.threadTs?.trim();
334
+ return {
335
+ kind: "slack",
336
+ visibility: args.source.visibility ?? "public",
337
+ teamId: args.source.teamId?.trim() || args.destination.teamId,
338
+ channelId: args.source.channelId,
339
+ ...threadTs ? { threadTs } : void 0
340
+ };
341
+ }
342
+ /**
343
+ * Resolve the Conversation Location, Destination, and session Source.
344
+ *
345
+ * Reads parent Conversations when this Conversation has no Destination.
346
+ */
347
+ async function resolveConversationRouting(args) {
348
+ const conversationStore = args.conversationStore ?? (await import("./agent-hooks-DXHpTJ4m.js").then((n) => n.St)).getConversationStore();
349
+ const chain = await loadConversationChain(args.conversationId, conversationStore);
350
+ let destination;
351
+ let source;
352
+ let location;
353
+ for (const conversation of chain) {
354
+ destination ??= conversation.destination;
355
+ source ??= conversation.sessionSource;
356
+ location ??= conversation.location;
357
+ if (destination && source) break;
358
+ }
359
+ if (!destination) return;
360
+ if (destination.platform === "slack") {
361
+ const slackSource = slackSourceForDestination({
362
+ destination,
363
+ source
364
+ });
365
+ return {
366
+ destination,
367
+ ...location ? { location } : void 0,
368
+ ...slackSource ? { source: slackSource } : void 0
369
+ };
370
+ }
371
+ return {
372
+ destination,
373
+ ...location ? { location } : void 0,
374
+ ...source ? { source } : void 0
375
+ };
376
+ }
377
+ /** Require the Conversation Destination and Source from SQL. */
378
+ async function resolveTurnSessionRouting(args) {
379
+ const routing = await resolveConversationRouting(args);
380
+ if (!routing?.destination || !routing.source) throw new Error(`Conversation ${args.conversationId} is missing durable routing metadata`);
381
+ return {
382
+ destination: routing.destination,
383
+ ...routing.location ? { location: routing.location } : void 0,
384
+ source: routing.source
385
+ };
386
+ }
387
+ //#endregion
388
+ //#region src/chat/plugins/task-runner.ts
389
+ var task_runner_exports = /* @__PURE__ */ __exportAll({
390
+ processPluginTask: () => processPluginTask,
391
+ scheduleSessionCompletedPluginTasks: () => scheduleSessionCompletedPluginTasks
392
+ });
393
+ const PLUGIN_TASK_LOCK_TTL_MS = 3e5;
394
+ function isRecord(value) {
395
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
396
+ }
397
+ function textPart(value) {
398
+ if (isRecord(value) && value.type === "text" && typeof value.text === "string") return value.text;
399
+ }
400
+ function messageText(message) {
401
+ const content = message.content;
402
+ if (typeof content === "string") return sanitizeText(content);
403
+ if (!Array.isArray(content)) return "";
404
+ return sanitizeText(content.map(textPart).filter(Boolean).join("\n"));
405
+ }
406
+ function toolResultText(message) {
407
+ const record = message;
408
+ return sanitizeText([
409
+ messageText(message),
410
+ record.output,
411
+ record.result,
412
+ record.stdout,
413
+ record.stderr,
414
+ record.toolResult
415
+ ].filter((value) => typeof value === "string" && value.length > 0).join("\n"));
416
+ }
417
+ function sanitizeText(text) {
418
+ return text.replace(/<data_base64>[\s\S]*?<\/data_base64>/g, "<data_base64>[omitted]</data_base64>").replace(/data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=]+/gi, "[image data omitted]").replaceAll("\0", " ").trim();
419
+ }
420
+ /** Build the transcript provenance for a user message from its Pi provenance. */
421
+ function messageProvenance(provenance) {
422
+ return {
423
+ authority: provenance.authority,
424
+ ...provenance.actor ? { actor: provenance.actor } : void 0
425
+ };
426
+ }
427
+ function runTranscriptEntry(message, provenance, runActor) {
428
+ const role = getPiMessageRole(message);
429
+ if (role === "user" || role === "assistant") {
430
+ const text = role === "user" ? instructionTextForProjection(messageText(message)) : messageText(message);
431
+ if (!text) return;
432
+ if (role === "assistant") return {
433
+ type: "message",
434
+ role,
435
+ text
436
+ };
437
+ return {
438
+ type: "message",
439
+ role,
440
+ text,
441
+ provenance: messageProvenance(provenance),
442
+ isRunActor: sameActorIdentity(provenance.actor, runActor)
443
+ };
444
+ }
445
+ if (!isToolResultMessage(message)) return;
446
+ const toolName = normalizeToolNameFromResult(message);
447
+ if (!toolName) return;
448
+ const text = toolResultText(message);
449
+ return {
450
+ type: "toolResult",
451
+ toolName,
452
+ isError: isToolResultError(message),
453
+ ...text ? { text } : void 0
454
+ };
455
+ }
456
+ /**
457
+ * Slice the current turn's Pi messages with their aligned provenance and strip
458
+ * runtime turn context, keeping each surviving message paired with the exact
459
+ * provenance recorded for it. Stripping can drop or rewrite messages, so the
460
+ * pairing is applied per message rather than by post-strip index.
461
+ */
462
+ function turnMessagesWithProvenance(record) {
463
+ const startIndex = record.turnStartMessageIndex ?? 0;
464
+ const messages = record.piMessages.slice(startIndex);
465
+ const provenance = record.piMessageProvenance.slice(startIndex);
466
+ const paired = [];
467
+ for (const [index, message] of messages.entries()) for (const stripped of stripRuntimeTurnContext([message])) paired.push({
468
+ message: stripped,
469
+ provenance: provenance[index] ?? { authority: "context" }
470
+ });
471
+ return paired;
472
+ }
473
+ /** Recover the Slack context author identity from a persisted thread message. */
474
+ function slackContextAuthor(location, message) {
475
+ const userId = message.author?.userId?.trim();
476
+ if (!userId) return;
477
+ return {
478
+ platform: "slack",
479
+ teamId: location.teamId,
480
+ userId,
481
+ ...message.author?.userName ? { userName: message.author.userName } : void 0,
482
+ ...message.author?.fullName ? { fullName: message.author.fullName } : void 0
483
+ };
484
+ }
485
+ function slackTimestampMs(value) {
486
+ const timestamp = parseSlackMessageTs(value);
487
+ if (!timestamp) return;
488
+ const timestampMs = Number(timestamp) * 1e3;
489
+ return Number.isFinite(timestampMs) ? timestampMs : void 0;
490
+ }
491
+ function conversationMessageTimestampMs(message) {
492
+ if (message.meta?.slackTs !== void 0) return slackTimestampMs(message.meta.slackTs);
493
+ return Number.isFinite(message.createdAtMs) ? message.createdAtMs : void 0;
494
+ }
495
+ function messageExistedAtRunCompletion(message, completedAtMs) {
496
+ const messageTimestampMs = conversationMessageTimestampMs(message);
497
+ return messageTimestampMs !== void 0 && messageTimestampMs <= completedAtMs;
498
+ }
499
+ /**
500
+ * Project bounded public-thread context into the run transcript.
501
+ *
502
+ * Prior public Slack messages from the current Conversation are durable
503
+ * evidence a completed run may have acted on, so passive consumers can cite
504
+ * them. They are always context authority (never instruction), and private or
505
+ * local Conversations add nothing here.
506
+ */
507
+ async function loadConversationContextTranscriptEntries(record, location, visibility, runActor) {
508
+ if (location?.provider !== "slack" || visibility !== "public") return [];
509
+ const state = await getPersistedThreadState(record.conversationId);
510
+ const conversation = coerceThreadConversationState(state);
511
+ await hydrateConversationMessages({
512
+ conversation,
513
+ conversationId: record.conversationId
514
+ });
515
+ const entries = [];
516
+ for (const message of conversation.messages) {
517
+ if (message.role !== "user") continue;
518
+ if (!messageExistedAtRunCompletion(message, record.updatedAtMs)) continue;
519
+ const text = sanitizeText(message.text);
520
+ if (!text) continue;
521
+ const author = slackContextAuthor(location, message);
522
+ entries.push({
523
+ type: "message",
524
+ role: "user",
525
+ text,
526
+ provenance: {
527
+ authority: "context",
528
+ ...author ? { actor: author } : void 0
529
+ },
530
+ isRunActor: sameActorIdentity(author, runActor)
531
+ });
532
+ }
533
+ return entries;
534
+ }
535
+ async function withPluginTaskLock(taskId, callback) {
536
+ const state = getStateAdapter();
537
+ await state.connect();
538
+ const lock = await state.acquireLock(`plugin:task:${taskId}`, PLUGIN_TASK_LOCK_TTL_MS);
539
+ if (!lock) throw new Error(`Could not acquire plugin task lock for ${taskId}`);
540
+ try {
541
+ return await callback();
542
+ } finally {
543
+ await state.releaseLock(lock);
544
+ }
545
+ }
546
+ /** Load the bounded completed-run projection exposed to plugin tasks. */
547
+ async function loadPluginRun(params) {
548
+ const record = await getTurnRecord(params.conversationId, params.sessionId);
549
+ if (!record) throw new Error("Completed plugin task session record is unavailable");
550
+ if (record.state !== "completed") throw new Error("Completed plugin task session record is not completed");
551
+ const conversationStore = getConversationStore();
552
+ const routing = await resolveConversationRouting({
553
+ conversationId: params.conversationId,
554
+ conversationStore
555
+ });
556
+ if (!routing) throw new Error(`Conversation ${params.conversationId} is missing durable routing metadata`);
557
+ const source = record.source ?? routing.source;
558
+ if (!source) throw new Error("Completed plugin task session record is missing Source");
559
+ const conversation = await conversationStore.get({ conversationId: params.conversationId });
560
+ const runActor = record.actor ?? record.actors[0] ?? (record.dispatchId ? (await getDispatchRecord(record.dispatchId))?.actor : void 0);
561
+ const actorUser = runActor ? (await readActorIdentity(runActor))?.user : void 0;
562
+ const runEntries = turnMessagesWithProvenance(record).map(({ message, provenance }) => runTranscriptEntry(message, provenance, runActor)).filter((entry) => Boolean(entry));
563
+ const runMessageTexts = new Set(runEntries.filter((entry) => entry.type === "message").map((entry) => entry.text));
564
+ const contextEntries = (await loadConversationContextTranscriptEntries(record, conversation?.location, conversation?.visibility, runActor)).filter((entry) => entry.type !== "message" || !runMessageTexts.has(entry.text));
565
+ return pluginRunContextSchema.parse({
566
+ ...actorUser ? { actorUserId: actorUser.id } : void 0,
567
+ completedAtMs: record.updatedAtMs,
568
+ conversationId: record.conversationId,
569
+ destination: routing.destination,
570
+ ...routing.location ? { locationId: routing.location.id } : void 0,
571
+ actors: record.actors,
572
+ ...runActor ? { actor: runActor } : void 0,
573
+ runId: record.turnId,
574
+ source,
575
+ transcript: [...contextEntries, ...runEntries]
576
+ });
577
+ }
578
+ /** Build the plugin-facing context for one claimed task attempt. */
579
+ function taskPluginContext(plugin, message, options = {}) {
580
+ const pluginName = plugin.manifest.name;
581
+ const sessionParams = pluginTaskParamsSchema.parse(message.params);
582
+ return {
583
+ db: getDb(),
584
+ embedder: createPluginEmbedder(pluginName, { signal: options.signal }),
585
+ events: createPluginConversationEvents({
586
+ conversationId: sessionParams.conversationId,
587
+ operationId: pluginTaskId(message),
588
+ plugin,
589
+ turnId: sessionParams.sessionId
590
+ }),
591
+ id: pluginTaskId(message),
592
+ log: createPluginLogger(pluginName),
593
+ model: createPluginModel(pluginName, plugin.model, { signal: options.signal }),
594
+ name: message.name,
595
+ plugin: { name: pluginName },
596
+ run: { async load() {
597
+ return await loadPluginRun(sessionParams);
598
+ } },
599
+ state: createPluginState(pluginName)
600
+ };
601
+ }
602
+ function findPluginTask(message) {
603
+ const plugin = getPlugins().find((candidate) => candidate.manifest.name === message.plugin);
604
+ if (!plugin?.tasks || !Object.hasOwn(plugin.tasks, message.name)) return;
605
+ return {
606
+ plugin,
607
+ task: plugin.tasks[message.name]
608
+ };
609
+ }
610
+ /** Schedule all plugin tasks interested in a completed agent-run session. */
611
+ async function scheduleSessionCompletedPluginTasks(params, options = {}) {
612
+ const coreParams = pluginTaskParamsSchema.parse(params);
613
+ const taskRegistrations = getPlugins().flatMap((plugin) => Object.keys(plugin.tasks ?? {}).map((name) => ({
614
+ name,
615
+ plugin
616
+ })));
617
+ if (taskRegistrations.length === 0) return;
618
+ const record = await getTurnRecord(coreParams.conversationId, coreParams.sessionId);
619
+ if (!record || record.state !== "completed") throw new Error("Completed plugin task session record is not ready");
620
+ const send = options.send ?? sendVercelPluginTask;
621
+ const messages = taskRegistrations.map(({ name, plugin }) => ({
622
+ name,
623
+ params: coreParams,
624
+ plugin: plugin.manifest.name
625
+ }));
626
+ await Promise.all(messages.map(async (message) => {
627
+ await send(message);
628
+ }));
629
+ }
630
+ /** Execute one parsed plugin task request. */
631
+ async function processPluginTask(message, options = {}) {
632
+ await withPluginTaskLock(pluginTaskId(message), async () => {
633
+ const resolved = findPluginTask(message);
634
+ if (!resolved) throw new Error(`Plugin task "${message.plugin}.${message.name}" is not registered`);
635
+ await resolved.task.run(taskPluginContext(resolved.plugin, message, options));
636
+ });
637
+ }
638
+ //#endregion
639
+ export { markDispatchCompleted as _, claimDispatchMailboxAppend as a, getDispatchConversationId as c, getDispatchTurnId as d, getPluginDispatchProjection as f, markDispatchBlocked as g, markDispatchAwaitingResume as h, resolveTurnSessionRouting as i, getDispatchInputMessageId as l, listPendingDispatchMailboxAppends as m, scheduleSessionCompletedPluginTasks as n, confirmDispatchMailboxAppend as o, isTerminalDispatchStatus as p, task_runner_exports as r, createOrGetDispatch as s, processPluginTask as t, getDispatchRecord as u, markDispatchFailed as v, markDispatchRunning as y };