@sentry/junior 0.147.0 → 0.149.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 (162) hide show
  1. package/dist/{agent-hooks-HS6VGUPM.js → agent-hooks-CWMCXAMT.js} +10 -10
  2. package/dist/api/conversations/access.d.ts +2 -1
  3. package/dist/api/conversations/aggregate.d.ts +2 -0
  4. package/dist/api/conversations/archive.d.ts +3 -5
  5. package/dist/api/conversations/create.d.ts +6 -0
  6. package/dist/api/conversations/detail.d.ts +2 -296
  7. package/dist/api/conversations/event-list.d.ts +2 -193
  8. package/dist/api/conversations/list.d.ts +4 -66
  9. package/dist/api/conversations/pending-messages.d.ts +15 -0
  10. package/dist/api/conversations/routes.d.ts +1 -1
  11. package/dist/api/conversations/stats.d.ts +0 -54
  12. package/dist/api/http.d.ts +2 -6
  13. package/dist/api/locations/detail.d.ts +3 -111
  14. package/dist/api/locations/list.d.ts +0 -45
  15. package/dist/api/locations/query.d.ts +2 -1
  16. package/dist/api/locations/routes.d.ts +1 -1
  17. package/dist/api/people/list.d.ts +0 -29
  18. package/dist/api/people/profile.d.ts +2 -106
  19. package/dist/api/people/profile.query.d.ts +2 -1
  20. package/dist/api/people/routes.d.ts +1 -1
  21. package/dist/api/people/shared.d.ts +3 -1
  22. package/dist/api/people/spend.d.ts +3 -4
  23. package/dist/api/personal-tokens/routes.d.ts +2 -2
  24. package/dist/api/route.d.ts +8 -14
  25. package/dist/api/schema/conversation.d.ts +130 -25
  26. package/dist/api/schema/location.d.ts +4 -0
  27. package/dist/api/schema/person.d.ts +7 -3
  28. package/dist/api/schema.d.ts +2 -2
  29. package/dist/api/schema.js +13 -1
  30. package/dist/api/stats.d.ts +0 -13
  31. package/dist/api/validation.d.ts +7 -0
  32. package/dist/api/viewer.d.ts +3 -0
  33. package/dist/api.d.ts +2 -1
  34. package/dist/api.js +772 -561
  35. package/dist/app.js +241 -204
  36. package/dist/{catalog-runtime-M6ZTPYRV.js → catalog-runtime-GAW5APVI.js} +5 -5
  37. package/dist/chat/actor.d.ts +4 -1
  38. package/dist/chat/agent/request.d.ts +2 -0
  39. package/dist/chat/agent/resume.d.ts +1 -0
  40. package/dist/chat/agent-dispatch/context.d.ts +2 -2
  41. package/dist/chat/agent-invocations/types.d.ts +52 -0
  42. package/dist/chat/api-turns/work.d.ts +108 -0
  43. package/dist/chat/app/services.d.ts +1 -1
  44. package/dist/chat/conversations/history.d.ts +60 -0
  45. package/dist/chat/conversations/provenance.d.ts +6 -0
  46. package/dist/chat/conversations/sql/actor-identity.d.ts +19 -0
  47. package/dist/chat/conversations/store.d.ts +1 -1
  48. package/dist/chat/resource-events/store.d.ts +1 -1
  49. package/dist/chat/runtime/reply-executor.d.ts +1 -3
  50. package/dist/chat/runtime/slack-runtime.d.ts +2 -1
  51. package/dist/chat/runtime/turn-preparation.d.ts +9 -0
  52. package/dist/chat/scheduled-tasks/tool-support.d.ts +5 -5
  53. package/dist/chat/scheduled-tasks/tools/create.d.ts +2 -2
  54. package/dist/chat/scheduled-tasks/tools/delete.d.ts +2 -2
  55. package/dist/chat/scheduled-tasks/tools/list.d.ts +2 -2
  56. package/dist/chat/scheduled-tasks/tools/run-now.d.ts +2 -2
  57. package/dist/chat/scheduled-tasks/tools/update.d.ts +2 -2
  58. package/dist/chat/services/conversation-memory.d.ts +2 -2
  59. package/dist/chat/services/conversation-title.d.ts +20 -0
  60. package/dist/chat/slack/assistant-thread/title.d.ts +10 -27
  61. package/dist/chat/slack/channel.d.ts +26 -0
  62. package/dist/chat/slack/dispatch-turn.d.ts +1 -0
  63. package/dist/chat/slack/timestamp-param.d.ts +0 -2
  64. package/dist/chat/slack/{tools → tool-support}/canvas/api.d.ts +1 -1
  65. package/dist/chat/slack/{tools → tool-support}/canvas/context.d.ts +3 -0
  66. package/dist/chat/slack/tool-support/channel-access.d.ts +42 -0
  67. package/dist/chat/slack/tool-support/channel-target.d.ts +27 -0
  68. package/dist/chat/slack/tool-support/list/params.d.ts +5 -0
  69. package/dist/chat/slack/tools/canvas/create.d.ts +1 -1
  70. package/dist/chat/slack/tools/channel-join.d.ts +28 -0
  71. package/dist/chat/slack/tools/channel-list-messages.d.ts +3 -2
  72. package/dist/chat/slack/tools/conversation-message-search.d.ts +2 -8
  73. package/dist/chat/slack/tools/message-add-reaction.d.ts +1 -1
  74. package/dist/chat/slack/tools/public-search.d.ts +63 -2
  75. package/dist/chat/slack/tools/send-files.d.ts +1 -1
  76. package/dist/chat/slack/tools/thread-read.d.ts +2 -5
  77. package/dist/chat/source.d.ts +2 -0
  78. package/dist/chat/state/conversation.d.ts +1 -0
  79. package/dist/chat/task-execution/checkpoint.d.ts +1 -0
  80. package/dist/chat/task-execution/state.d.ts +9 -4
  81. package/dist/chat/task-execution/store.d.ts +3 -2
  82. package/dist/chat/task-execution/turn-cursor.d.ts +2 -0
  83. package/dist/chat/task-execution/worker.d.ts +2 -0
  84. package/dist/chat/tasks/read.d.ts +1 -1
  85. package/dist/chat/tools/search-conversation-events.d.ts +1 -1
  86. package/dist/chat/tools/types.d.ts +11 -4
  87. package/dist/chat/tools/watch-resource-events.d.ts +2 -2
  88. package/dist/{chunk-ORO3PWKQ.js → chunk-25SC7D6Z.js} +1 -1
  89. package/dist/{chunk-GGB4WL5A.js → chunk-2JHKISQA.js} +1 -1
  90. package/dist/{chunk-AVZDSUFB.js → chunk-2P6BQQER.js} +4 -4
  91. package/dist/{chunk-7NVJVJID.js → chunk-32CXPIZE.js} +2 -2
  92. package/dist/{chunk-YSB2N563.js → chunk-3GYZ5YEH.js} +121 -87
  93. package/dist/{chunk-DU74WPIC.js → chunk-3I2IVWSZ.js} +1 -1
  94. package/dist/{chunk-SVHUEHUL.js → chunk-3JVUWLR7.js} +65 -4
  95. package/dist/{chunk-RXBVB2JM.js → chunk-3KGQOKIG.js} +1 -1
  96. package/dist/{chunk-YBVXBNSV.js → chunk-4JGM7FXO.js} +2 -2
  97. package/dist/{chunk-AZ7EAOBE.js → chunk-4YKPOYRV.js} +5 -5
  98. package/dist/chunk-5CWUQXXT.js +806 -0
  99. package/dist/{chunk-DLDRQHG5.js → chunk-6G5RCDKP.js} +1 -1
  100. package/dist/{chunk-AYU72WO7.js → chunk-75DSYFFB.js} +47 -70
  101. package/dist/{chunk-YHDERDRO.js → chunk-7BH46NB4.js} +91 -75
  102. package/dist/{chunk-745QJ3ZA.js → chunk-ALJG73MQ.js} +2 -2
  103. package/dist/chunk-ART7P442.js +48 -0
  104. package/dist/{chunk-7A4M7PCV.js → chunk-DK5UT6PQ.js} +1180 -556
  105. package/dist/{chunk-AIOK6LAG.js → chunk-EW2LNCRL.js} +813 -44
  106. package/dist/chunk-EXW76GW4.js +36 -0
  107. package/dist/chunk-GDFF3BQG.js +136 -0
  108. package/dist/{chunk-7UQIVT6G.js → chunk-JMH3DBM5.js} +6 -6
  109. package/dist/{chunk-CWP5RHVX.js → chunk-JMSMXXED.js} +12 -5
  110. package/dist/{chunk-CSZINQJ2.js → chunk-MEMURSBG.js} +1 -1
  111. package/dist/{chunk-YNTTROJT.js → chunk-QIW7OQEO.js} +1 -1
  112. package/dist/{chunk-Q4R73I43.js → chunk-RB26T3LU.js} +58 -28
  113. package/dist/{chunk-4LJO56OJ.js → chunk-RLVZIWNQ.js} +1 -1
  114. package/dist/{chunk-SGZO7UWH.js → chunk-SWHPVZBV.js} +2 -2
  115. package/dist/{chunk-PVNG6UMU.js → chunk-TFBNBXPU.js} +22 -10
  116. package/dist/{chunk-EFLJ2WJH.js → chunk-URDCZAL4.js} +5 -5
  117. package/dist/{chunk-6H5SZR24.js → chunk-UZGASGOD.js} +300 -383
  118. package/dist/{chunk-YEO3VX72.js → chunk-W7UONUIZ.js} +8 -6
  119. package/dist/{chunk-JUUKWRBE.js → chunk-X22XXSZA.js} +1 -1
  120. package/dist/{chunk-X5UY34KT.js → chunk-XPYWWMHR.js} +8 -8
  121. package/dist/{chunk-IIOOO4FQ.js → chunk-ZYMYLLWG.js} +2 -1
  122. package/dist/cli/chat.js +32 -35
  123. package/dist/cli/check.js +6 -6
  124. package/dist/cli/plugins.js +13 -13
  125. package/dist/cli/snapshot-create.js +9 -9
  126. package/dist/cli/upgrade.js +7 -7
  127. package/dist/db/schema/agent-invocations.d.ts +20 -0
  128. package/dist/db/schema/conversation-annotations.d.ts +2 -2
  129. package/dist/db/schema.d.ts +22 -2
  130. package/dist/{db-CSEW4RII.js → db-JBZLRIVY.js} +7 -7
  131. package/dist/instrumentation.js +4 -4
  132. package/dist/nitro.js +2 -2
  133. package/dist/{oauth-callback-server-FUEV5HNG.js → oauth-callback-server-MNTVYY2I.js} +24 -26
  134. package/dist/{runner-U3XHSKSH.js → runner-K24BWZSZ.js} +23 -25
  135. package/dist/{skills-AKE6W3WI.js → skills-AU5ST2CG.js} +6 -6
  136. package/dist/spawn-6PO7CVJ6.js +38 -0
  137. package/dist/{validation-WDTAULUF.js → validation-IXGZCFRO.js} +6 -6
  138. package/dist/version.js +1 -1
  139. package/dist/{work-C3HAO2BG.js → work-WCZIYILX.js} +18 -19
  140. package/dist/{worker-LNP4YKBM.js → worker-U2AACBEY.js} +11 -10
  141. package/package.json +6 -5
  142. package/dist/api/health.d.ts +0 -7
  143. package/dist/api/personal-tokens/create.d.ts +0 -11
  144. package/dist/api/personal-tokens/list.d.ts +0 -12
  145. package/dist/api/personal-tokens/revoke.d.ts +0 -5
  146. package/dist/api/plugin-reports.d.ts +0 -63
  147. package/dist/api/plugins.d.ts +0 -8
  148. package/dist/api/runtime.d.ts +0 -24
  149. package/dist/api/skills.d.ts +0 -6
  150. package/dist/chat/slack/tools/channel-access.d.ts +0 -30
  151. package/dist/chunk-34GS7B6L.js +0 -115
  152. package/dist/chunk-AVOCH2Z5.js +0 -285
  153. package/dist/chunk-HLXXQI5Q.js +0 -238
  154. package/dist/chunk-K4SZAP3K.js +0 -67
  155. package/dist/chunk-R776HKPK.js +0 -81
  156. package/dist/chunk-Z4YU7Z4L.js +0 -76
  157. package/dist/spawn-B6GDTWJ4.js +0 -39
  158. /package/dist/chat/slack/{tools → tool-support}/canvas/markdown.d.ts +0 -0
  159. /package/dist/chat/slack/{tools → tool-support}/channel-capabilities.d.ts +0 -0
  160. /package/dist/chat/slack/{tools → tool-support}/context.d.ts +0 -0
  161. /package/dist/chat/slack/{tools → tool-support}/list/api.d.ts +0 -0
  162. /package/dist/chat/slack/{tools → tool-support}/slack-message-url.d.ts +0 -0
