@sentry/junior 0.141.0 → 0.143.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 (70) hide show
  1. package/dist/{agent-hooks-HIRVKUQJ.js → agent-hooks-2EP3QS47.js} +10 -10
  2. package/dist/api/schema/task.d.ts +8 -0
  3. package/dist/api/schema.js +1 -1
  4. package/dist/api.js +34 -22
  5. package/dist/app.js +62 -91
  6. package/dist/{catalog-runtime-XVLED5UH.js → catalog-runtime-IAVW4W3B.js} +5 -5
  7. package/dist/chat/event-tasks/types.d.ts +7 -1
  8. package/dist/chat/runtime/turn.d.ts +0 -4
  9. package/dist/chat/scheduled-tasks/tools/create.d.ts +1 -0
  10. package/dist/chat/scheduled-tasks/types.d.ts +5 -0
  11. package/dist/chat/services/conversation-memory.d.ts +2 -1
  12. package/dist/chat/services/short-title.d.ts +25 -0
  13. package/dist/chat/state/conversation.d.ts +6 -17
  14. package/dist/chat/tools/create-event-task.d.ts +1 -0
  15. package/dist/{chunk-3H42HMA2.js → chunk-2KLMGRRQ.js} +5 -1
  16. package/dist/{chunk-HN4UZTG7.js → chunk-2T5CHXRZ.js} +2 -2
  17. package/dist/{chunk-PGJUJCOQ.js → chunk-5YJBS4I5.js} +1 -1
  18. package/dist/{chunk-BBLO66XT.js → chunk-7GVX5SOG.js} +2 -2
  19. package/dist/{chunk-2DOVDDZJ.js → chunk-A3XPSABC.js} +3 -3
  20. package/dist/{chunk-FZFJZZR3.js → chunk-AEFJN7OK.js} +4 -4
  21. package/dist/{chunk-DK3GQWJK.js → chunk-ANG7IWAI.js} +6 -6
  22. package/dist/{chunk-LMGKSDO4.js → chunk-BJ6ISBOA.js} +1 -1
  23. package/dist/{chunk-H4WNQFBH.js → chunk-F6RWNJBK.js} +5 -5
  24. package/dist/{chunk-UFEU4UX6.js → chunk-GZRWULXF.js} +1 -1
  25. package/dist/{chunk-DVIJ6DP6.js → chunk-I25V3XRN.js} +2 -2
  26. package/dist/{chunk-A2O7SGK5.js → chunk-I4NQNZVW.js} +6 -6
  27. package/dist/{chunk-DKH4WGZU.js → chunk-I5PIM72M.js} +341 -16
  28. package/dist/{chunk-FWXSPJRN.js → chunk-LSBQHDI2.js} +1 -1
  29. package/dist/{chunk-6U44QGXL.js → chunk-M2T7MHQJ.js} +1 -1
  30. package/dist/{chunk-5MFCEKR6.js → chunk-NAINBOZQ.js} +3 -3
  31. package/dist/{chunk-EYD523BV.js → chunk-NVFVM67W.js} +1 -1
  32. package/dist/{chunk-43OPSFUY.js → chunk-OB7ZJK7Y.js} +1 -1
  33. package/dist/{chunk-HQAFIN6O.js → chunk-OG5JQHKR.js} +1 -1
  34. package/dist/{chunk-YWM6MJT4.js → chunk-OGQGJAD7.js} +2 -0
  35. package/dist/{chunk-PIQKV7IR.js → chunk-PBV7VXUA.js} +10 -451
  36. package/dist/{chunk-LIVSCWI5.js → chunk-QFWXGUAH.js} +23 -26
  37. package/dist/{chunk-UONUDOJP.js → chunk-RABHACKG.js} +7 -7
  38. package/dist/{chunk-7ULXCNOZ.js → chunk-SNBTWIQO.js} +6 -6
  39. package/dist/chunk-STX5WIH6.js +75 -0
  40. package/dist/{chunk-44NWPKY3.js → chunk-TPJK6AVJ.js} +1 -1
  41. package/dist/{chunk-6FRAWWGR.js → chunk-U5ATCOHE.js} +2 -2
  42. package/dist/{chunk-5LPWHC7Q.js → chunk-UFCA4EZB.js} +80 -46
  43. package/dist/chunk-WJHWOAVZ.js +67 -0
  44. package/dist/{chunk-NFL4FRIU.js → chunk-WM4PRM7W.js} +4 -4
  45. package/dist/{chunk-OFQBDHVG.js → chunk-WSWVWP2V.js} +1 -1
  46. package/dist/{chunk-DBKS7JR2.js → chunk-X5SC2UGE.js} +3 -3
  47. package/dist/{chunk-OHO46E7F.js → chunk-XYJNNZYR.js} +88 -36
  48. package/dist/cli/chat.js +32 -30
  49. package/dist/cli/check.js +6 -6
  50. package/dist/cli/plugins.js +13 -13
  51. package/dist/cli/snapshot-create.js +9 -9
  52. package/dist/cli/upgrade.js +7 -7
  53. package/dist/db/schema/event-tasks.d.ts +20 -52
  54. package/dist/db/schema/scheduled-tasks.d.ts +17 -0
  55. package/dist/db/schema.d.ts +36 -52
  56. package/dist/{db-PSMQZR4P.js → db-RBXBVEVO.js} +7 -7
  57. package/dist/instrumentation.js +4 -4
  58. package/dist/nitro.js +2 -2
  59. package/dist/{oauth-callback-server-XZUK2AT6.js → oauth-callback-server-LRB6BX6C.js} +23 -21
  60. package/dist/{runner-5EKJCQWP.js → runner-WK5VUOW6.js} +24 -25
  61. package/dist/{skills-2PRM42SE.js → skills-MJWRME3A.js} +6 -6
  62. package/dist/{spawn-UGTLRTFP.js → spawn-ZCW6UOAA.js} +17 -17
  63. package/dist/{validation-XLXAIRTR.js → validation-556WSWVU.js} +6 -6
  64. package/dist/version.js +1 -1
  65. package/dist/{work-CNTNEQ2Q.js → work-W6IXSPY3.js} +16 -16
  66. package/dist/{worker-BGW7CKZ7.js → worker-5KM346HH.js} +10 -10
  67. package/migrations/0023_task_title_columns.sql +2 -0
  68. package/migrations/meta/0023_snapshot.json +2198 -0
  69. package/migrations/meta/_journal.json +7 -0
  70. package/package.json +5 -5
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createAndEnqueueAgentInvocation
3
- } from "./chunk-A2O7SGK5.js";
3
+ } from "./chunk-I4NQNZVW.js";
4
4
  import {
5
5
  actorFromRouting,
6
6
  toolInvocationDestination
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getChatConfig
3
- } from "./chunk-BBLO66XT.js";
3
+ } from "./chunk-7GVX5SOG.js";
4
4
 
