@sentry/junior 0.88.0 → 0.90.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 (158) hide show
  1. package/README.md +16 -5
  2. package/dist/{agent-hooks-RIJ5TSWP.js → agent-hooks-MXAF7RQL.js} +10 -10
  3. package/dist/api/people/list.js +8 -8
  4. package/dist/api/people/profile.js +8 -8
  5. package/dist/api-reference.d.ts +1 -1
  6. package/dist/app.js +716 -732
  7. package/dist/{catalog-runtime-MW7XERSU.js → catalog-runtime-DXE6NXHT.js} +5 -5
  8. package/dist/chat/agent/index.d.ts +4 -0
  9. package/dist/chat/agent/prompt.d.ts +78 -0
  10. package/dist/chat/{respond.d.ts → agent/request.d.ts} +98 -55
  11. package/dist/chat/agent/resume.d.ts +65 -0
  12. package/dist/chat/agent/sandbox.d.ts +15 -0
  13. package/dist/chat/agent/session.d.ts +20 -0
  14. package/dist/chat/agent/skills.d.ts +26 -0
  15. package/dist/chat/agent/tools.d.ts +78 -0
  16. package/dist/chat/agent-dispatch/runner.d.ts +3 -5
  17. package/dist/chat/app/production.d.ts +3 -1
  18. package/dist/chat/app/services.d.ts +1 -4
  19. package/dist/chat/logging.d.ts +2 -0
  20. package/dist/chat/mcp/tool-manager.d.ts +1 -6
  21. package/dist/chat/pi/messages.d.ts +18 -2
  22. package/dist/chat/pi/transcript.d.ts +31 -0
  23. package/dist/chat/plugins/agent-hooks.d.ts +2 -2
  24. package/dist/chat/prompt.d.ts +1 -1
  25. package/dist/chat/runtime/agent-continue-runner.d.ts +5 -5
  26. package/dist/chat/runtime/agent-run-outcome.d.ts +21 -0
  27. package/dist/chat/runtime/agent-runner.d.ts +11 -0
  28. package/dist/chat/runtime/delivered-turn-state.d.ts +2 -2
  29. package/dist/chat/runtime/generated-artifacts.d.ts +16 -0
  30. package/dist/chat/runtime/reply-executor.d.ts +2 -2
  31. package/dist/chat/runtime/slack-resume.d.ts +21 -12
  32. package/dist/chat/runtime/turn.d.ts +5 -39
  33. package/dist/chat/services/reply-delivery-plan.d.ts +0 -8
  34. package/dist/chat/services/turn-failure-response.d.ts +4 -4
  35. package/dist/chat/services/turn-result.d.ts +4 -7
  36. package/dist/chat/slack/channel.d.ts +9 -5
  37. package/dist/chat/slack/client.d.ts +3 -1
  38. package/dist/chat/slack/context.d.ts +17 -5
  39. package/dist/chat/slack/id-param.d.ts +25 -0
  40. package/dist/chat/slack/ids.d.ts +18 -4
  41. package/dist/chat/slack/outbound.d.ts +16 -0
  42. package/dist/chat/slack/output.d.ts +3 -3
  43. package/dist/chat/slack/reply.d.ts +4 -7
  44. package/dist/chat/slack/timestamp-param.d.ts +25 -0
  45. package/dist/chat/slack/tools/canvas/context.d.ts +18 -0
  46. package/dist/chat/slack/tools/canvas/create.d.ts +4 -0
  47. package/dist/chat/slack/tools/canvas/edit.d.ts +3 -0
  48. package/dist/chat/slack/tools/canvas/read.d.ts +6 -0
  49. package/dist/chat/slack/tools/canvas/write.d.ts +3 -0
  50. package/dist/chat/{tools/slack → slack/tools}/channel-access.d.ts +4 -3
  51. package/dist/chat/{tools → slack/tools}/channel-capabilities.d.ts +2 -0
  52. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -0
  53. package/dist/chat/{tools/slack → slack/tools}/context.d.ts +5 -4
  54. package/dist/chat/slack/tools/list/add-items.d.ts +3 -0
  55. package/dist/chat/{tools/slack/lists.d.ts → slack/tools/list/api.d.ts} +2 -1
  56. package/dist/chat/slack/tools/list/create.d.ts +3 -0
  57. package/dist/chat/slack/tools/list/get-items.d.ts +3 -0
  58. package/dist/chat/slack/tools/list/update-item.d.ts +3 -0
  59. package/dist/chat/slack/tools/message-add-reaction.d.ts +4 -0
  60. package/dist/chat/slack/tools/send-message.d.ts +11 -0
  61. package/dist/chat/{tools/slack → slack/tools}/slack-message-url.d.ts +5 -5
  62. package/dist/chat/slack/tools/thread-read.d.ts +6 -0
  63. package/dist/chat/slack/tools/user-lookup.d.ts +2 -0
  64. package/dist/chat/slack/users.d.ts +2 -1
  65. package/dist/chat/{services → slack}/vision-context.d.ts +4 -2
  66. package/dist/chat/state/session-log.d.ts +1 -1
  67. package/dist/chat/tool-exposure.d.ts +10 -0
  68. package/dist/chat/tool-support/catalog-tool-call.d.ts +10 -0
  69. package/dist/chat/{tools/execution → tool-support}/normalize-result.d.ts +4 -1
  70. package/dist/chat/{tools/agent-tools.d.ts → tool-support/pi-tool-adapter.d.ts} +3 -9
  71. package/dist/chat/tool-support/schema-summary.d.ts +2 -0
  72. package/dist/chat/{tools → tool-support}/skill/mcp-tool-summary.d.ts +0 -6
  73. package/dist/chat/tool-support/structured-result.d.ts +73 -0
  74. package/dist/chat/tool-support/text-range-result.d.ts +58 -0
  75. package/dist/chat/tool-support/tool-execution-report.d.ts +7 -0
  76. package/dist/chat/tool-support/zod-tool.d.ts +27 -0
  77. package/dist/chat/tools/advisor/tool.d.ts +9 -18
  78. package/dist/chat/tools/definition.d.ts +56 -9
  79. package/dist/chat/tools/execute-tool.d.ts +3 -0
  80. package/dist/chat/tools/index.d.ts +3 -2
  81. package/dist/chat/tools/resource-events.d.ts +3 -13
  82. package/dist/chat/tools/runtime/report-progress.d.ts +1 -3
  83. package/dist/chat/tools/sandbox/bash.d.ts +1 -4
  84. package/dist/chat/tools/sandbox/edit-file.d.ts +25 -7
  85. package/dist/chat/tools/sandbox/file-uploads.d.ts +30 -0
  86. package/dist/chat/tools/sandbox/file-utils.d.ts +10 -1
  87. package/dist/chat/tools/sandbox/find-files.d.ts +12 -11
  88. package/dist/chat/tools/sandbox/grep.d.ts +18 -12
  89. package/dist/chat/tools/sandbox/list-dir.d.ts +15 -7
  90. package/dist/chat/tools/sandbox/read-file.d.ts +2 -31
  91. package/dist/chat/tools/sandbox/write-file.d.ts +1 -4
  92. package/dist/chat/tools/search-tools.d.ts +4 -0
  93. package/dist/chat/tools/skill/call-mcp-tool.d.ts +1 -4
  94. package/dist/chat/tools/skill/load-skill.d.ts +4 -5
  95. package/dist/chat/tools/skill/search-mcp-tools.d.ts +1 -5
  96. package/dist/chat/tools/system-time.d.ts +1 -1
  97. package/dist/chat/tools/types.d.ts +15 -5
  98. package/dist/chat/tools/web/fetch-content.d.ts +4 -2
  99. package/dist/chat/tools/web/fetch-tool.d.ts +4 -4
  100. package/dist/chat/tools/web/image-generate.d.ts +4 -3
  101. package/dist/chat/tools/web/search.d.ts +1 -4
  102. package/dist/{chunk-4IXHIV37.js → chunk-2S7M37HY.js} +6 -8
  103. package/dist/{chunk-L7OHKDOX.js → chunk-3MPOMK3K.js} +58 -7
  104. package/dist/{chunk-V6XDCS3X.js → chunk-5GBUZI6M.js} +4 -4
  105. package/dist/{chunk-7ES37H6U.js → chunk-AN2437E3.js} +4 -4
  106. package/dist/chunk-AOISHLSF.js +747 -0
  107. package/dist/{chunk-NYJLQI42.js → chunk-BKSZL4QO.js} +11257 -11957
  108. package/dist/{chunk-XS6NLQPA.js → chunk-DACFZ5CI.js} +474 -33
  109. package/dist/chunk-GCWD2VHP.js +1216 -0
  110. package/dist/chunk-GHGPTPBL.js +53 -0
  111. package/dist/{chunk-S35CPNPT.js → chunk-HXL2ZV74.js} +40 -216
  112. package/dist/{chunk-PBV4ZIVM.js → chunk-IRRMABQD.js} +1 -1
  113. package/dist/{chunk-2DPZRS3B.js → chunk-IX76WFJV.js} +8 -8
  114. package/dist/{chunk-66NX7MNW.js → chunk-KC42JAAQ.js} +1 -1
  115. package/dist/chunk-LFLTJQVR.js +90 -0
  116. package/dist/{chunk-IATAYWFU.js → chunk-NW7AXDA5.js} +2 -2
  117. package/dist/{chunk-F22AM3OW.js → chunk-NYWPISSO.js} +1 -1
  118. package/dist/chunk-OB42YVAE.js +16 -0
  119. package/dist/chunk-ONFOWI2D.js +955 -0
  120. package/dist/chunk-PHZHJTCK.js +1062 -0
  121. package/dist/{chunk-6UPQ5GTJ.js → chunk-QCKGI74V.js} +7 -7
  122. package/dist/{chunk-YPAE5RH3.js → chunk-RS6ANWVT.js} +1 -1
  123. package/dist/{chunk-C5NCV3OB.js → chunk-TYUXD3FN.js} +1 -1
  124. package/dist/{chunk-KBFQXJL4.js → chunk-XP7F4LYB.js} +5 -7
  125. package/dist/{chunk-VMBZFPZM.js → chunk-ZUK7BL63.js} +187 -9
  126. package/dist/cli/chat.js +55 -40
  127. package/dist/cli/check.js +6 -6
  128. package/dist/cli/init.js +30 -36
  129. package/dist/cli/plugins.js +16 -16
  130. package/dist/cli/snapshot-warmup.js +9 -9
  131. package/dist/cli/upgrade.js +21 -21
  132. package/dist/db-CXI7PR5U.js +18 -0
  133. package/dist/handlers/agent-dispatch.d.ts +3 -3
  134. package/dist/handlers/mcp-oauth-callback.d.ts +3 -3
  135. package/dist/handlers/oauth-callback.d.ts +3 -3
  136. package/dist/instrumentation.js +6 -5
  137. package/dist/nitro.js +3 -7
  138. package/dist/reporting.js +23 -25
  139. package/dist/{runner-BCRF2FCI.js → runner-FKL3RAHA.js} +89 -77
  140. package/dist/{validation-FMJO3L5P.js → validation-I7GD2YWS.js} +6 -6
  141. package/package.json +6 -6
  142. package/dist/chat/respond-helpers.d.ts +0 -82
  143. package/dist/chat/services/attachment-claims.d.ts +0 -2
  144. package/dist/chat/services/channel-intent.d.ts +0 -2
  145. package/dist/chat/tools/sandbox/attach-file.d.ts +0 -7
  146. package/dist/chat/tools/slack/canvas-tools.d.ts +0 -30
  147. package/dist/chat/tools/slack/channel-list-messages.d.ts +0 -9
  148. package/dist/chat/tools/slack/channel-post-message.d.ts +0 -5
  149. package/dist/chat/tools/slack/list-tools.d.ts +0 -21
  150. package/dist/chat/tools/slack/message-add-reaction.d.ts +0 -5
  151. package/dist/chat/tools/slack/thread-read.d.ts +0 -12
  152. package/dist/chat/tools/slack/user-lookup.d.ts +0 -8
  153. package/dist/chunk-AHLSXMOU.js +0 -506
  154. package/dist/chunk-FTMXFBDC.js +0 -49
  155. package/dist/chunk-KBSLCOGE.js +0 -2226
  156. package/dist/chunk-KQKIA4CU.js +0 -186
  157. package/dist/db-37HOGLI7.js +0 -18
  158. /package/dist/chat/{tools/slack/canvases.d.ts → slack/tools/canvas/api.d.ts} +0 -0