@@ -1,11 +1,6 @@
1
1
  import {
2
- JUNIOR_NATIVE_EVENT_NAMESPACE,
3
- ToolInputError,
4
- addAgentTurnUsage,
5
- agentsInstructionsUpdatedEvent,
6
- authenticationLinkedEvent,
7
- authenticationUnlinkedEvent
8
- } from "./chunk-HLXXQI5Q.js";
2
+ usageSchema
3
+ } from "./chunk-PDO5BLNM.js";
9
4
  import {
10
5
  agentHistoryItemSchema,
11
6
  bindProviderConversation,
@@ -18,7 +13,7 @@ import {
18
13
  instructionProvenanceFor,
19
14
  sanitizePostgresJson,
20
15
  withConversationEventLock
21
- } from "./chunk-YSB2N563.js";
16
+ } from "./chunk-3GYZ5YEH.js";
22
17
  import {
23
18
  SANDBOX_WORKSPACE_ROOT,
24
19
  sleep
@@ -28,18 +23,26 @@ import {
28
23
  fenceLock,
29
24
  getStateAdapter,
30
25
  withLock
31
- } from "./chunk-ORO3PWKQ.js";
26
+ } from "./chunk-25SC7D6Z.js";
32
27
  import {
33
28
  botConfig,
29
+ completeText,
30
+ escapeXml,
31
+ resolveGatewayModel,
34
32
  unwrapCurrentInstruction
35
- } from "./chunk-7NVJVJID.js";
33
+ } from "./chunk-32CXPIZE.js";
36
34
  import {
37
35
  toStoredSlackActor
38
36
  } from "./chunk-HZF5P5UN.js";