5
5
  // src/chat/state/adapter.ts
6
6
  import { createMemoryState } from "@chat-adapter/state-memory";
@@ -5,12 +5,12 @@ import {
5
5
  juniorConversations,
6
6
  juniorIdentities,
7
7
  juniorUsers
8
- } from "./chunk-3H42HMA2.js";
8
+ } from "./chunk-2KLMGRRQ.js";
9
9
  import {
10
10
  TURN_REASONING_LEVELS,
11
11
  getChatConfig,
12
12
  modelProfileSchema
13
- } from "./chunk-BBLO66XT.js";
13
+ } from "./chunk-7GVX5SOG.js";
14
14
  import {
15
15
  juniorDestinations
16
16
  } from "./chunk-VFUK3X5B.js";
@@ -20,7 +20,7 @@ import {
20
20
  } from "./chunk-F3B2YJ76.js";
21
21
  import {
22
22
  normalizeIdentityEmail
23
- } from "./chunk-UFEU4UX6.js";
23
+ } from "./chunk-GZRWULXF.js";
24
24
 
25
25
  // src/chat/conversations/sql/store.ts
26
26
  import { randomUUID as randomUUID2 } from "crypto";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createPluginCatalogRuntime
3
- } from "./chunk-LMGKSDO4.js";
3
+ } from "./chunk-BJ6ISBOA.js";
4
4
 