@@ -0,0 +1,747 @@
1
+ import {
2
+ pluginTaskId,
3
+ pluginTaskParamsSchema,
4
+ sendVercelPluginTask
5
+ } from "./chunk-KNFROR7R.js";
6
+ import {
7
+ clearPendingAuth,
8
+ markTurnCompleted,
9
+ mergeArtifactsState
10
+ } from "./chunk-ONFOWI2D.js";
11
+ import {
12
+ escapeXml,
13
+ getAgentTurnSessionRecord,
14
+ getInterruptionMarker
15
+ } from "./chunk-GCWD2VHP.js";
16
+ import {
17
+ createPluginEmbedder,
18
+ createPluginModel,
19
+ getPlugins
20
+ } from "./chunk-DACFZ5CI.js";
21
+ import {
22
+ createPluginLogger,
23
+ createPluginState
24
+ } from "./chunk-NW7AXDA5.js";
25
+ import {
26
+ getStateAdapter
27
+ } from "./chunk-RS6ANWVT.js";
28
+ import {
29
+ getDb
30
+ } from "./chunk-2S7M37HY.js";
31
+ import {
32
+ GEN_AI_PROVIDER_NAME,
33
+ botConfig,
34
+ getPiMessageRole,
35
+ isToolResultError,
36
+ isToolResultMessage,
37
+ normalizeToolNameFromResult,
38
+ resolveGatewayModel,
39
+ stripRuntimeTurnContext
40
+ } from "./chunk-HXL2ZV74.js";
41
+ import {
42
+ buildTurnFailureResponse,
43
+ logWarn,
44
+ setSpanAttributes
45
+ } from "./chunk-ZUK7BL63.js";
46
+ import {
47
+ parseSlackMessageTs
48
+ } from "./chunk-LFLTJQVR.js";
49
+
50
+ // src/chat/services/context-budget.ts
51
+ var COMPACTION_TRIGGER_INPUT_RATIO = 0.75;
52
+ var COMPACTION_OUTPUT_RESERVE_RATIO = 0.25;
53
+ var COMPACTION_TARGET_RATIO = 0.8;
54
+ var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
55
+ var FALLBACK_MAX_OUTPUT_TOKENS = 128e3;
56
+ function positiveInteger(value, fallback) {
57
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
58
+ }
59
+ function estimateTextTokens(text) {
60
+ return Math.ceil(text.length / 4);
61
+ }
62
+ function calculateContextCompactionTriggerTokens(model) {
63
+ const contextWindow = positiveInteger(
64
+ model.contextWindow,
65
+ FALLBACK_CONTEXT_WINDOW_TOKENS
66
+ );
67
+ const maxTokens = positiveInteger(
68
+ model.maxTokens,
69
+ FALLBACK_MAX_OUTPUT_TOKENS
70
+ );
71
+ const outputReserve = Math.min(
72
+ maxTokens,
73
+ Math.floor(contextWindow * COMPACTION_OUTPUT_RESERVE_RATIO)
74
+ );
75
+ const usableInputTokens = Math.max(1, contextWindow - outputReserve);
76
+ return Math.max(
77
+ 1,
78
+ Math.floor(usableInputTokens * COMPACTION_TRIGGER_INPUT_RATIO)
79
+ );
80
+ }
81
+ function calculateContextCompactionTargetTokens(triggerTokens) {
82
+ return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
83
+ }
84
+ function getAgentContextCompactionTriggerTokens() {
85
+ const model = resolveGatewayModel(botConfig.modelId);
86
+ return calculateContextCompactionTriggerTokens({
87
+ contextWindow: botConfig.modelContextWindowTokens ?? model.contextWindow,
88
+ maxTokens: model.maxTokens
89
+ });
90
+ }
91
+ function getConversationContextCompactionTriggerTokens() {
92
+ const model = resolveGatewayModel(botConfig.fastModelId);
93
+ return calculateContextCompactionTriggerTokens({
94
+ contextWindow: model.contextWindow,
95
+ maxTokens: model.maxTokens
96
+ });
97
+ }
98
+
99
+ // src/chat/services/conversation-memory.ts
100
+ var CONTEXT_MIN_LIVE_MESSAGES = 12;
101
+ var CONTEXT_COMPACTION_BATCH_SIZE = 24;
102
+ var CONTEXT_MAX_COMPACTIONS = 16;
103
+ var CONTEXT_MAX_MESSAGE_CHARS = 3200;
104
+ var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
105
+ function generateConversationId(prefix) {
106
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
107
+ }
108
+ function normalizeConversationText(text) {
109
+ return text.trim().replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
110
+ }
111
+ function buildImageContextSuffix(message, conversation) {
112
+ const byFileId = conversation?.vision.byFileId;
113
+ const imageFileIds = message.meta?.imageFileIds ?? [];
114
+ if (!byFileId || imageFileIds.length === 0) {
115
+ return "";
116
+ }
117
+ const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
118
+ if (summaries.length === 0) {
119
+ return "";
120
+ }
121
+ return ` [image context: ${summaries.join(" | ")}]`;
122
+ }
123
+ function renderConversationMessageLine(message, conversation) {
124
+ const displayName = conversationAuthorDisplayName(message);
125
+ const markers = [];
126
+ if (message.meta?.replied === false) {
127
+ markers.push(
128
+ `assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
129
+ );
130
+ }
131
+ if (message.meta?.explicitMention) {
132
+ markers.push("explicit mention");
133
+ }
134
+ const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
135
+ const imageContext = buildImageContextSuffix(message, conversation);
136
+ return `[${message.role}] ${displayName}: ${message.text}${imageContext}${markerSuffix}`;
137
+ }
138
+ function conversationAuthorDisplayName(message) {
139
+ const author = message.author;
140
+ const fullName = authorDisplayField(author?.fullName, author?.userId);
141
+ const userName = authorDisplayField(author?.userName, author?.userId);
142
+ return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
143
+ }
144
+ function authorDisplayField(value, userId) {
145
+ if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
146
+ return void 0;
147
+ }
148
+ return value;
149
+ }
150
+ function updateConversationStats(conversation) {
151
+ const contextText = buildConversationContext(conversation);
152
+ conversation.stats.estimatedContextTokens = estimateTextTokens(
153
+ contextText ?? ""
154
+ );
155
+ conversation.stats.totalMessageCount = conversation.messages.length;
156
+ conversation.stats.updatedAtMs = Date.now();
157
+ }
158
+ function upsertConversationMessage(conversation, message) {
159
+ const existingIndex = conversation.messages.findIndex(
160
+ (entry) => entry.id === message.id
161
+ );
162
+ if (existingIndex >= 0) {
163
+ conversation.messages[existingIndex] = {
164
+ ...conversation.messages[existingIndex],
165
+ ...message,
166
+ meta: {
167
+ ...conversation.messages[existingIndex]?.meta,
168
+ ...message.meta
169
+ }
170
+ };
171
+ updateConversationStats(conversation);
172
+ return message.id;
173
+ }
174
+ conversation.messages.push(message);
175
+ updateConversationStats(conversation);
176
+ return message.id;
177
+ }
178
+ function markConversationMessage(conversation, messageId, patch) {
179
+ if (!messageId) return;
180
+ const messageIndex = conversation.messages.findIndex(
181
+ (entry) => entry.id === messageId
182
+ );
183
+ if (messageIndex < 0) return;
184
+ const current = conversation.messages[messageIndex];
185
+ conversation.messages[messageIndex] = {
186
+ ...current,
187
+ meta: {
188
+ ...current.meta ?? {},
189
+ ...patch
190
+ }
191
+ };
192
+ updateConversationStats(conversation);
193
+ }
194
+ function buildConversationContext(conversation, options = {}) {
195
+ const messages = conversation.messages.filter(
196
+ (entry) => entry.id !== options.excludeMessageId
197
+ );
198
+ if (messages.length === 0 && conversation.compactions.length === 0) {
199
+ return void 0;
200
+ }
201
+ const lines = [];
202
+ if (conversation.compactions.length > 0) {
203
+ lines.push("<thread-compactions>");
204
+ for (const [index, compaction] of conversation.compactions.entries()) {
205
+ lines.push(
206
+ ` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageIds.length}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
207
+ compaction.summary,
208
+ " </compaction>"
209
+ );
210
+ }
211
+ lines.push("</thread-compactions>");
212
+ }
213
+ if (messages.length > 0) {
214
+ if (lines.length > 0) {
215
+ lines.push("");
216
+ }
217
+ lines.push("<thread-transcript>");
218
+ for (const [index, message] of messages.entries()) {
219
+ const author = escapeXml(conversationAuthorDisplayName(message));
220
+ const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
221
+ const ts = new Date(message.createdAtMs).toISOString();
222
+ const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
223
+ lines.push(
224
+ ` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
225
+ renderConversationMessageLine(message, conversation),
226
+ " </message>"
227
+ );
228
+ }
229
+ lines.push("</thread-transcript>");
230
+ }
231
+ return lines.join("\n");
232
+ }
233
+ function pruneCompactions(compactions) {
234
+ if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
235
+ return compactions;
236
+ }
237
+ const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
238
+ const merged = compactions.slice(0, overflowCount);
239
+ const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
240
+ const mergedIds = merged.flatMap((entry) => entry.coveredMessageIds).slice(0, 500);
241
+ const compacted = {
242
+ id: generateConversationId("compaction"),
243
+ createdAtMs: Date.now(),
244
+ summary: mergedSummary,
245
+ coveredMessageIds: mergedIds
246
+ };
247
+ return [compacted, ...compactions.slice(overflowCount)];
248
+ }
249
+ async function summarizeConversationChunk(messages, conversation, context, deps) {
250
+ const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
251
+ try {
252
+ const result = await deps.completeText({
253
+ modelId: botConfig.fastModelId,
254
+ temperature: 0,
255
+ messages: [
256
+ {
257
+ role: "user",
258
+ content: [
259
+ "Summarize the following older Slack thread transcript segment for future assistant turns.",
260
+ "Keep the summary factual and concise. Do not invent details.",
261
+ "",
262
+ "Output exactly three XML sections in this order:",
263
+ "<active-asks> one bullet per outstanding user ask that has not been narrowed, answered, or replaced by a later turn. Omit the section body if none. </active-asks>",
264
+ "<resolved-or-replaced-asks> one bullet per ask that has been replaced, narrowed, answered, or already acted on in this segment. Include the replacement/outcome inline. Omit the section body if none. </resolved-or-replaced-asks>",
265
+ "<facts> one bullet per durable fact useful regardless of scope: names, ids, URLs, decisions, locations, preferences, constraints that remain true. Omit the section body if none. </facts>",
266
+ "",
267
+ "Do not output any text outside the three sections.",
268
+ "",
269
+ transcript
270
+ ].join("\n"),
271
+ timestamp: Date.now()
272
+ }
273
+ ],
274
+ metadata: {
275
+ modelId: botConfig.fastModelId,
276
+ threadId: context.threadId ?? "",
277
+ channelId: context.channelId ?? "",
278
+ requesterId: context.requesterId ?? "",
279
+ runId: context.runId ?? ""
280
+ }
281
+ });
282
+ const summary = result.text.trim();
283
+ if (summary.length > 0) {
284
+ return summary.slice(0, 3500);
285
+ }
286
+ } catch (error) {
287
+ logWarn(
288
+ "conversation_compaction_summary_failed",
289
+ {
290
+ slackThreadId: context.threadId,
291
+ slackUserId: context.requesterId,
292
+ slackChannelId: context.channelId,
293
+ runId: context.runId,
294
+ assistantUserName: botConfig.userName,
295
+ modelId: botConfig.fastModelId
296
+ },
297
+ {
298
+ "exception.message": error instanceof Error ? error.message : String(error),
299
+ "app.compaction_messages_covered": messages.length
300
+ },
301
+ "Compaction summarization failed; using fallback summary"
302
+ );
303
+ }
304
+ return transcript.slice(0, 2800);
305
+ }
306
+ async function generateThreadTitleWithDeps(sourceText, deps) {
307
+ const result = await deps.completeText({
308
+ modelId: botConfig.fastModelId,
309
+ temperature: 0,
310
+ messages: [
311
+ {
312
+ role: "user",
313
+ content: [
314
+ "Generate a concise 5-8 word Slack conversation title from the first user message below.",
315
+ "Capture the user's main request.",
316
+ "Reply with ONLY the title, with no quotes or trailing punctuation.",
317
+ "",
318
+ `First user message: ${sourceText.slice(0, 500)}`
319
+ ].join("\n"),
320
+ timestamp: Date.now()
321
+ }
322
+ ],
323
+ metadata: {
324
+ modelId: botConfig.fastModelId
325
+ }
326
+ });
327
+ return result.text.trim().slice(0, 60);
328
+ }
329
+ function getThreadTitleSourceMessage(conversation) {
330
+ let firstMessage;
331
+ for (const message of conversation.messages) {
332
+ if (!isHumanConversationMessage(message)) {
333
+ continue;
334
+ }
335
+ if (!firstMessage) {
336
+ firstMessage = message;
337
+ continue;
338
+ }
339
+ if (message.createdAtMs < firstMessage.createdAtMs) {
340
+ firstMessage = message;
341
+ continue;
342
+ }
343
+ if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
344
+ firstMessage = message;
345
+ }
346
+ }
347
+ return firstMessage;
348
+ }
349
+ async function compactConversationIfNeededWithDeps(conversation, context, deps) {
350
+ updateConversationStats(conversation);
351
+ let estimatedTokens = conversation.stats.estimatedContextTokens;
352
+ setSpanAttributes({
353
+ "app.context_tokens_estimated": estimatedTokens
354
+ });
355
+ const triggerTokens = getConversationContextCompactionTriggerTokens();
356
+ const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
357
+ while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
358
+ const compactCount = Math.min(
359
+ CONTEXT_COMPACTION_BATCH_SIZE,
360
+ conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
361
+ );
362
+ if (compactCount <= 0) {
363
+ break;
364
+ }
365
+ const compactedChunk = conversation.messages.slice(0, compactCount);
366
+ const summary = await summarizeConversationChunk(
367
+ compactedChunk,
368
+ conversation,
369
+ context,
370
+ deps
371
+ );
372
+ conversation.compactions.push({
373
+ id: generateConversationId("compaction"),
374
+ createdAtMs: Date.now(),
375
+ summary,
376
+ coveredMessageIds: compactedChunk.map((entry) => entry.id)
377
+ });
378
+ conversation.compactions = pruneCompactions(conversation.compactions);
379
+ conversation.messages = conversation.messages.slice(compactCount);
380
+ conversation.stats.compactedMessageCount += compactCount;
381
+ updateConversationStats(conversation);
382
+ estimatedTokens = conversation.stats.estimatedContextTokens;
383
+ setSpanAttributes({
384
+ "app.compaction_messages_covered": compactCount,
385
+ "app.compaction.trigger_tokens": triggerTokens,
386
+ "app.compaction.target_tokens": targetTokens,
387
+ "app.context_tokens_estimated": estimatedTokens
388
+ });
389
+ if (estimatedTokens <= targetTokens) {
390
+ break;
391
+ }
392
+ }
393
+ }
394
+ function createConversationMemoryService(deps) {
395
+ return {
396
+ compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps),
397
+ generateThreadTitle: async (sourceText) => await generateThreadTitleWithDeps(sourceText, deps)
398
+ };
399
+ }
400
+ function isHumanConversationMessage(message) {
401
+ return message.role === "user" && message.author?.isBot !== true;
402
+ }
403
+ function getConversationMessageSlackTs(message) {
404
+ return parseSlackMessageTs(message.meta?.slackTs);
405
+ }
406
+
407
+ // src/chat/services/turn-failure-response.ts
408
+ function requireTurnFailureEventId(eventId, eventName) {
409
+ if (!eventId) {
410
+ throw new Error(`Sentry did not return an event ID for ${eventName}`);
411
+ }
412
+ return eventId;
413
+ }
414
+ function getExecutionFailureReason(reply) {
415
+ const errorMessage = reply.diagnostics.errorMessage?.trim();
416
+ if (errorMessage) {
417
+ return errorMessage;
418
+ }
419
+ if (reply.diagnostics.toolErrorCount > 0) {
420
+ return `${reply.diagnostics.toolErrorCount} tool result error(s)`;
421
+ }
422
+ if (reply.diagnostics.assistantMessageCount > 0) {
423
+ return "assistant returned no text";
424
+ }
425
+ return "empty assistant turn";
426
+ }
427
+ function getFailureCapture(reply) {
428
+ if (reply.diagnostics.outcome === "provider_error") {
429
+ return {
430
+ eventName: "agent_turn_provider_error",
431
+ error: reply.diagnostics.providerError ?? new Error(
432
+ reply.diagnostics.errorMessage ?? "Provider error without explicit message"
433
+ ),
434
+ attributes: {},
435
+ body: "Agent turn failed with provider error"
436
+ };
437
+ }
438
+ const failureReason = getExecutionFailureReason(reply);
439
+ return {
440
+ eventName: "agent_turn_execution_failure",
441
+ error: new Error(`Agent turn execution failure: ${failureReason}`),
442
+ attributes: {
443
+ "app.ai.execution_failure_reason": failureReason
444
+ },
445
+ body: "Agent turn completed with execution failure"
446
+ };
447
+ }
448
+ function getAgentTurnDiagnosticsAttributes(reply) {
449
+ return {
450
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
451
+ "gen_ai.operation.name": "invoke_agent",
452
+ "app.ai.outcome": reply.diagnostics.outcome,
453
+ "app.ai.assistant_messages": reply.diagnostics.assistantMessageCount,
454
+ "app.ai.tool_results": reply.diagnostics.toolResultCount,
455
+ "app.ai.tool_error_results": reply.diagnostics.toolErrorCount,
456
+ "app.ai.tool_call_count": reply.diagnostics.toolCalls.length,
457
+ "app.ai.used_primary_text": reply.diagnostics.usedPrimaryText,
458
+ ...reply.diagnostics.thinkingLevel ? {
459
+ "app.ai.reasoning_effort": reply.diagnostics.thinkingLevel
460
+ } : {},
461
+ ...reply.diagnostics.stopReason ? {
462
+ "gen_ai.response.finish_reasons": [reply.diagnostics.stopReason]
463
+ } : {},
464
+ ...reply.diagnostics.errorMessage ? { "exception.message": reply.diagnostics.errorMessage } : {}
465
+ };
466
+ }
467
+ function finalizeFailedTurnReply(args) {
468
+ if (args.reply.diagnostics.outcome === "success") {
469
+ return args.reply;
470
+ }
471
+ const capture = getFailureCapture(args.reply);
472
+ const eventId = requireTurnFailureEventId(
473
+ args.logException(
474
+ capture.error,
475
+ capture.eventName,
476
+ args.context,
477
+ {
478
+ ...getAgentTurnDiagnosticsAttributes(args.reply),
479
+ ...args.attributes,
480
+ ...capture.attributes
481
+ },
482
+ capture.body
483
+ ),
484
+ capture.eventName
485
+ );
486
+ const providerPartialText = args.reply.diagnostics.outcome === "provider_error" && args.reply.diagnostics.assistantMessageCount > 0 ? args.reply.text.trim() : "";
487
+ return {
488
+ ...args.reply,
489
+ text: providerPartialText ? `${providerPartialText}${getInterruptionMarker()}` : buildTurnFailureResponse(eventId),
490
+ deliveryMode: "thread",
491
+ deliveryPlan: {
492
+ mode: "thread",
493
+ postThreadText: true
494
+ }
495
+ };
496
+ }
497
+
498
+ // src/chat/plugins/task-runner.ts
499
+ import { pluginRunContextSchema } from "@sentry/junior-plugin-api";
500
+ var PLUGIN_TASK_LOCK_TTL_MS = 5 * 60 * 1e3;
501
+ function isRecord(value) {
502
+ return Boolean(value && typeof value === "object" && !Array.isArray(value));
503
+ }
504
+ function textPart(value) {
505
+ if (isRecord(value) && value.type === "text" && typeof value.text === "string") {
506
+ return value.text;
507
+ }
508
+ return void 0;
509
+ }
510
+ function messageText(message) {
511
+ const content = message.content;
512
+ if (typeof content === "string") {
513
+ return sanitizeText(content);
514
+ }
515
+ if (!Array.isArray(content)) {
516
+ return "";
517
+ }
518
+ return sanitizeText(content.map(textPart).filter(Boolean).join("\n"));
519
+ }
520
+ function toolResultText(message) {
521
+ const record = message;
522
+ const parts = [
523
+ messageText(message),
524
+ record.output,
525
+ record.result,
526
+ record.stdout,
527
+ record.stderr,
528
+ record.toolResult
529
+ ].filter(
530
+ (value) => typeof value === "string" && value.length > 0
531
+ );
532
+ return sanitizeText(parts.join("\n"));
533
+ }
534
+ function sanitizeText(text) {
535
+ return text.replace(
536
+ /<data_base64>[\s\S]*?<\/data_base64>/g,
537
+ "<data_base64>[omitted]</data_base64>"
538
+ ).replace(
539
+ /data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=]+/gi,
540
+ "[image data omitted]"
541
+ ).replaceAll("\0", " ").trim();
542
+ }
543
+ function runTranscriptEntry(message) {
544
+ const role = getPiMessageRole(message);
545
+ if (role === "user" || role === "assistant") {
546
+ const text2 = messageText(message);
547
+ if (!text2) {
548
+ return void 0;
549
+ }
550
+ return { type: "message", role, text: text2 };
551
+ }
552
+ if (!isToolResultMessage(message)) {
553
+ return void 0;
554
+ }
555
+ const toolName = normalizeToolNameFromResult(message);
556
+ if (!toolName) {
557
+ return void 0;
558
+ }
559
+ const text = toolResultText(message);
560
+ return {
561
+ type: "toolResult",
562
+ toolName,
563
+ isError: isToolResultError(message),
564
+ ...text ? { text } : {}
565
+ };
566
+ }
567
+ async function withPluginTaskLock(taskId, callback) {
568
+ const state = getStateAdapter();
569
+ await state.connect();
570
+ const lock = await state.acquireLock(
571
+ `plugin:task:${taskId}`,
572
+ PLUGIN_TASK_LOCK_TTL_MS
573
+ );
574
+ if (!lock) {
575
+ throw new Error(`Could not acquire plugin task lock for ${taskId}`);
576
+ }
577
+ try {
578
+ return await callback();
579
+ } finally {
580
+ await state.releaseLock(lock);
581
+ }
582
+ }
583
+ async function loadPluginRun(params) {
584
+ const record = await getAgentTurnSessionRecord(
585
+ params.conversationId,
586
+ params.sessionId
587
+ );
588
+ if (!record) {
589
+ throw new Error("Completed plugin task session record is unavailable");
590
+ }
591
+ if (record.state !== "completed") {
592
+ throw new Error("Completed plugin task session record is not completed");
593
+ }
594
+ if (!record.source || !record.destination) {
595
+ throw new Error(
596
+ "Completed plugin task session record is missing source or destination"
597
+ );
598
+ }
599
+ const sessionMessages = stripRuntimeTurnContext(
600
+ record.piMessages.slice(record.turnStartMessageIndex ?? 0)
601
+ );
602
+ return pluginRunContextSchema.parse({
603
+ completedAtMs: record.updatedAtMs,
604
+ conversationId: record.conversationId,
605
+ destination: record.destination,
606
+ ...record.requester ? { requester: record.requester } : {},
607
+ runId: record.sessionId,
608
+ source: record.source,
609
+ transcript: sessionMessages.map(runTranscriptEntry).filter((entry) => Boolean(entry))
610
+ });
611
+ }
612
+ function taskPluginContext(plugin, message, options = {}) {
613
+ const pluginName = plugin.manifest.name;
614
+ const sessionParams = pluginTaskParamsSchema.parse(message.params);
615
+ return {
616
+ db: getDb(),
617
+ embedder: createPluginEmbedder(pluginName, {
618
+ signal: options.signal
619
+ }),
620
+ id: pluginTaskId(message),
621
+ log: createPluginLogger(pluginName),
622
+ model: createPluginModel(pluginName, plugin.model, {
623
+ signal: options.signal
624
+ }),
625
+ name: message.name,
626
+ plugin: { name: pluginName },
627
+ run: {
628
+ async load() {
629
+ return await loadPluginRun(sessionParams);
630
+ }
631
+ },
632
+ state: createPluginState(pluginName)
633
+ };
634
+ }
635
+ function findPluginTask(message) {
636
+ const plugin = getPlugins().find(
637
+ (candidate) => candidate.manifest.name === message.plugin
638
+ );
639
+ if (!plugin?.tasks || !Object.hasOwn(plugin.tasks, message.name)) {
640
+ return void 0;
641
+ }
642
+ const task = plugin.tasks[message.name];
643
+ return { plugin, task };
644
+ }
645
+ async function scheduleSessionCompletedPluginTasks(params, options = {}) {
646
+ const coreParams = pluginTaskParamsSchema.parse(params);
647
+ const taskRegistrations = getPlugins().flatMap(
648
+ (plugin) => Object.keys(plugin.tasks ?? {}).map((name) => ({ name, plugin }))
649
+ );
650
+ if (taskRegistrations.length === 0) {
651
+ return;
652
+ }
653
+ const record = await getAgentTurnSessionRecord(
654
+ coreParams.conversationId,
655
+ coreParams.sessionId
656
+ );
657
+ if (!record || record.state !== "completed") {
658
+ throw new Error("Completed plugin task session record is not ready");
659
+ }
660
+ const send = options.send ?? sendVercelPluginTask;
661
+ const messages = taskRegistrations.map(({ name, plugin }) => ({
662
+ name,
663
+ params: coreParams,
664
+ plugin: plugin.manifest.name
665
+ }));
666
+ await Promise.all(
667
+ messages.map(async (message) => {
668
+ await send(message);
669
+ })
670
+ );
671
+ }
672
+ async function processPluginTask(message, options = {}) {
673
+ await withPluginTaskLock(pluginTaskId(message), async () => {
674
+ const resolved = findPluginTask(message);
675
+ if (!resolved) {
676
+ throw new Error(
677
+ `Plugin task "${message.plugin}.${message.name}" is not registered`
678
+ );
679
+ }
680
+ await resolved.task.run(
681
+ taskPluginContext(resolved.plugin, message, options)
682
+ );
683
+ });
684
+ }
685
+
686
+ // src/chat/runtime/delivered-turn-state.ts
687
+ var NO_VISIBLE_REPLY_CONVERSATION_TEXT = "[no visible reply]";
688
+ function buildDeliveredTurnStatePatch(args) {
689
+ const conversation = structuredClone(args.conversation);
690
+ const artifactStatePatch = {
691
+ ...args.reply.artifactStatePatch ?? {},
692
+ ...args.artifactStatePatch ?? {}
693
+ };
694
+ const artifacts = Object.keys(artifactStatePatch).length > 0 ? mergeArtifactsState(args.artifacts, artifactStatePatch) : void 0;
695
+ clearPendingAuth(conversation, args.sessionId);
696
+ const assistantText = normalizeConversationText(args.reply.text) || (args.reply.deliveryPlan?.postThreadText === false ? NO_VISIBLE_REPLY_CONVERSATION_TEXT : "[empty response]");
697
+ markConversationMessage(conversation, args.userMessageId, {
698
+ replied: true,
699
+ skippedReason: void 0
700
+ });
701
+ upsertConversationMessage(conversation, {
702
+ id: generateConversationId("assistant"),
703
+ role: "assistant",
704
+ text: assistantText,
705
+ createdAtMs: Date.now(),
706
+ author: {
707
+ userName: botConfig.userName,
708
+ isBot: true
709
+ },
710
+ meta: {
711
+ replied: true
712
+ }
713
+ });
714
+ markTurnCompleted({
715
+ conversation,
716
+ nowMs: Date.now(),
717
+ sessionId: args.sessionId,
718
+ updateConversationStats
719
+ });
720
+ return {
721
+ artifacts,
722
+ conversation,
723
+ sandboxId: args.reply.sandboxId,
724
+ sandboxDependencyProfileHash: args.reply.sandboxDependencyProfileHash
725
+ };
726
+ }
727
+
728
+ export {
729
+ estimateTextTokens,
730
+ getAgentContextCompactionTriggerTokens,
731
+ generateConversationId,
732
+ normalizeConversationText,
733
+ updateConversationStats,
734
+ upsertConversationMessage,
735
+ markConversationMessage,
736
+ buildConversationContext,
737
+ getThreadTitleSourceMessage,
738
+ createConversationMemoryService,
739
+ isHumanConversationMessage,
740
+ getConversationMessageSlackTs,
741
+ requireTurnFailureEventId,
742
+ getAgentTurnDiagnosticsAttributes,
743
+ finalizeFailedTurnReply,
744
+ scheduleSessionCompletedPluginTasks,
745
+ processPluginTask,
746
+ buildDeliveredTurnStatePatch
747
+ };