37
+ import {
38
+ parseSlackMessageTs
39
+ } from "./chunk-F3B2YJ76.js";
39
40
  import {
40
41
  getActiveTraceId,
41
- logException
42
- } from "./chunk-4LJO56OJ.js";
42
+ logException,
43
+ logWarn,
44
+ setSpanAttributes
45
+ } from "./chunk-RLVZIWNQ.js";
43
46
 
44
47
  // src/chat/turn-context-tag.ts
45
48
  var TURN_CONTEXT_TAG = "runtime-turn-context";
@@ -178,10 +181,518 @@ function stripRuntimeTurnContext(messages) {
178
181
  });
179
182
  }
180
183
 
184
+ // src/chat/services/context-budget.ts
185
+ var COMPACTION_TRIGGER_RATIO = 0.9;
186
+ var CONTEXT_INPUT_LIMIT_RATIO = 0.95;
187
+ var COMPACTION_TARGET_RATIO = 0.8;
188
+ var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
189
+ function positiveInteger(value, fallback) {
190
+ return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
191
+ }
192
+ function estimateTextTokens(text) {
193
+ return Math.ceil(text.length / 4);
194
+ }
195
+ function calculateContextCompactionTriggerTokens(model) {
196
+ const contextWindow = positiveInteger(
197
+ model.contextWindow,
198
+ FALLBACK_CONTEXT_WINDOW_TOKENS
199
+ );
200
+ return Math.max(1, Math.floor(contextWindow * COMPACTION_TRIGGER_RATIO));
201
+ }
202
+ function calculateContextInputLimitTokens(model) {
203
+ const contextWindow = positiveInteger(
204
+ model.contextWindow,
205
+ FALLBACK_CONTEXT_WINDOW_TOKENS
206
+ );
207
+ return Math.max(1, Math.floor(contextWindow * CONTEXT_INPUT_LIMIT_RATIO));
208
+ }
209
+ function calculateContextCompactionTargetTokens(triggerTokens) {
210
+ return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
211
+ }
212
+ function getModelContextBudget(modelId) {
213
+ const model = resolveGatewayModel(modelId);
214
+ const advertisedContextWindow = positiveInteger(
215
+ model.contextWindow,
216
+ FALLBACK_CONTEXT_WINDOW_TOKENS
217
+ );
218
+ return {
219
+ contextWindow: Math.min(
220
+ botConfig.contextWindowTokens,
221
+ advertisedContextWindow
222
+ )
223
+ };
224
+ }
225
+ function getAgentContextCompactionTriggerTokens(modelId) {
226
+ return calculateContextCompactionTriggerTokens(
227
+ getModelContextBudget(modelId)
228
+ );
229
+ }
230
+ function getAgentContextInputLimitTokens(modelId) {
231
+ return calculateContextInputLimitTokens(getModelContextBudget(modelId));
232
+ }
233
+ function getConversationContextCompactionTriggerTokens() {
234
+ return calculateContextCompactionTriggerTokens(
235
+ getModelContextBudget(botConfig.fastModelId)
236
+ );
237
+ }
238
+
239
+ // src/chat/services/conversation-memory.ts
240
+ var CONTEXT_MIN_LIVE_MESSAGES = 12;
241
+ var CONTEXT_COMPACTION_BATCH_SIZE = 24;
242
+ var CONTEXT_MAX_COMPACTIONS = 16;
243
+ var CONTEXT_MAX_MESSAGE_CHARS = 3200;
244
+ var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
245
+ function generateConversationId(prefix) {
246
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
247
+ }
248
+ function normalizeConversationText(text) {
249
+ return text.replace(/\r\n?/g, "\n").trim();
250
+ }
251
+ function buildImageContextSuffix(message, conversation) {
252
+ const byFileId = conversation?.vision.byFileId;
253
+ const imageFileIds = message.meta?.imageFileIds ?? [];
254
+ if (!byFileId || imageFileIds.length === 0) {
255
+ return "";
256
+ }
257
+ const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
258
+ if (summaries.length === 0) {
259
+ return "";
260
+ }
261
+ return ` [image context: ${summaries.join(" | ")}]`;
262
+ }
263
+ function renderConversationMessageLine(message, conversation) {
264
+ const displayName = conversationAuthorDisplayName(message);
265
+ const text = message.text.replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
266
+ const markers = [];
267
+ if (message.meta?.replied === false) {
268
+ markers.push(
269
+ `assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
270
+ );
271
+ }
272
+ if (message.meta?.explicitMention) {
273
+ markers.push("explicit mention");
274
+ }
275
+ const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
276
+ const imageContext = buildImageContextSuffix(message, conversation);
277
+ return `[${message.role}] ${displayName}: ${text}${imageContext}${markerSuffix}`;
278
+ }
279
+ function conversationAuthorDisplayName(message) {
280
+ const author = message.author;
281
+ const fullName = authorDisplayField(author?.fullName, author?.userId);
282
+ const userName = authorDisplayField(author?.userName, author?.userId);
283
+ return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
284
+ }
285
+ function authorDisplayField(value, userId) {
286
+ if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
287
+ return void 0;
288
+ }
289
+ return value;
290
+ }
291
+ function estimateConversationContextTokens(conversation) {
292
+ return estimateTextTokens(buildConversationContext(conversation) ?? "");
293
+ }
294
+ function upsertConversationMessage(conversation, message) {
295
+ const existingIndex = conversation.messages.findIndex(
296
+ (entry) => entry.id === message.id
297
+ );
298
+ if (existingIndex >= 0) {
299
+ conversation.messages[existingIndex] = {
300
+ ...conversation.messages[existingIndex],
301
+ ...message,
302
+ meta: {
303
+ ...conversation.messages[existingIndex]?.meta,
304
+ ...message.meta
305
+ }
306
+ };
307
+ return message.id;
308
+ }
309
+ conversation.messages.push(message);
310
+ return message.id;
311
+ }
312
+ function recordDeliveredAssistantMessage(args) {
313
+ const prefix = `${args.sessionId}:assistant:`;
314
+ const ordinal = args.conversation.messages.filter(
315
+ (message) => message.id.startsWith(prefix)
316
+ ).length + 1;
317
+ const messageId = `${prefix}${ordinal}`;
318
+ markConversationMessage(args.conversation, args.userMessageId, {
319
+ replied: true,
320
+ skippedReason: void 0
321
+ });
322
+ upsertConversationMessage(args.conversation, {
323
+ id: messageId,
324
+ role: "assistant",
325
+ text: normalizeConversationText(args.text),
326
+ createdAtMs: Date.now(),
327
+ author: {
328
+ userName: botConfig.userName,
329
+ isBot: true
330
+ },
331
+ meta: {
332
+ replied: true,
333
+ ...args.source ? { source: args.source } : {}
334
+ }
335
+ });
336
+ return messageId;
337
+ }
338
+ function turnHasReply(conversation, turnId) {
339
+ const assistantPrefix = `${turnId}:assistant:`;
340
+ return conversation.messages.some(
341
+ (message) => message.role === "assistant" && message.id.startsWith(assistantPrefix)
342
+ );
343
+ }
344
+ function markConversationMessage(conversation, messageId, patch) {
345
+ if (!messageId) return;
346
+ const messageIndex = conversation.messages.findIndex(
347
+ (entry) => entry.id === messageId
348
+ );
349
+ if (messageIndex < 0) return;
350
+ const current = conversation.messages[messageIndex];
351
+ conversation.messages[messageIndex] = {
352
+ ...current,
353
+ meta: {
354
+ ...current.meta ?? {},
355
+ ...patch
356
+ }
357
+ };
358
+ }
359
+ function buildConversationContext(conversation, options = {}) {
360
+ const messages = conversation.messages.filter(
361
+ (entry) => entry.id !== options.excludeMessageId
362
+ );
363
+ if (messages.length === 0 && conversation.compactions.length === 0) {
364
+ return void 0;
365
+ }
366
+ const lines = [];
367
+ if (conversation.compactions.length > 0) {
368
+ lines.push("<thread-compactions>");
369
+ for (const [index, compaction] of conversation.compactions.entries()) {
370
+ lines.push(
371
+ ` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageCount}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
372
+ compaction.summary,
373
+ " </compaction>"
374
+ );
375
+ }
376
+ lines.push("</thread-compactions>");
377
+ }
378
+ if (messages.length > 0) {
379
+ if (lines.length > 0) {
380
+ lines.push("");
381
+ }
382
+ lines.push("<thread-transcript>");
383
+ for (const [index, message] of messages.entries()) {
384
+ const author = escapeXml(conversationAuthorDisplayName(message));
385
+ const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
386
+ const ts = new Date(message.createdAtMs).toISOString();
387
+ const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
388
+ lines.push(
389
+ ` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
390
+ renderConversationMessageLine(message, conversation),
391
+ " </message>"
392
+ );
393
+ }
394
+ lines.push("</thread-transcript>");
395
+ }
396
+ return lines.join("\n");
397
+ }
398
+ function pruneCompactions(compactions) {
399
+ if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
400
+ return compactions;
401
+ }
402
+ const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
403
+ const merged = compactions.slice(0, overflowCount);
404
+ const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
405
+ const coveredMessageCount = merged.reduce(
406
+ (count, entry) => count + entry.coveredMessageCount,
407
+ 0
408
+ );
409
+ const compacted = {
410
+ id: generateConversationId("compaction"),
411
+ createdAtMs: Date.now(),
412
+ summary: mergedSummary,
413
+ coveredMessageCount
414
+ };
415
+ return [compacted, ...compactions.slice(overflowCount)];
416
+ }
417
+ async function summarizeConversationChunk(messages, conversation, context, deps) {
418
+ const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
419
+ try {
420
+ const result = await deps.completeText({
421
+ modelId: botConfig.fastModelId,
422
+ temperature: 0,
423
+ promptName: "junior.thread_compaction",
424
+ messages: [
425
+ {
426
+ role: "user",
427
+ content: [
428
+ "Summarize the following older Slack thread transcript segment for future assistant turns.",
429
+ "Keep the summary factual and concise. Do not invent details.",
430
+ "",
431
+ "Output exactly three XML sections in this order:",
432
+ "<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>",
433
+ "<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>",
434
+ "<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>",
435
+ "",
436
+ "Do not output any text outside the three sections.",
437
+ "",
438
+ transcript
439
+ ].join("\n"),
440
+ timestamp: Date.now()
441
+ }
442
+ ],
443
+ metadata: {
444
+ modelId: botConfig.fastModelId,
445
+ threadId: context.threadId ?? "",
446
+ channelId: context.channelId ?? "",
447
+ actorId: context.actorId ?? "",
448
+ runId: context.runId ?? ""
449
+ }
450
+ });
451
+ const summary = result.text.trim();
452
+ if (summary.length > 0) {
453
+ return summary.slice(0, 3500);
454
+ }
455
+ } catch (error) {
456
+ logWarn("conversation.compaction.summary.failed", {
457
+ "exception.message": error instanceof Error ? error.message : String(error),
458
+ "app.compaction_messages_covered": messages.length
459
+ });
460
+ }
461
+ return transcript.slice(0, 2800);
462
+ }
463
+ function getThreadTitleSourceMessage(conversation) {
464
+ let firstMessage;
465
+ for (const message of conversation.messages) {
466
+ if (!isHumanConversationMessage(message)) {
467
+ continue;
468
+ }
469
+ if (!firstMessage) {
470
+ firstMessage = message;
471
+ continue;
472
+ }
473
+ if (message.createdAtMs < firstMessage.createdAtMs) {
474
+ firstMessage = message;
475
+ continue;
476
+ }
477
+ if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
478
+ firstMessage = message;
479
+ }
480
+ }
481
+ return firstMessage;
482
+ }
483
+ async function compactConversationIfNeededWithDeps(conversation, context, deps) {
484
+ let estimatedTokens = estimateConversationContextTokens(conversation);
485
+ setSpanAttributes({
486
+ "app.context_tokens_estimated": estimatedTokens
487
+ });
488
+ const triggerTokens = getConversationContextCompactionTriggerTokens();
489
+ const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
490
+ while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
491
+ const compactCount = Math.min(
492
+ CONTEXT_COMPACTION_BATCH_SIZE,
493
+ conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
494
+ );
495
+ if (compactCount <= 0) {
496
+ break;
497
+ }
498
+ const compactedChunk = conversation.messages.slice(0, compactCount);
499
+ const summary = await summarizeConversationChunk(
500
+ compactedChunk,
501
+ conversation,
502
+ context,
503
+ deps
504
+ );
505
+ conversation.compactions.push({
506
+ id: generateConversationId("compaction"),
507
+ createdAtMs: Date.now(),
508
+ summary,
509
+ coveredMessageCount: compactedChunk.length
510
+ });
511
+ conversation.compactions = pruneCompactions(conversation.compactions);
512
+ conversation.messages = conversation.messages.slice(compactCount);
513
+ estimatedTokens = estimateConversationContextTokens(conversation);
514
+ setSpanAttributes({
515
+ "app.compaction_messages_covered": compactCount,
516
+ "app.compaction.trigger_tokens": triggerTokens,
517
+ "app.compaction.target_tokens": targetTokens,
518
+ "app.context_tokens_estimated": estimatedTokens
519
+ });
520
+ if (estimatedTokens <= targetTokens) {
521
+ break;
522
+ }
523
+ }
524
+ }
525
+ function createConversationMemoryService(deps) {
526
+ return {
527
+ compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps)
528
+ };
529
+ }
530
+ function isHumanConversationMessage(message) {
531
+ return message.role === "user" && message.author?.isBot !== true;
532
+ }
533
+ function getConversationMessageSlackTs(message) {
534
+ return parseSlackMessageTs(message.meta?.slackTs);
535
+ }
536
+
181
537
  // src/chat/conversations/messages.ts
182
538
  import { createHash } from "crypto";
183
539
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
184
540
 
541
+ // src/chat/services/short-title.ts
542
+ var SHORT_TITLE_MAX_LENGTH = 60;
543
+ var TITLE_PROMPTS = {
544
+ conversation: [
545
+ "Generate a concise 5-8 word conversation title from the first user message below.",
546
+ "Capture the user's main request.",
547
+ "Reply with ONLY the title, with no quotes or trailing punctuation."
548
+ ],
549
+ task: [
550
+ "Generate a concise 5-8 word task title from the task instruction below.",
551
+ "Capture the task's main purpose.",
552
+ "Reply with ONLY the title, with no quotes or trailing punctuation."
553
+ ]
554
+ };
555
+ function normalizeShortTitle(value) {
556
+ return value.replace(/\s+/g, " ").trim().slice(0, SHORT_TITLE_MAX_LENGTH);
557
+ }
558
+ function fallbackShortTitle(sourceText, fallback = "Untitled") {
559
+ const firstLine = sourceText.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0) ?? "";
560
+ return normalizeShortTitle(firstLine) || fallback;
561
+ }
562
+ async function generateShortTitle(args) {
563
+ const sourceText = args.sourceText.trim();
564
+ if (!sourceText) return void 0;
565
+ try {
566
+ const result = await args.completeText({
567
+ modelId: botConfig.fastModelId,
568
+ temperature: 0,
569
+ promptName: `junior.${args.kind}_title`,
570
+ messages: [
571
+ {
572
+ role: "user",
573
+ content: [
574
+ ...TITLE_PROMPTS[args.kind],
575
+ "",
576
+ args.kind === "conversation" ? `First user message: ${sourceText.slice(0, 500)}` : `Task instruction: ${sourceText.slice(0, 500)}`
577
+ ].join("\n"),
578
+ timestamp: Date.now()
579
+ }
580
+ ],
581
+ metadata: {
582
+ modelId: botConfig.fastModelId
583
+ }
584
+ });
585
+ const title = normalizeShortTitle(result.text);
586
+ return title || void 0;
587
+ } catch (error) {
588
+ logWarn(`${args.kind}.title.generation.failed`, {
589
+ "exception.message": error instanceof Error ? error.message : String(error)
590
+ });
591
+ return void 0;
592
+ }
593
+ }
594
+ async function resolveTaskTitle(args) {
595
+ const explicit = normalizeShortTitle(args.title ?? "");
596
+ if (explicit) return explicit;
597
+ return generateShortTitle({
598
+ completeText: args.completeText,
599
+ kind: "task",
600
+ sourceText: args.instruction
601
+ });
602
+ }
603
+
604
+ // src/chat/services/conversation-title.ts
605
+ var inFlightTitles = /* @__PURE__ */ new Map();
606
+ async function ensureConversationTitle(args) {
607
+ const pending = inFlightTitles.get(args.conversationId);
608
+ if (pending) {
609
+ return pending;
610
+ }
611
+ const work = ensureConversationTitleOnce(args).finally(() => {
612
+ inFlightTitles.delete(args.conversationId);
613
+ });
614
+ inFlightTitles.set(args.conversationId, work);
615
+ return work;
616
+ }
617
+ async function ensureConversationTitleOnce(args) {
618
+ if (!args.conversation.messages.some(isHumanConversationMessage)) {
619
+ return void 0;
620
+ }
621
+ const store = args.conversationStore ?? getConversationStore();
622
+ try {
623
+ const stored = await store.get({ conversationId: args.conversationId });
624
+ if (stored?.title?.trim()) {
625
+ return void 0;
626
+ }
627
+ if (stored?.lineage) {
628
+ return void 0;
629
+ }
630
+ const titleSourceMessage = getThreadTitleSourceMessage(args.conversation);
631
+ const sourceText = titleSourceMessage?.text.trim() ?? "";
632
+ if (!titleSourceMessage || !sourceText) {
633
+ return void 0;
634
+ }
635
+ let title;
636
+ try {
637
+ const generated = await generateShortTitle({
638
+ completeText,
639
+ kind: "conversation",
640
+ sourceText
641
+ });
642
+ title = generated ?? fallbackShortTitle(sourceText, "Conversation");
643
+ } catch (error) {
644
+ logWarn("conversation.title.generation.failed", {
645
+ "exception.message": error instanceof Error ? error.message : String(error)
646
+ });
647
+ title = fallbackShortTitle(sourceText, "Conversation");
648
+ }
649
+ const normalized = title.trim();
650
+ if (!normalized) {
651
+ return void 0;
652
+ }
653
+ const activityAtMs = titleSourceMessage.createdAtMs;
654
+ const nowMs = args.nowMs ?? Date.now();
655
+ try {
656
+ await store.recordActivity({
657
+ activityAtMs,
658
+ conversationId: args.conversationId,
659
+ nowMs,
660
+ title: normalized
661
+ });
662
+ } catch (error) {
663
+ logException(error, "conversation.title.persist.failed");
664
+ return void 0;
665
+ }
666
+ return normalized;
667
+ } catch (error) {
668
+ logException(error, "conversation.title.ensure.failed");
669
+ return void 0;
670
+ }
671
+ }
672
+ function scheduleConversationTitle(args) {
673
+ if (!args.conversationId) {
674
+ return;
675
+ }
676
+ void ensureConversationTitle({
677
+ conversation: args.conversation,
678
+ conversationId: args.conversationId
679
+ }).catch((error) => {
680
+ logException(error, "conversation.title.task.failed");
681
+ });
682
+ }
683
+ async function resolveConversationTitle(args) {
684
+ const pending = inFlightTitles.get(args.conversationId);
685
+ if (pending) {
686
+ const generated = await pending;
687
+ if (generated?.trim()) {
688
+ return generated.trim();
689
+ }
690
+ }
691
+ const store = args.conversationStore ?? getConversationStore();
692
+ const stored = await store.get({ conversationId: args.conversationId });
693
+ return stored?.title?.trim() || void 0;
694
+ }
695
+
185
696
  // src/chat/conversations/message-summaries.ts
186
697
  import { isDeepStrictEqual } from "util";
187
698
  function projectConversationMessageSummaries(events) {
@@ -325,6 +836,10 @@ async function persistConversationMessages(args) {
325
836
  ...args,
326
837
  conversationId: args.conversationId
327
838
  });
839
+ scheduleConversationTitle({
840
+ conversation: args.conversation,
841
+ conversationId: args.conversationId
842
+ });
328
843
  }
329
844
  async function appendConversationMessages(eventStore, args) {
330
845
  if (args.conversation.messages.length === 0) return;
@@ -397,6 +912,131 @@ var piContentMessageSchema = z.object({
397
912
  role: z.string().min(1)
398
913
  }).passthrough().transform((value) => value);
399
914
 
915
+ // src/chat/conversations/structured-events.ts
916
+ import {
917
+ conversationEventPresentationSchema,
918
+ defineConversationEvent
919
+ } from "@sentry/junior-plugin-api";
920
+ import { z as z2 } from "zod";
921
+ var JUNIOR_NATIVE_EVENT_NAMESPACE = "junior";
922
+ var authenticationEventContentSchema = z2.object({
923
+ accountLabel: z2.string().trim().min(1).max(120).optional(),
924
+ actorId: z2.string().min(1),
925
+ authorizationId: z2.string().min(1).optional(),
926
+ provider: z2.string().regex(/^[a-z][a-z0-9-]*$/),
927
+ providerLabel: z2.string().trim().min(1).max(80).optional()
928
+ }).strict();
929
+ var agentsInstructionsUpdatedContentSchema = z2.object({
930
+ action: z2.enum(["loaded", "replaced", "cleared"]),
931
+ directory: z2.string().trim().min(1).max(500).optional(),
932
+ fingerprint: z2.string().trim().min(1).max(128),
933
+ sources: z2.array(
934
+ z2.object({
935
+ content: z2.string().min(1).max(32 * 1024),
936
+ path: z2.string().trim().min(1).max(500)
937
+ }).strict()
938
+ ).max(32),
939
+ textBytes: z2.number().int().nonnegative().optional()
940
+ }).strict();
941
+ function providerTitle(content) {
942
+ return content.providerLabel?.trim() || content.provider;
943
+ }
944
+ function agentsInstructionsTitle(action) {
945
+ if (action === "loaded") return "Loaded AGENTS.md";
946
+ if (action === "replaced") return "Updated AGENTS.md";
947
+ return "Cleared AGENTS.md";
948
+ }
949
+ function sourceFilename(sourcePath) {
950
+ return sourcePath.split("/").at(-1) || sourcePath;
951
+ }
952
+ function formatBytes(bytes) {
953
+ if (bytes < 1024) return `${bytes} bytes`;
954
+ const kilobytes = bytes / 1024;
955
+ return `${Number.isInteger(kilobytes) ? kilobytes : kilobytes.toFixed(1)} KB`;
956
+ }
957
+ function agentsInstructionsPreview(content) {
958
+ const source = content.sources[0];
959
+ if (!source) return void 0;
960
+ const filename = sourceFilename(source.path);
961
+ const label = content.sources.length === 1 ? filename : `${content.sources.length} AGENTS.md files`;
962
+ return typeof content.textBytes === "number" ? `${label} \xB7 ${formatBytes(content.textBytes)}` : label;
963
+ }
964
+ var authenticationLinkedEvent = defineConversationEvent({
965
+ name: "authentication_linked",
966
+ version: 1,
967
+ schema: authenticationEventContentSchema,
968
+ renderEvent(event) {
969
+ const provider = providerTitle(event);
970
+ return {
971
+ icon: "link",
972
+ title: `${provider} connected`,
973
+ ...event.accountLabel ? { preview: `Connected as \`${event.accountLabel}\`` } : {},
974
+ details: [
975
+ {
976
+ title: `${provider} connected`,
977
+ ...event.accountLabel ? { description: `Connected as \`${event.accountLabel}\`` } : {},
978
+ metadata: [event.provider]
979
+ }
980
+ ]
981
+ };
982
+ }
983
+ });
984
+ var authenticationUnlinkedEvent = defineConversationEvent({
985
+ name: "authentication_unlinked",
986
+ version: 1,
987
+ schema: authenticationEventContentSchema,
988
+ renderEvent(event) {
989
+ const provider = providerTitle(event);
990
+ return {
991
+ icon: "key",
992
+ title: `${provider} disconnected`,
993
+ details: [
994
+ {
995
+ title: `${provider} disconnected`,
996
+ metadata: [event.provider]
997
+ }
998
+ ]
999
+ };
1000
+ }
1001
+ });
1002
+ var agentsInstructionsUpdatedEvent = defineConversationEvent({
1003
+ name: "agents_instructions_updated",
1004
+ version: 1,
1005
+ schema: agentsInstructionsUpdatedContentSchema,
1006
+ renderEvent(event) {
1007
+ const title = agentsInstructionsTitle(event.action);
1008
+ const preview = agentsInstructionsPreview(event);
1009
+ return {
1010
+ icon: "brain",
1011
+ title,
1012
+ ...preview ? { preview } : {},
1013
+ ...event.sources.length > 0 ? {
1014
+ details: event.sources.map((source) => ({
1015
+ title: sourceFilename(source.path),
1016
+ content: source.content
1017
+ }))
1018
+ } : {}
1019
+ };
1020
+ }
1021
+ });
1022
+ var NATIVE_EVENT_DEFINITIONS = [
1023
+ authenticationLinkedEvent,
1024
+ authenticationUnlinkedEvent,
1025
+ agentsInstructionsUpdatedEvent
1026
+ ];
1027
+ function getJuniorNativeEventDefinition(args) {
1028
+ return NATIVE_EVENT_DEFINITIONS.find(
1029
+ (definition) => definition.eventName === args.name && definition.version === args.version
1030
+ );
1031
+ }
1032
+ function renderJuniorNativeConversationEvent(args) {
1033
+ if (args.namespace !== JUNIOR_NATIVE_EVENT_NAMESPACE) return void 0;
1034
+ const definition = getJuniorNativeEventDefinition(args);
1035
+ if (!definition) return void 0;
1036
+ const presentation = definition.renderEvent(definition.parse(args.content));
1037
+ return presentation === void 0 ? void 0 : conversationEventPresentationSchema.parse(presentation);
1038
+ }
1039
+
400
1040
  // src/chat/pi/conversation-events.ts