5
5
  // src/chat/plugins/catalog-runtime.ts
6
6
  var pluginCatalogRuntime = createPluginCatalogRuntime();
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@sentry/junior",
4
- version: "0.141.0",
4
+ version: "0.143.0",
5
5
  private: false,
6
6
  publishConfig: {
7
7
  access: "public"
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-F3B2YJ76.js";
6
6
  import {
7
7
  toOptionalString
8
- } from "./chunk-UFEU4UX6.js";
8
+ } from "./chunk-GZRWULXF.js";
9
9
 
10
10
  // src/chat/slack/context.ts
11
11
  import { z } from "zod";
@@ -609,6 +609,8 @@ var taskSummaryBaseSchema = z7.object({
609
609
  lastRunAt: z7.string().datetime().optional(),
610
610
  ownedByViewer: z7.boolean(),
611
611
  runsLast7Days: z7.number().int().nonnegative(),
612
+ /** Short display title; falls back from instruction when unset. */
613
+ title: z7.string().min(1),
612
614
  totalRuns: z7.number().int().nonnegative()
613
615
  });
614
616
  var scheduledTaskSummarySchema = taskSummaryBaseSchema.extend({
@@ -21,35 +21,28 @@ import {
21
21
  instructionProvenanceFor,
22
22
  sanitizePostgresJson,
23
23
  withConversationEventLock
24
- } from "./chunk-5MFCEKR6.js";
24
+ } from "./chunk-NAINBOZQ.js";
25
25
  import {
26
26
  SANDBOX_WORKSPACE_ROOT,
27
27
  sleep
28
28
  } from "./chunk-EM225ITJ.js";
29
29
  import {
30
30
  getStateAdapter
31
- } from "./chunk-6U44QGXL.js";
31
+ } from "./chunk-M2T7MHQJ.js";
32
32
  import {
33
33
  botConfig,
34
- escapeXml,
35
- resolveGatewayModel,
36
34
  unwrapCurrentInstruction
37
- } from "./chunk-BBLO66XT.js";
35
+ } from "./chunk-7GVX5SOG.js";
38
36
  import {
39
37
  toStoredSlackActor
40
38
  } from "./chunk-HZF5P5UN.js";
41
- import {
42
- parseSlackMessageTs
43
- } from "./chunk-F3B2YJ76.js";
44
39
  import {
45
40
  getActiveTraceId,
46
41
  isRecord,
47
42
  logException,
48
- logWarn,
49
- setSpanAttributes,
50
43
  toOptionalNumber,
51
44
  toOptionalString
52
- } from "./chunk-UFEU4UX6.js";
45
+ } from "./chunk-GZRWULXF.js";
53
46
 
54
47
  // src/chat/turn-context-tag.ts
55
48
  var TURN_CONTEXT_TAG = "runtime-turn-context";
@@ -188,392 +181,6 @@ function stripRuntimeTurnContext(messages) {
188
181
  });
189
182
  }
190
183
 