401
1041
  function authorizationObservationMessage(data, createdAtMs) {
402
1042
  const label = data.kind === "mcp" ? "MCP authorization" : "Authorization";
@@ -1131,7 +1771,7 @@ async function persistWithRetry(persist) {
1131
1771
  }
1132
1772
 
1133
1773
  // src/chat/task-execution/turn-cursor.ts
1134
- import { z as z2 } from "zod";
1774
+ import { z as z3 } from "zod";
1135
1775
 
1136
1776
  // src/chat/repository-instructions.ts
1137
1777
  import { createHash as createHash2 } from "crypto";
@@ -1139,6 +1779,16 @@ import path2 from "path";
1139
1779
 
1140
1780
  // src/chat/tools/sandbox/file-utils.ts
1141
1781
  import path from "path";
1782
+
1783
+ // src/chat/tools/execution/tool-input-error.ts
1784
+ var ToolInputError = class extends Error {
1785
+ constructor(message, options) {
1786
+ super(message, options);
1787
+ this.name = "ToolInputError";
1788
+ }
1789
+ };
1790
+
1791
+ // src/chat/tools/sandbox/file-utils.ts
1142
1792
  var MAX_TEXT_CHARS = 6e4;
1143
1793
  var RIPGREP_EXCLUDED_GLOBS = [
1144
1794
  "!**/.git/**",
@@ -1146,7 +1796,7 @@ var RIPGREP_EXCLUDED_GLOBS = [
1146
1796
  ];
1147
1797
  var DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
1148
1798
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
1149
- function positiveInteger(value) {
1799
+ function positiveInteger2(value) {
1150
1800
  if (typeof value !== "number" || !Number.isFinite(value)) {
1151
1801
  return void 0;
1152
1802
  }
@@ -1524,63 +2174,64 @@ function definedProps(values) {
1524
2174
  Object.entries(values).filter(([, value]) => value !== void 0)
1525
2175
  );
1526
2176
  }
1527
- var turnStatusSchema = z2.enum([
2177
+ var turnStatusSchema = z3.enum([
1528
2178
  "running",
1529
2179
  "paused",
1530
2180
  "completed",
1531
2181
  "failed",
1532
2182
  "abandoned"
1533
2183
  ]);
1534
- var turnSurfaceSchema = z2.enum([
2184
+ var turnSurfaceSchema = z3.enum([
1535
2185
  "slack",
1536
2186
  "api",
1537
2187
  "scheduler",
1538
2188
  "internal"
1539
2189
  ]);
1540
- var turnPauseReasonSchema = z2.enum([
2190
+ var turnPauseReasonSchema = z3.enum([
1541
2191
  "timeout",
1542
2192
  "auth",
1543
2193
  "yield",
1544
2194
  "retry"
1545
2195
  ]);
1546
- var nonNegativeNumberSchema = z2.number().finite().nonnegative();
1547
- var seqCursorSchema = z2.number().int().min(-1);
1548
- var storedTurnSummarySchema = z2.object({
1549
- schemaVersion: z2.literal(TURN_CURSOR_SCHEMA_VERSION),
1550
- version: z2.number().int().nonnegative(),
1551
- conversationId: z2.string().min(1),
1552
- dispatchId: z2.string().min(1).optional(),
1553
- dispatchOutcome: z2.enum(["blocked", "completed", "failed"]).optional(),
1554
- resultMessageId: z2.string().min(1).optional(),
2196
+ var nonNegativeNumberSchema = z3.number().finite().nonnegative();
2197
+ var seqCursorSchema = z3.number().int().min(-1);
2198
+ var storedTurnSummarySchema = z3.object({
2199
+ schemaVersion: z3.literal(TURN_CURSOR_SCHEMA_VERSION),
2200
+ version: z3.number().int().nonnegative(),
2201
+ conversationId: z3.string().min(1),
2202
+ dispatchId: z3.string().min(1).optional(),
2203
+ dispatchOutcome: z3.enum(["blocked", "completed", "failed"]).optional(),
2204
+ resultMessageId: z3.string().min(1).optional(),
1555
2205
  resumeReason: turnPauseReasonSchema.optional(),
1556
- turnId: z2.string().min(1),
1557
- sliceId: z2.number().int().nonnegative(),
2206
+ turnId: z3.string().min(1),
2207
+ sliceId: z3.number().int().nonnegative(),
1558
2208
  state: turnStatusSchema,
1559
2209
  surface: turnSurfaceSchema.optional(),
1560
2210
  updatedAtMs: nonNegativeNumberSchema
1561
2211
  }).strip();
1562
- var storedTurnRecordSchema = z2.object({
1563
- schemaVersion: z2.literal(TURN_CURSOR_SCHEMA_VERSION),
1564
- version: z2.number().int().nonnegative(),
1565
- conversationId: z2.string().min(1),
1566
- dispatchId: z2.string().min(1).optional(),
1567
- dispatchOutcome: z2.enum(["blocked", "completed", "failed"]).optional(),
1568
- resultMessageId: z2.string().min(1).optional(),
2212
+ var storedTurnRecordSchema = z3.object({
2213
+ schemaVersion: z3.literal(TURN_CURSOR_SCHEMA_VERSION),
2214
+ version: z3.number().int().nonnegative(),
2215
+ conversationId: z3.string().min(1),
2216
+ dispatchId: z3.string().min(1).optional(),
2217
+ dispatchOutcome: z3.enum(["blocked", "completed", "failed"]).optional(),
2218
+ resultMessageId: z3.string().min(1).optional(),
1569
2219
  lastProgressAtMs: nonNegativeNumberSchema,
1570
2220
  resumeReason: turnPauseReasonSchema.optional(),
1571
- resumedFromSliceId: z2.number().int().nonnegative().optional(),
1572
- turnId: z2.string().min(1),
1573
- sliceId: z2.number().int().nonnegative(),
2221
+ publishExternally: z3.boolean().optional(),
2222
+ resumedFromSliceId: z3.number().int().nonnegative().optional(),
2223
+ turnId: z3.string().min(1),
2224
+ sliceId: z3.number().int().nonnegative(),
1574
2225
  startedAtMs: nonNegativeNumberSchema,
1575
2226
  state: turnStatusSchema,
1576
2227
  surface: turnSurfaceSchema.optional(),
1577
- traceId: z2.string().optional(),
2228
+ traceId: z3.string().optional(),
1578
2229
  updatedAtMs: nonNegativeNumberSchema,
1579
2230
  committedSeq: seqCursorSchema,
1580
- historyVersion: z2.number().int().nonnegative().optional(),
1581
- errorMessage: z2.string().optional(),
2231
+ historyVersion: z3.number().int().nonnegative().optional(),
2232
+ errorMessage: z3.string().optional(),
1582
2233
  turnStartSeq: seqCursorSchema.optional(),
1583
- runtimeContext: z2.array(piMessageSchema).optional()
2234
+ runtimeContext: z3.array(piMessageSchema).optional()
1584
2235
  }).strip();
1585
2236
  function conversationExecutionFromSummary(summary) {
1586
2237
  const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
@@ -1629,7 +2280,7 @@ async function recordConversationActivityMetadata(args) {
1629
2280
  });
1630
2281
  const isChild = Boolean(conversation?.lineage);
1631
2282
  const destination = isChild ? void 0 : args.destination;
1632
- const activitySource = isChild ? "internal" : destination?.platform === "local" ? "local" : destination ? args.summary.surface : void 0;
2283
+ const activitySource = isChild ? "internal" : args.source?.platform === "web" ? "web" : destination?.platform === "local" ? "local" : destination ? args.summary.surface : void 0;
1633
2284
  await conversationStore.recordActivity({
1634
2285
  activityAtMs: args.summary.updatedAtMs,
1635
2286
  channelName: args.summary.channelName,
@@ -1681,6 +2332,7 @@ function materializeTurnRecord(stored, piProjection, turnStartMessageIndex, rest
1681
2332
  dispatchOutcome: stored.dispatchOutcome,
1682
2333
  errorMessage: stored.errorMessage,
1683
2334
  resumeReason: stored.resumeReason,
2335
+ publishExternally: stored.publishExternally,
1684
2336
  resultMessageId: stored.resultMessageId,
1685
2337
  resumedFromSliceId: stored.resumedFromSliceId,
1686
2338
  surface: stored.surface,
@@ -1790,6 +2442,7 @@ function buildStoredRecord(args) {
1790
2442
  errorMessage: args.errorMessage,
1791
2443
  historyVersion: args.historyVersion,
1792
2444
  resumeReason: args.resumeReason,
2445
+ publishExternally: args.publishExternally,
1793
2446
  resultMessageId: args.resultMessageId,
1794
2447
  resumedFromSliceId: args.resumedFromSliceId,
1795
2448
  runtimeContext: args.runtimeContext && args.runtimeContext.length > 0 ? args.runtimeContext : void 0,
@@ -1877,6 +2530,7 @@ async function updateTurnState(args) {
1877
2530
  errorMessage: args.errorMessage ?? args.existing.errorMessage,
1878
2531
  historyVersion: parsed.historyVersion,
1879
2532
  resumeReason: args.existing.resumeReason,
2533
+ publishExternally: args.existing.publishExternally,
1880
2534
  resultMessageId: args.resultMessageId ?? args.existing.resultMessageId,
1881
2535
  resumedFromSliceId: args.existing.resumedFromSliceId,
1882
2536
  surface: args.existing.surface,
@@ -1964,6 +2618,7 @@ async function upsertTurnRecordLocked(args, fence) {
1964
2618
  errorMessage: args.errorMessage,
1965
2619
  lastProgressAtMs: args.lastProgressAtMs,
1966
2620
  resumeReason: args.resumeReason,
2621
+ publishExternally: args.publishExternally ?? existingRecord?.publishExternally,
1967
2622
  resultMessageId: args.resultMessageId ?? existingRecord?.resultMessageId,
1968
2623
  resumedFromSliceId: args.resumedFromSliceId,
1969
2624
  runtimeContext: args.state === "running" || args.state === "paused" ? retainedRuntimeContext : void 0,
@@ -2120,6 +2775,96 @@ async function failTurnRecord(args) {
2120
2775
  );
2121
2776
  }
2122
2777
 
2778
+ // src/chat/usage.ts
2779
+ var agentTurnUsageSchema = usageSchema;
2780
+ var COMPONENT_USAGE_FIELDS = [
2781
+ "inputTokens",
2782
+ "outputTokens",
2783
+ "cachedInputTokens",
2784
+ "cacheCreationTokens"
2785
+ ];
2786
+ var COST_COMPONENT_FIELDS = [
2787
+ "input",
2788
+ "output",
2789
+ "cacheRead",
2790
+ "cacheWrite"
2791
+ ];
2792
+ function hasAgentTurnUsage(usage) {
2793
+ return Boolean(
2794
+ usage && (Object.entries(usage).some(
2795
+ ([field, value]) => field !== "cost" && typeof value === "number" && Number.isFinite(value)
2796
+ ) || Object.values(usage.cost ?? {}).some(
2797
+ (value) => typeof value === "number" && Number.isFinite(value)
2798
+ ))
2799
+ );
2800
+ }
2801
+ function getFiniteCount(value) {
2802
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
2803
+ }
2804
+ function getFiniteCost(value) {
2805
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : void 0;
2806
+ }
2807
+ function addCost(left, right) {
2808
+ return Math.round(((left ?? 0) + right) * 1e12) / 1e12;
2809
+ }
2810
+ function getComponentTotal(usage) {
2811
+ let total;
2812
+ for (const field of COMPONENT_USAGE_FIELDS) {
2813
+ const value = getFiniteCount(usage[field]);
2814
+ if (value === void 0) continue;
2815
+ total = (total ?? 0) + value;
2816
+ }
2817
+ return total;
2818
+ }
2819
+ function addAgentTurnUsage(...usages) {
2820
+ const components = {};
2821
+ let componentTotal;
2822
+ let totalOnlyTokens;
2823
+ let reasoningTokens;
2824
+ const cost = {};
2825
+ for (const usage of usages) {
2826
+ if (!usage) continue;
2827
+ const reasoning = getFiniteCount(usage.reasoningTokens);
2828
+ if (reasoning !== void 0) {
2829
+ reasoningTokens = (reasoningTokens ?? 0) + reasoning;
2830
+ }
2831
+ if (usage.cost) {
2832
+ for (const field of [...COST_COMPONENT_FIELDS, "total"]) {
2833
+ const value = getFiniteCost(usage.cost[field]);
2834
+ if (value === void 0) continue;
2835
+ cost[field] = addCost(cost[field], value);
2836
+ }
2837
+ }
2838
+ const usageComponentTotal = getComponentTotal(usage);
2839
+ if (usageComponentTotal !== void 0) {
2840
+ componentTotal = (componentTotal ?? 0) + usageComponentTotal;
2841
+ for (const field of COMPONENT_USAGE_FIELDS) {
2842
+ const value = getFiniteCount(usage[field]);
2843
+ if (value === void 0) continue;
2844
+ components[field] = (components[field] ?? 0) + value;
2845
+ }
2846
+ continue;
2847
+ }
2848
+ const totalTokens = getFiniteCount(usage.totalTokens);
2849
+ if (totalTokens !== void 0) {
2850
+ totalOnlyTokens = (totalOnlyTokens ?? 0) + totalTokens;
2851
+ }
2852
+ }
2853
+ if (totalOnlyTokens !== void 0) {
2854
+ return {
2855
+ totalTokens: totalOnlyTokens + (componentTotal ?? 0),
2856
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
2857
+ ...Object.keys(cost).length > 0 ? { cost } : {}
2858
+ };
2859
+ }
2860
+ const result = {
2861
+ ...components,
2862
+ ...reasoningTokens !== void 0 ? { reasoningTokens } : {},
2863
+ ...Object.keys(cost).length > 0 ? { cost } : {}
2864
+ };
2865
+ return hasAgentTurnUsage(result) ? result : void 0;
2866
+ }
2867
+
2123
2868
  // src/chat/services/turn-limit.ts
2124
2869
  var TurnSliceLimitExceededError = class extends Error {
2125
2870
  constructor(maxSlices) {
@@ -2180,6 +2925,7 @@ function sharedWrite(args, latest) {
2180
2925
  destination: args.destination,
2181
2926
  destinationVisibility: args.destinationVisibility,
2182
2927
  dispatchId: args.dispatchId ?? latest?.dispatchId,
2928
+ publishExternally: args.publishExternally ?? latest?.publishExternally,
2183
2929
  source: args.source,
2184
2930
  surface: args.surface ?? latest?.surface,
2185
2931
  traceId: getActiveTraceId() ?? latest?.traceId,
@@ -2371,6 +3117,7 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
2371
3117
  }
2372
3118
 
2373
3119
  export {
3120
+ renderJuniorNativeConversationEvent,
2374
3121
  TURN_CONTEXT_TAG,
2375
3122
  isAssistantMessage,
2376
3123
  isToolResultMessage,
@@ -2388,6 +3135,24 @@ export {
2388
3135
  stripRuntimeTurnContext,
2389
3136
  historyItemFromPiMessage,
2390
3137
  piMessageFromHistoryItem,
3138
+ estimateTextTokens,
3139
+ getAgentContextCompactionTriggerTokens,
3140
+ getAgentContextInputLimitTokens,
3141
+ normalizeConversationText,
3142
+ estimateConversationContextTokens,
3143
+ upsertConversationMessage,
3144
+ recordDeliveredAssistantMessage,
3145
+ turnHasReply,
3146
+ markConversationMessage,
3147
+ buildConversationContext,
3148
+ createConversationMemoryService,
3149
+ isHumanConversationMessage,
3150
+ getConversationMessageSlackTs,
3151
+ SHORT_TITLE_MAX_LENGTH,
3152
+ fallbackShortTitle,
3153
+ generateShortTitle,
3154
+ resolveTaskTitle,
3155
+ resolveConversationTitle,
2391
3156
  persistConversationMessageSummaries,
2392
3157
  hydrateConversationMessages,
2393
3158
  persistConversationMessages,
@@ -2407,10 +3172,11 @@ export {
2407
3172
  recordTurnRoute,
2408
3173
  recordToolExecutionStarted,
2409
3174
  recordGuardianActionReviewed,
3175
+ ToolInputError,
2410
3176
  MAX_TEXT_CHARS,
2411
3177
  RIPGREP_EXCLUDED_GLOBS,
2412
3178
  DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
2413
- positiveInteger,
3179
+ positiveInteger2 as positiveInteger,
2414
3180
  normalizeToLf,
2415
3181
  truncateText,
2416
3182
  isMissingPathError,
@@ -2445,6 +3211,9 @@ export {
2445
3211
  sanitizeAssistantText,
2446
3212
  decideReply,
2447
3213
  getAssistantReplyText,
3214
+ agentTurnUsageSchema,
3215
+ hasAgentTurnUsage,
3216
+ addAgentTurnUsage,
2448
3217
  TurnSliceLimitExceededError,
2449
3218
  buildTurnLimitResponse,
2450
3219
  persistWithRetry,