191
- // src/chat/services/context-budget.ts
192
- var COMPACTION_TRIGGER_RATIO = 0.9;
193
- var CONTEXT_INPUT_LIMIT_RATIO = 0.95;
194
- var COMPACTION_TARGET_RATIO = 0.8;
195
- var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
196
- function positiveInteger(value, fallback) {
197
- return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
198
- }
199
- function estimateTextTokens(text) {
200
- return Math.ceil(text.length / 4);
201
- }
202
- function calculateContextCompactionTriggerTokens(model) {
203
- const contextWindow = positiveInteger(
204
- model.contextWindow,
205
- FALLBACK_CONTEXT_WINDOW_TOKENS
206
- );
207
- return Math.max(1, Math.floor(contextWindow * COMPACTION_TRIGGER_RATIO));
208
- }
209
- function calculateContextInputLimitTokens(model) {
210
- const contextWindow = positiveInteger(
211
- model.contextWindow,
212
- FALLBACK_CONTEXT_WINDOW_TOKENS
213
- );
214
- return Math.max(1, Math.floor(contextWindow * CONTEXT_INPUT_LIMIT_RATIO));
215
- }
216
- function calculateContextCompactionTargetTokens(triggerTokens) {
217
- return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
218
- }
219
- function getModelContextBudget(modelId) {
220
- const model = resolveGatewayModel(modelId);
221
- const advertisedContextWindow = positiveInteger(
222
- model.contextWindow,
223
- FALLBACK_CONTEXT_WINDOW_TOKENS
224
- );
225
- return {
226
- contextWindow: Math.min(
227
- botConfig.contextWindowTokens,
228
- advertisedContextWindow
229
- )
230
- };
231
- }
232
- function getAgentContextCompactionTriggerTokens(modelId) {
233
- return calculateContextCompactionTriggerTokens(
234
- getModelContextBudget(modelId)
235
- );
236
- }
237
- function getAgentContextInputLimitTokens(modelId) {
238
- return calculateContextInputLimitTokens(getModelContextBudget(modelId));
239
- }
240
- function getConversationContextCompactionTriggerTokens() {
241
- return calculateContextCompactionTriggerTokens(
242
- getModelContextBudget(botConfig.fastModelId)
243
- );
244
- }
245
-
246
- // src/chat/services/conversation-memory.ts
247
- var CONTEXT_MIN_LIVE_MESSAGES = 12;
248
- var CONTEXT_COMPACTION_BATCH_SIZE = 24;
249
- var CONTEXT_MAX_COMPACTIONS = 16;
250
- var CONTEXT_MAX_MESSAGE_CHARS = 3200;
251
- var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
252
- function generateConversationId(prefix) {
253
- return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
254
- }
255
- function normalizeConversationText(text) {
256
- return text.replace(/\r\n?/g, "\n").trim();
257
- }
258
- function buildImageContextSuffix(message, conversation) {
259
- const byFileId = conversation?.vision.byFileId;
260
- const imageFileIds = message.meta?.imageFileIds ?? [];
261
- if (!byFileId || imageFileIds.length === 0) {
262
- return "";
263
- }
264
- const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
265
- if (summaries.length === 0) {
266
- return "";
267
- }
268
- return ` [image context: ${summaries.join(" | ")}]`;
269
- }
270
- function renderConversationMessageLine(message, conversation) {
271
- const displayName = conversationAuthorDisplayName(message);
272
- const text = message.text.replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
273
- const markers = [];
274
- if (message.meta?.replied === false) {
275
- markers.push(
276
- `assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
277
- );
278
- }
279
- if (message.meta?.explicitMention) {
280
- markers.push("explicit mention");
281
- }
282
- const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
283
- const imageContext = buildImageContextSuffix(message, conversation);
284
- return `[${message.role}] ${displayName}: ${text}${imageContext}${markerSuffix}`;
285
- }
286
- function conversationAuthorDisplayName(message) {
287
- const author = message.author;
288
- const fullName = authorDisplayField(author?.fullName, author?.userId);
289
- const userName = authorDisplayField(author?.userName, author?.userId);
290
- return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
291
- }
292
- function authorDisplayField(value, userId) {
293
- if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
294
- return void 0;
295
- }
296
- return value;
297
- }
298
- function updateConversationStats(conversation) {
299
- const contextText = buildConversationContext(conversation);
300
- conversation.stats.estimatedContextTokens = estimateTextTokens(
301
- contextText ?? ""
302
- );
303
- conversation.stats.totalMessageCount = conversation.messages.length;
304
- conversation.stats.updatedAtMs = Date.now();
305
- }
306
- function upsertConversationMessage(conversation, message) {
307
- const existingIndex = conversation.messages.findIndex(
308
- (entry) => entry.id === message.id
309
- );
310
- if (existingIndex >= 0) {
311
- conversation.messages[existingIndex] = {
312
- ...conversation.messages[existingIndex],
313
- ...message,
314
- meta: {
315
- ...conversation.messages[existingIndex]?.meta,
316
- ...message.meta
317
- }
318
- };
319
- updateConversationStats(conversation);
320
- return message.id;
321
- }
322
- conversation.messages.push(message);
323
- updateConversationStats(conversation);
324
- return message.id;
325
- }
326
- function recordDeliveredAssistantMessage(args) {
327
- const prefix = `${args.sessionId}:assistant:`;
328
- const ordinal = args.conversation.messages.filter(
329
- (message) => message.id.startsWith(prefix)
330
- ).length + 1;
331
- const messageId = `${prefix}${ordinal}`;
332
- markConversationMessage(args.conversation, args.userMessageId, {
333
- replied: true,
334
- skippedReason: void 0
335
- });
336
- upsertConversationMessage(args.conversation, {
337
- id: messageId,
338
- role: "assistant",
339
- text: normalizeConversationText(args.text),
340
- createdAtMs: Date.now(),
341
- author: {
342
- userName: botConfig.userName,
343
- isBot: true
344
- },
345
- meta: {
346
- replied: true
347
- }
348
- });
349
- return messageId;
350
- }
351
- function turnHasReply(conversation, turnId) {
352
- const assistantPrefix = `${turnId}:assistant:`;
353
- return conversation.messages.some(
354
- (message) => message.role === "assistant" && message.id.startsWith(assistantPrefix)
355
- );
356
- }
357
- function markConversationMessage(conversation, messageId, patch) {
358
- if (!messageId) return;
359
- const messageIndex = conversation.messages.findIndex(
360
- (entry) => entry.id === messageId
361
- );
362
- if (messageIndex < 0) return;
363
- const current = conversation.messages[messageIndex];
364
- conversation.messages[messageIndex] = {
365
- ...current,
366
- meta: {
367
- ...current.meta ?? {},
368
- ...patch
369
- }
370
- };
371
- updateConversationStats(conversation);
372
- }
373
- function buildConversationContext(conversation, options = {}) {
374
- const messages = conversation.messages.filter(
375
- (entry) => entry.id !== options.excludeMessageId
376
- );
377
- if (messages.length === 0 && conversation.compactions.length === 0) {
378
- return void 0;
379
- }
380
- const lines = [];
381
- if (conversation.compactions.length > 0) {
382
- lines.push("<thread-compactions>");
383
- for (const [index, compaction] of conversation.compactions.entries()) {
384
- lines.push(
385
- ` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageCount}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
386
- compaction.summary,
387
- " </compaction>"
388
- );
389
- }
390
- lines.push("</thread-compactions>");
391
- }
392
- if (messages.length > 0) {
393
- if (lines.length > 0) {
394
- lines.push("");
395
- }
396
- lines.push("<thread-transcript>");
397
- for (const [index, message] of messages.entries()) {
398
- const author = escapeXml(conversationAuthorDisplayName(message));
399
- const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
400
- const ts = new Date(message.createdAtMs).toISOString();
401
- const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
402
- lines.push(
403
- ` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
404
- renderConversationMessageLine(message, conversation),
405
- " </message>"
406
- );
407
- }
408
- lines.push("</thread-transcript>");
409
- }
410
- return lines.join("\n");
411
- }
412
- function pruneCompactions(compactions) {
413
- if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
414
- return compactions;
415
- }
416
- const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
417
- const merged = compactions.slice(0, overflowCount);
418
- const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
419
- const coveredMessageCount = merged.reduce(
420
- (count, entry) => count + entry.coveredMessageCount,
421
- 0
422
- );
423
- const compacted = {
424
- id: generateConversationId("compaction"),
425
- createdAtMs: Date.now(),
426
- summary: mergedSummary,
427
- coveredMessageCount
428
- };
429
- return [compacted, ...compactions.slice(overflowCount)];
430
- }
431
- async function summarizeConversationChunk(messages, conversation, context, deps) {
432
- const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
433
- try {
434
- const result = await deps.completeText({
435
- modelId: botConfig.fastModelId,
436
- temperature: 0,
437
- messages: [
438
- {
439
- role: "user",
440
- content: [
441
- "Summarize the following older Slack thread transcript segment for future assistant turns.",
442
- "Keep the summary factual and concise. Do not invent details.",
443
- "",
444
- "Output exactly three XML sections in this order:",
445
- "<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>",
446
- "<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>",
447
- "<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>",
448
- "",
449
- "Do not output any text outside the three sections.",
450
- "",
451
- transcript
452
- ].join("\n"),
453
- timestamp: Date.now()
454
- }
455
- ],
456
- metadata: {
457
- modelId: botConfig.fastModelId,
458
- threadId: context.threadId ?? "",
459
- channelId: context.channelId ?? "",
460
- actorId: context.actorId ?? "",
461
- runId: context.runId ?? ""
462
- }
463
- });
464
- const summary = result.text.trim();
465
- if (summary.length > 0) {
466
- return summary.slice(0, 3500);
467
- }
468
- } catch (error) {
469
- logWarn("conversation.compaction.summary.failed", {
470
- "exception.message": error instanceof Error ? error.message : String(error),
471
- "app.compaction_messages_covered": messages.length
472
- });
473
- }
474
- return transcript.slice(0, 2800);
475
- }
476
- async function generateThreadTitleWithDeps(sourceText, deps) {
477
- const result = await deps.completeText({
478
- modelId: botConfig.fastModelId,
479
- temperature: 0,
480
- messages: [
481
- {
482
- role: "user",
483
- content: [
484
- "Generate a concise 5-8 word Slack conversation title from the first user message below.",
485
- "Capture the user's main request.",
486
- "Reply with ONLY the title, with no quotes or trailing punctuation.",
487
- "",
488
- `First user message: ${sourceText.slice(0, 500)}`
489
- ].join("\n"),
490
- timestamp: Date.now()
491
- }
492
- ],
493
- metadata: {
494
- modelId: botConfig.fastModelId
495
- }
496
- });
497
- return result.text.trim().slice(0, 60);
498
- }
499
- function getThreadTitleSourceMessage(conversation) {
500
- let firstMessage;
501
- for (const message of conversation.messages) {
502
- if (!isHumanConversationMessage(message)) {
503
- continue;
504
- }
505
- if (!firstMessage) {
506
- firstMessage = message;
507
- continue;
508
- }
509
- if (message.createdAtMs < firstMessage.createdAtMs) {
510
- firstMessage = message;
511
- continue;
512
- }
513
- if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
514
- firstMessage = message;
515
- }
516
- }
517
- return firstMessage;
518
- }
519
- async function compactConversationIfNeededWithDeps(conversation, context, deps) {
520
- updateConversationStats(conversation);
521
- let estimatedTokens = conversation.stats.estimatedContextTokens;
522
- setSpanAttributes({
523
- "app.context_tokens_estimated": estimatedTokens
524
- });
525
- const triggerTokens = getConversationContextCompactionTriggerTokens();
526
- const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
527
- while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
528
- const compactCount = Math.min(
529
- CONTEXT_COMPACTION_BATCH_SIZE,
530
- conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
531
- );
532
- if (compactCount <= 0) {
533
- break;
534
- }
535
- const compactedChunk = conversation.messages.slice(0, compactCount);
536
- const summary = await summarizeConversationChunk(
537
- compactedChunk,
538
- conversation,
539
- context,
540
- deps
541
- );
542
- conversation.compactions.push({
543
- id: generateConversationId("compaction"),
544
- createdAtMs: Date.now(),
545
- summary,
546
- coveredMessageCount: compactedChunk.length
547
- });
548
- conversation.compactions = pruneCompactions(conversation.compactions);
549
- conversation.messages = conversation.messages.slice(compactCount);
550
- conversation.stats.compactedMessageCount += compactCount;
551
- updateConversationStats(conversation);
552
- estimatedTokens = conversation.stats.estimatedContextTokens;
553
- setSpanAttributes({
554
- "app.compaction_messages_covered": compactCount,
555
- "app.compaction.trigger_tokens": triggerTokens,
556
- "app.compaction.target_tokens": targetTokens,
557
- "app.context_tokens_estimated": estimatedTokens
558
- });
559
- if (estimatedTokens <= targetTokens) {
560
- break;
561
- }
562
- }
563
- }
564
- function createConversationMemoryService(deps) {
565
- return {
566
- compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps),
567
- generateThreadTitle: async (sourceText) => await generateThreadTitleWithDeps(sourceText, deps)
568
- };
569
- }
570
- function isHumanConversationMessage(message) {
571
- return message.role === "user" && message.author?.isBot !== true;
572
- }
573
- function getConversationMessageSlackTs(message) {
574
- return parseSlackMessageTs(message.meta?.slackTs);
575
- }
576
-
577
184
  // src/chat/conversations/messages.ts
578
185
  import { createHash } from "crypto";
579
186
  import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
@@ -714,7 +321,6 @@ async function hydrateConversationMessages(args) {
714
321
  );
715
322
  args.conversation.compactions = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
716
323
  args.conversation.messages = projectConversationMessages(history);
717
- updateConversationStats(args.conversation);
718
324
  }
719
325
  async function persistConversationMessages(args) {
720
326
  if (!args.conversationId || args.conversation.messages.length === 0) return;
@@ -1387,7 +993,6 @@ function isTurnInputDeferredError(error) {
1387
993
  }
1388
994
  function startActiveTurn(args) {
1389
995
  args.conversation.processing.activeTurnId = args.nextTurnId;
1390
- args.updateConversationStats(args.conversation);
1391
996
  }
1392
997
  function clearActiveTurn(conversation, sessionId) {
1393
998
  if (!sessionId || conversation.processing.activeTurnId === sessionId) {
@@ -1397,12 +1002,10 @@ function clearActiveTurn(conversation, sessionId) {
1397
1002
  function markTurnClosed(args) {
1398
1003
  clearActiveTurn(args.conversation, args.sessionId);
1399
1004
  args.conversation.processing.lastCompletedAtMs = args.nowMs;
1400
- args.updateConversationStats(args.conversation);
1401
1005
  }
1402
1006
  function markTurnCompleted(args) {
1403
1007
  clearActiveTurn(args.conversation, args.sessionId);
1404
1008
  args.conversation.processing.lastCompletedAtMs = args.nowMs;
1405
- args.updateConversationStats(args.conversation);
1406
1009
  }
1407
1010
  function markTurnFailed(args) {
1408
1011
  clearActiveTurn(args.conversation, args.sessionId);
@@ -1411,7 +1014,6 @@ function markTurnFailed(args) {
1411
1014
  replied: false,
1412
1015
  skippedReason: "reply failed"
1413
1016
  });
1414
- args.updateConversationStats(args.conversation);
1415
1017
  }
1416
1018
 
1417
1019
  // src/chat/no-reply.ts
@@ -1470,7 +1072,7 @@ var RIPGREP_EXCLUDED_GLOBS = [
1470
1072
  ];
1471
1073
  var DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
1472
1074
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
1473
- function positiveInteger2(value) {
1075
+ function positiveInteger(value) {
1474
1076
  if (typeof value !== "number" || !Number.isFinite(value)) {
1475
1077
  return void 0;
1476
1078
  }
@@ -2817,19 +2419,11 @@ async function persistYieldSessionRecord(args) {
2817
2419
 
2818
2420
  // src/chat/state/conversation.ts
2819
2421
  function defaultConversationState() {
2820
- const nowMs = Date.now();
2821
2422
  return {
2822
2423
  schemaVersion: 1,
2823
2424
  messages: [],
2824
2425
  compactions: [],
2825
- backfill: {},
2826
2426
  processing: {},
2827
- stats: {
2828
- estimatedContextTokens: 0,
2829
- totalMessageCount: 0,
2830
- compactedMessageCount: 0,
2831
- updatedAtMs: nowMs
2832
- },
2833
2427
  vision: {
2834
2428
  byFileId: {}
2835
2429
  }
@@ -2864,27 +2458,14 @@ function coerceThreadConversationState(value) {
2864
2458
  }
2865
2459
  const root = value;
2866
2460
  const rawConversation = isRecord(root.conversation) ? root.conversation : {};
2867
- const base = defaultConversationState();
2868
2461
  const messages = [];
2869
2462
  const compactions = [];
2870
- const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
2871
- const backfill = {
2872
- completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
2873
- source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
2874
- };
2875
2463
  const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
2876
2464
  const processing = {
2877
2465
  activeTurnId: toOptionalString(rawProcessing.activeTurnId),
2878
2466
  lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
2879
2467
  pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
2880
2468
  };
2881
- const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
2882
- const stats = {
2883
- estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
2884
- totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
2885
- compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
2886
- updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
2887
- };
2888
2469
  const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
2889
2470
  const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
2890
2471
  const byFileId = {};
@@ -2903,9 +2484,7 @@ function coerceThreadConversationState(value) {
2903
2484
  schemaVersion: 1,
2904
2485
  messages,
2905
2486
  compactions,
2906
- backfill,
2907
2487
  processing,
2908
- stats,
2909
2488
  vision: {
2910
2489
  backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
2911
2490
  byFileId
@@ -2913,19 +2492,13 @@ function coerceThreadConversationState(value) {
2913
2492
  };
2914
2493
  }
2915
2494
  function buildConversationStatePatch(conversation) {
2916
- const {
2917
- compactions: _compactions,
2918
- messages: _messages,
2919
- ...scratch
2920
- } = conversation;
2921
2495
  return {
2922
2496
  conversation: {
2923
- ...scratch,
2924
2497
  schemaVersion: 1,
2925
- stats: {
2926
- ...conversation.stats,
2927
- totalMessageCount: conversation.messages.length,
2928
- updatedAtMs: Date.now()
2498
+ processing: { ...conversation.processing },
2499
+ vision: {
2500
+ backfillCompletedAtMs: conversation.vision.backfillCompletedAtMs,
2501
+ byFileId: { ...conversation.vision.byFileId }
2929
2502
  }
2930
2503
  }
2931
2504
  };
@@ -3393,20 +2966,6 @@ export {
3393
2966
  historyItemFromPiMessage,
3394
2967
  piMessageFromHistoryItem,
3395
2968
  persistConversationMessageSummaries,
3396
- estimateTextTokens,
3397
- getAgentContextCompactionTriggerTokens,
3398
- getAgentContextInputLimitTokens,
3399
- normalizeConversationText,
3400
- updateConversationStats,
3401
- upsertConversationMessage,
3402
- recordDeliveredAssistantMessage,
3403
- turnHasReply,
3404
- markConversationMessage,
3405
- buildConversationContext,
3406
- getThreadTitleSourceMessage,
3407
- createConversationMemoryService,
3408
- isHumanConversationMessage,
3409
- getConversationMessageSlackTs,
3410
2969
  hydrateConversationMessages,
3411
2970
  persistConversationMessages,
3412
2971
  loadProjection,
@@ -3427,7 +2986,7 @@ export {
3427
2986
  MAX_TEXT_CHARS,
3428
2987
  RIPGREP_EXCLUDED_GLOBS,
3429
2988
  DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
3430
- positiveInteger2 as positiveInteger,
2989
+ positiveInteger,
3431
2990
  normalizeToLf,
3432
2991
  truncateText,
3433
2992
  isMissingPathError,