@sentry/junior 0.94.0 → 0.95.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 (119) hide show
  1. package/dist/{agent-hooks-ZG7IDOIN.js → agent-hooks-FTQ5EGPO.js} +9 -10
  2. package/dist/api/people/list.js +7 -7
  3. package/dist/api/people/profile.js +7 -7
  4. package/dist/api/people/shared.d.ts +2 -2
  5. package/dist/api-reference.d.ts +1 -1
  6. package/dist/app.js +427 -196
  7. package/dist/{catalog-runtime-NZHMSMYC.js → catalog-runtime-2D7IVKUT.js} +3 -3
  8. package/dist/chat/advisor-request.d.ts +4 -0
  9. package/dist/chat/agent/request.d.ts +2 -0
  10. package/dist/chat/agent/resume.d.ts +2 -0
  11. package/dist/chat/conversations/history.d.ts +128 -0
  12. package/dist/chat/conversations/legacy-import.d.ts +38 -0
  13. package/dist/chat/conversations/messages.d.ts +39 -0
  14. package/dist/chat/conversations/projection.d.ts +94 -0
  15. package/dist/chat/conversations/retention.d.ts +36 -0
  16. package/dist/chat/conversations/sql/conversation-row.d.ts +14 -0
  17. package/dist/chat/conversations/sql/history.d.ts +4 -0
  18. package/dist/chat/conversations/sql/legacy-history-import.d.ts +55 -0
  19. package/dist/chat/conversations/sql/messages.d.ts +4 -0
  20. package/dist/chat/conversations/sql/migrations.d.ts +14 -3
  21. package/dist/chat/conversations/sql/purge.d.ts +53 -0
  22. package/dist/chat/conversations/sql/store.d.ts +7 -2
  23. package/dist/chat/conversations/store.d.ts +19 -0
  24. package/dist/chat/conversations/visible-compactions.d.ts +11 -0
  25. package/dist/chat/conversations/visible-messages.d.ts +47 -0
  26. package/dist/chat/db.d.ts +12 -1
  27. package/dist/chat/identities/sql.d.ts +1 -1
  28. package/dist/chat/logging.d.ts +2 -1
  29. package/dist/chat/resource-events/store.d.ts +2 -1
  30. package/dist/chat/runtime/reply-executor.d.ts +8 -0
  31. package/dist/chat/runtime/thread-state.d.ts +2 -0
  32. package/dist/chat/sandbox/resources.d.ts +4 -0
  33. package/dist/chat/services/mcp-auth-orchestration.d.ts +8 -0
  34. package/dist/chat/services/plugin-auth-orchestration.d.ts +12 -0
  35. package/dist/chat/services/turn-session-record.d.ts +14 -2
  36. package/dist/chat/sleep.d.ts +2 -0
  37. package/dist/chat/state/conversation.d.ts +7 -4
  38. package/dist/chat/state/session-log.d.ts +23 -1
  39. package/dist/chat/state/turn-session.d.ts +6 -0
  40. package/dist/chat/tool-support/private-trace-result.d.ts +4 -0
  41. package/dist/chat/tool-support/zod-tool.d.ts +1 -0
  42. package/dist/chat/tools/advisor/tool.d.ts +9 -4
  43. package/dist/chat/tools/definition.d.ts +2 -0
  44. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  45. package/dist/chat/tools/system-time.d.ts +8 -0
  46. package/dist/chat/xml.d.ts +2 -0
  47. package/dist/{chunk-NYWPISSO.js → chunk-2XXEKR44.js} +1 -1
  48. package/dist/{chunk-QGEWC27H.js → chunk-5XMNOD3S.js} +5 -7
  49. package/dist/{chunk-NFXSLTYG.js → chunk-6ONME5IG.js} +864 -62
  50. package/dist/{chunk-LFLTJQVR.js → chunk-7FBGKXPE.js} +4 -0
  51. package/dist/{chunk-MC4ZZE4Q.js → chunk-AYP4ERVZ.js} +1 -1
  52. package/dist/{chunk-GHGPTPBL.js → chunk-B7PUBQQM.js} +1 -1
  53. package/dist/chunk-CM5EZD5F.js +17 -0
  54. package/dist/chunk-CRRBJ3JP.js +1037 -0
  55. package/dist/{chunk-G3E7SCME.js → chunk-CZEX6KIQ.js} +23 -0
  56. package/dist/{chunk-TNUUATT7.js → chunk-E3E2XNCN.js} +2 -2
  57. package/dist/{chunk-LNCREA5Z.js → chunk-EKNJM5IG.js} +3 -127
  58. package/dist/{chunk-NAPITCZ6.js → chunk-GUE7HTNR.js} +251 -6
  59. package/dist/{chunk-BTH37NCU.js → chunk-GVOUCI2O.js} +1 -1
  60. package/dist/{chunk-YYBX2BD5.js → chunk-GY34VGP5.js} +1 -1
  61. package/dist/{chunk-KRMANDHI.js → chunk-HCXXB35T.js} +1481 -349
  62. package/dist/{chunk-YTNPU7I2.js → chunk-HDTO3A74.js} +2 -2
  63. package/dist/{chunk-YJ4OPVNA.js → chunk-HZNJLKKI.js} +1 -1
  64. package/dist/{chunk-HDNWZMUN.js → chunk-KDPCGVIT.js} +116 -51
  65. package/dist/chunk-N4RNJ32C.js +48 -0
  66. package/dist/{chunk-UUB3D2XI.js → chunk-NSCHU54X.js} +8 -10
  67. package/dist/{chunk-PFXC67GJ.js → chunk-RAIB6WSG.js} +6 -80
  68. package/dist/chunk-WAGQ377B.js +724 -0
  69. package/dist/{chunk-6H4PP63X.js → chunk-WCMQJE4F.js} +24 -14
  70. package/dist/{chunk-S6QKIGRM.js → chunk-WPOJ443W.js} +15 -25
  71. package/dist/{chunk-MAVOJ54A.js → chunk-XX4N4E6C.js} +25 -388
  72. package/dist/cli/chat.js +24 -23
  73. package/dist/cli/check.js +4 -4
  74. package/dist/cli/plugins.js +11 -12
  75. package/dist/cli/snapshot-warmup.js +7 -8
  76. package/dist/cli/upgrade.js +78 -17
  77. package/dist/db/schema/agent-steps.d.ts +134 -0
  78. package/dist/db/schema/conversation-messages.d.ts +153 -0
  79. package/dist/{chat/conversations/sql → db}/schema/conversations.d.ts +34 -0
  80. package/dist/{chat/conversations/sql → db}/schema.d.ts +319 -8
  81. package/dist/db-W55B74OP.js +24 -0
  82. package/dist/deployment.d.ts +2 -0
  83. package/dist/handlers/retention.d.ts +6 -0
  84. package/dist/instrumentation.js +16 -3
  85. package/dist/legacy-import-ZPLGLAFD.js +17 -0
  86. package/dist/nitro.js +14 -4
  87. package/dist/reporting/conversations/activity.d.ts +40 -0
  88. package/dist/reporting/conversations/context.d.ts +8 -0
  89. package/dist/reporting/conversations/details.d.ts +6 -0
  90. package/dist/reporting/conversations/index.d.ts +4 -0
  91. package/dist/reporting/conversations/shared.d.ts +9 -0
  92. package/dist/reporting/conversations/statistics.d.ts +4 -0
  93. package/dist/reporting/conversations/summaries.d.ts +17 -0
  94. package/dist/reporting/conversations/transcript.d.ts +28 -0
  95. package/dist/reporting/conversations/types.d.ts +178 -0
  96. package/dist/reporting/conversations.d.ts +1 -178
  97. package/dist/reporting.d.ts +4 -3
  98. package/dist/reporting.js +603 -827
  99. package/dist/{runner-XAZPJNAQ.js → runner-OOSBDYRK.js} +35 -41
  100. package/dist/{validation-KYAWETI4.js → validation-QCGRFRFZ.js} +4 -4
  101. package/migrations/README.md +32 -0
  102. package/migrations/meta/0000_snapshot.json +978 -0
  103. package/migrations/meta/_journal.json +13 -0
  104. package/package.json +10 -7
  105. package/dist/chat/sql/schema.d.ts +0 -1049
  106. package/dist/chat/state/conversation-details.d.ts +0 -47
  107. package/dist/chunk-7STN2ZSK.js +0 -250
  108. package/dist/chunk-LE5ANBUZ.js +0 -1040
  109. package/dist/chunk-QL66APYJ.js +0 -1377
  110. package/dist/db-YDN6AFJH.js +0 -18
  111. /package/dist/{chat/sql → db}/db.d.ts +0 -0
  112. /package/dist/{chat/sql → db}/executor.d.ts +0 -0
  113. /package/dist/{chat/sql → db}/neon.d.ts +0 -0
  114. /package/dist/{chat/sql → db}/postgres.d.ts +0 -0
  115. /package/dist/{chat/conversations/sql → db}/schema/destinations.d.ts +0 -0
  116. /package/dist/{chat/conversations/sql → db}/schema/identities.d.ts +0 -0
  117. /package/dist/{chat/conversations/sql → db}/schema/migrations.d.ts +0 -0
  118. /package/dist/{chat/conversations/sql → db}/schema/timestamps.d.ts +0 -0
  119. /package/dist/{chat/conversations/sql → db}/schema/users.d.ts +0 -0
@@ -1,18 +1,17 @@
1
1
  import {
2
- SANDBOX_WORKSPACE_ROOT
3
- } from "./chunk-G3E7SCME.js";
4
- import {
5
- getStateAdapter
6
- } from "./chunk-7STN2ZSK.js";
2
+ SANDBOX_WORKSPACE_ROOT,
3
+ sleep
4
+ } from "./chunk-CZEX6KIQ.js";
7
5
  import {
6
+ getStateAdapter,
8
7
  toOptionalTrimmed
9
- } from "./chunk-NAPITCZ6.js";
8
+ } from "./chunk-GUE7HTNR.js";
10
9
  import {
11
10
  pluginCatalogRuntime
12
- } from "./chunk-BTH37NCU.js";
11
+ } from "./chunk-GVOUCI2O.js";
13
12
  import {
14
13
  withSpan
15
- } from "./chunk-S6QKIGRM.js";
14
+ } from "./chunk-WPOJ443W.js";
16
15
 
17
16
  // src/chat/sandbox/runtime-dependency-snapshots.ts
18
17
  import { createHash } from "crypto";
@@ -134,6 +133,19 @@ function createSandboxInstance(sandbox) {
134
133
  };
135
134
  }
136
135
 
136
+ // src/chat/sandbox/resources.ts
137
+ function getSandboxResources() {
138
+ const value = process.env.SANDBOX_VCPUS?.trim();
139
+ if (!value || !/^\d+$/.test(value)) {
140
+ return void 0;
141
+ }
142
+ const vcpus = Number(value);
143
+ if (!Number.isSafeInteger(vcpus) || vcpus <= 0) {
144
+ return void 0;
145
+ }
146
+ return { vcpus };
147
+ }
148
+
137
149
  // src/chat/sandbox/runtime-dependency-snapshots.ts
138
150
  var SNAPSHOT_CACHE_PREFIX = "junior:sandbox_snapshot_profile";
139
151
  var SNAPSHOT_LOCK_PREFIX = "junior:sandbox_snapshot_lock";
@@ -142,11 +154,6 @@ var SNAPSHOT_CACHE_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
142
154
  var SNAPSHOT_BUILD_LOCK_TTL_MS = 10 * 60 * 1e3;
143
155
  var SNAPSHOT_WAIT_FOR_LOCK_MS = SNAPSHOT_BUILD_LOCK_TTL_MS + 30 * 1e3;
144
156
  var DEFAULT_FLOATING_DEP_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
145
- function sleep(ms) {
146
- return new Promise((resolve) => {
147
- setTimeout(resolve, ms);
148
- });
149
- }
150
157
  function profileCacheKey(profileHash) {
151
158
  return `${SNAPSHOT_CACHE_PREFIX}:${profileHash}`;
152
159
  }
@@ -461,11 +468,13 @@ async function createDependencySnapshot(profile, runtime, timeoutMs) {
461
468
  },
462
469
  async () => {
463
470
  const sandboxCredentials = getVercelSandboxCredentials();
471
+ const resources = getSandboxResources();
464
472
  const sandbox = createSandboxInstance(
465
473
  await Sandbox.create({
466
474
  timeout: timeoutMs,
467
475
  runtime,
468
- ...sandboxCredentials ?? {}
476
+ ...sandboxCredentials ?? {},
477
+ ...resources ? { resources } : {}
469
478
  })
470
479
  );
471
480
  try {
@@ -681,6 +690,7 @@ export {
681
690
  buildNonInteractiveShellScript,
682
691
  runNonInteractiveCommand,
683
692
  getVercelSandboxCredentials,
693
+ getSandboxResources,
684
694
  createSandboxInstance,
685
695
  getRuntimeDependencyProfileHash,
686
696
  resolveRuntimeDependencySnapshot,
@@ -13,7 +13,7 @@ import {
13
13
  import {
14
14
  getDeploymentTelemetryAttributes,
15
15
  parseSlackTeamId
16
- } from "./chunk-LFLTJQVR.js";
16
+ } from "./chunk-7FBGKXPE.js";
17
17
 
18
18
  // src/chat/logging.ts
19
19
  import { AsyncLocalStorage } from "async_hooks";
@@ -1114,6 +1114,13 @@ function normalizeSpanAttributes(attributes) {
1114
1114
  }
1115
1115
  return normalized;
1116
1116
  }
1117
+ function setAttributesOnSpan(span, attributes) {
1118
+ for (const [key, value] of Object.entries(
1119
+ normalizeSpanAttributes(attributes)
1120
+ )) {
1121
+ span.setAttribute(key, value);
1122
+ }
1123
+ }
1117
1124
  function logInfo(eventName, context = {}, attributes = {}, body) {
1118
1125
  log.info(eventName, { ...toSpanAttributes(context), ...attributes }, body);
1119
1126
  }
@@ -1157,7 +1164,7 @@ async function withSpan(name, op, context, callback, attributes = {}) {
1157
1164
  ...normalizedAttributes
1158
1165
  }
1159
1166
  },
1160
- callback
1167
+ (span) => callback((attributes2) => setAttributesOnSpan(span, attributes2))
1161
1168
  );
1162
1169
  });
1163
1170
  }
@@ -1177,32 +1184,20 @@ function getTracePropagationHeaders() {
1177
1184
  return headers;
1178
1185
  }
1179
1186
  function setSpanAttributes(attributes) {
1180
- const sentry = sentry_exports;
1181
- const span = sentry.getActiveSpan?.();
1187
+ const span = (void 0)();
1182
1188
  if (!span) {
1183
1189
  return;
1184
1190
  }
1185
- const setAttribute = span.setAttribute;
1186
- if (typeof setAttribute !== "function") {
1187
- return;
1188
- }
1189
- for (const [key, value] of Object.entries(
1190
- normalizeSpanAttributes(attributes)
1191
- )) {
1192
- setAttribute.call(span, key, value);
1193
- }
1191
+ setAttributesOnSpan(span, attributes);
1194
1192
  }
1195
1193
  function setSpanStatus(status) {
1196
- const sentry = sentry_exports;
1197
- const span = sentry.getActiveSpan?.();
1194
+ const span = (void 0)();
1198
1195
  if (!span) {
1199
1196
  return;
1200
1197
  }
1201
- const setStatus = span.setStatus;
1202
- if (typeof setStatus !== "function") {
1203
- return;
1204
- }
1205
- setStatus.call(span, status === "ok" ? "ok" : "internal_error");
1198
+ span.setStatus(
1199
+ status === "ok" ? { code: 1 } : { code: 2, message: "internal_error" }
1200
+ );
1206
1201
  }
1207
1202
  function getActiveTraceId() {
1208
1203
  const sentry = sentry_exports;
@@ -1376,10 +1371,6 @@ var storedSlackActorSchema = z.object({
1376
1371
  slackUserName: exactStoredStringSchema.optional(),
1377
1372
  teamId: exactStoredStringSchema.optional()
1378
1373
  }).strict();
1379
- function parseActor(value) {
1380
- const result = actorSchema.safeParse(value);
1381
- return result.success ? result.data : void 0;
1382
- }
1383
1374
  function isUserActor(actor) {
1384
1375
  return Boolean(actor && "userId" in actor);
1385
1376
  }
@@ -1557,7 +1548,6 @@ export {
1557
1548
  extractGenAiUsageSummary,
1558
1549
  extractGenAiUsageAttributes,
1559
1550
  storedSlackActorSchema,
1560
- parseActor,
1561
1551
  isUserActor,
1562
1552
  parseActorUserId,
1563
1553
  isActorUserId,
@@ -5,409 +5,54 @@ import {
5
5
  } from "./chunk-KNFROR7R.js";
6
6
  import {
7
7
  clearPendingAuth,
8
+ getInterruptionMarker,
8
9
  getPersistedThreadState,
9
10
  markTurnCompleted,
10
11
  mergeArtifactsState
11
- } from "./chunk-HDNWZMUN.js";
12
+ } from "./chunk-KDPCGVIT.js";
12
13
  import {
13
14
  coerceThreadConversationState
14
- } from "./chunk-PFXC67GJ.js";
15
+ } from "./chunk-RAIB6WSG.js";
15
16
  import {
16
- getAgentTurnSessionRecord,
17
- getInterruptionMarker
18
- } from "./chunk-QL66APYJ.js";
17
+ getAgentTurnSessionRecord
18
+ } from "./chunk-WAGQ377B.js";
19
+ import {
20
+ generateConversationId,
21
+ hydrateConversationMessages,
22
+ markConversationMessage,
23
+ normalizeConversationText,
24
+ updateConversationStats,
25
+ upsertConversationMessage
26
+ } from "./chunk-CRRBJ3JP.js";
19
27
  import {
20
28
  createPluginEmbedder,
21
29
  createPluginModel,
22
30
  getPlugins
23
- } from "./chunk-UUB3D2XI.js";
31
+ } from "./chunk-NSCHU54X.js";
24
32
  import {
25
33
  createPluginLogger,
26
34
  createPluginState
27
- } from "./chunk-TNUUATT7.js";
28
- import {
29
- getStateAdapter
30
- } from "./chunk-7STN2ZSK.js";
35
+ } from "./chunk-E3E2XNCN.js";
31
36
  import {
32
37
  getDb
33
- } from "./chunk-NFXSLTYG.js";
38
+ } from "./chunk-6ONME5IG.js";
34
39
  import {
35
40
  GEN_AI_PROVIDER_NAME,
36
41
  botConfig,
37
- escapeXml,
38
42
  getPiMessageRole,
43
+ getStateAdapter,
39
44
  instructionTextForProjection,
40
45
  isToolResultError,
41
46
  isToolResultMessage,
42
47
  normalizeToolNameFromResult,
43
- resolveGatewayModel,
44
48
  stripRuntimeTurnContext
45
- } from "./chunk-NAPITCZ6.js";
49
+ } from "./chunk-GUE7HTNR.js";
46
50
  import {
47
- buildTurnFailureResponse,
48
- logWarn,
49
- setSpanAttributes
50
- } from "./chunk-S6QKIGRM.js";
51
+ buildTurnFailureResponse
52
+ } from "./chunk-WPOJ443W.js";
51
53
  import {
52
54
  parseSlackMessageTs
53
- } from "./chunk-LFLTJQVR.js";
54
-
55
- // src/chat/services/context-budget.ts
56
- var COMPACTION_TRIGGER_INPUT_RATIO = 0.75;
57
- var COMPACTION_OUTPUT_RESERVE_RATIO = 0.25;
58
- var COMPACTION_TARGET_RATIO = 0.8;
59
- var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
60
- var FALLBACK_MAX_OUTPUT_TOKENS = 128e3;
61
- function positiveInteger(value, fallback) {
62
- return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
63
- }
64
- function estimateTextTokens(text) {
65
- return Math.ceil(text.length / 4);
66
- }
67
- function calculateContextCompactionTriggerTokens(model) {
68
- const contextWindow = positiveInteger(
69
- model.contextWindow,
70
- FALLBACK_CONTEXT_WINDOW_TOKENS
71
- );
72
- const maxTokens = positiveInteger(
73
- model.maxTokens,
74
- FALLBACK_MAX_OUTPUT_TOKENS
75
- );
76
- const outputReserve = Math.min(
77
- maxTokens,
78
- Math.floor(contextWindow * COMPACTION_OUTPUT_RESERVE_RATIO)
79
- );
80
- const usableInputTokens = Math.max(1, contextWindow - outputReserve);
81
- return Math.max(
82
- 1,
83
- Math.floor(usableInputTokens * COMPACTION_TRIGGER_INPUT_RATIO)
84
- );
85
- }
86
- function calculateContextCompactionTargetTokens(triggerTokens) {
87
- return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
88
- }
89
- function getAgentContextCompactionTriggerTokens() {
90
- const model = resolveGatewayModel(botConfig.modelId);
91
- return calculateContextCompactionTriggerTokens({
92
- contextWindow: botConfig.modelContextWindowTokens ?? model.contextWindow,
93
- maxTokens: model.maxTokens
94
- });
95
- }
96
- function getConversationContextCompactionTriggerTokens() {
97
- const model = resolveGatewayModel(botConfig.fastModelId);
98
- return calculateContextCompactionTriggerTokens({
99
- contextWindow: model.contextWindow,
100
- maxTokens: model.maxTokens
101
- });
102
- }
103
-
104
- // src/chat/services/conversation-memory.ts
105
- var CONTEXT_MIN_LIVE_MESSAGES = 12;
106
- var CONTEXT_COMPACTION_BATCH_SIZE = 24;
107
- var CONTEXT_MAX_COMPACTIONS = 16;
108
- var CONTEXT_MAX_MESSAGE_CHARS = 3200;
109
- var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
110
- function generateConversationId(prefix) {
111
- return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
112
- }
113
- function normalizeConversationText(text) {
114
- return text.trim().replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
115
- }
116
- function buildImageContextSuffix(message, conversation) {
117
- const byFileId = conversation?.vision.byFileId;
118
- const imageFileIds = message.meta?.imageFileIds ?? [];
119
- if (!byFileId || imageFileIds.length === 0) {
120
- return "";
121
- }
122
- const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
123
- if (summaries.length === 0) {
124
- return "";
125
- }
126
- return ` [image context: ${summaries.join(" | ")}]`;
127
- }
128
- function renderConversationMessageLine(message, conversation) {
129
- const displayName = conversationAuthorDisplayName(message);
130
- const markers = [];
131
- if (message.meta?.replied === false) {
132
- markers.push(
133
- `assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
134
- );
135
- }
136
- if (message.meta?.explicitMention) {
137
- markers.push("explicit mention");
138
- }
139
- const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
140
- const imageContext = buildImageContextSuffix(message, conversation);
141
- return `[${message.role}] ${displayName}: ${message.text}${imageContext}${markerSuffix}`;
142
- }
143
- function conversationAuthorDisplayName(message) {
144
- const author = message.author;
145
- const fullName = authorDisplayField(author?.fullName, author?.userId);
146
- const userName = authorDisplayField(author?.userName, author?.userId);
147
- return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
148
- }
149
- function authorDisplayField(value, userId) {
150
- if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
151
- return void 0;
152
- }
153
- return value;
154
- }
155
- function updateConversationStats(conversation) {
156
- const contextText = buildConversationContext(conversation);
157
- conversation.stats.estimatedContextTokens = estimateTextTokens(
158
- contextText ?? ""
159
- );
160
- conversation.stats.totalMessageCount = conversation.messages.length;
161
- conversation.stats.updatedAtMs = Date.now();
162
- }
163
- function upsertConversationMessage(conversation, message) {
164
- const existingIndex = conversation.messages.findIndex(
165
- (entry) => entry.id === message.id
166
- );
167
- if (existingIndex >= 0) {
168
- conversation.messages[existingIndex] = {
169
- ...conversation.messages[existingIndex],
170
- ...message,
171
- meta: {
172
- ...conversation.messages[existingIndex]?.meta,
173
- ...message.meta
174
- }
175
- };
176
- updateConversationStats(conversation);
177
- return message.id;
178
- }
179
- conversation.messages.push(message);
180
- updateConversationStats(conversation);
181
- return message.id;
182
- }
183
- function markConversationMessage(conversation, messageId, patch) {
184
- if (!messageId) return;
185
- const messageIndex = conversation.messages.findIndex(
186
- (entry) => entry.id === messageId
187
- );
188
- if (messageIndex < 0) return;
189
- const current = conversation.messages[messageIndex];
190
- conversation.messages[messageIndex] = {
191
- ...current,
192
- meta: {
193
- ...current.meta ?? {},
194
- ...patch
195
- }
196
- };
197
- updateConversationStats(conversation);
198
- }
199
- function buildConversationContext(conversation, options = {}) {
200
- const messages = conversation.messages.filter(
201
- (entry) => entry.id !== options.excludeMessageId
202
- );
203
- if (messages.length === 0 && conversation.compactions.length === 0) {
204
- return void 0;
205
- }
206
- const lines = [];
207
- if (conversation.compactions.length > 0) {
208
- lines.push("<thread-compactions>");
209
- for (const [index, compaction] of conversation.compactions.entries()) {
210
- lines.push(
211
- ` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageIds.length}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
212
- compaction.summary,
213
- " </compaction>"
214
- );
215
- }
216
- lines.push("</thread-compactions>");
217
- }
218
- if (messages.length > 0) {
219
- if (lines.length > 0) {
220
- lines.push("");
221
- }
222
- lines.push("<thread-transcript>");
223
- for (const [index, message] of messages.entries()) {
224
- const author = escapeXml(conversationAuthorDisplayName(message));
225
- const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
226
- const ts = new Date(message.createdAtMs).toISOString();
227
- const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
228
- lines.push(
229
- ` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
230
- renderConversationMessageLine(message, conversation),
231
- " </message>"
232
- );
233
- }
234
- lines.push("</thread-transcript>");
235
- }
236
- return lines.join("\n");
237
- }
238
- function pruneCompactions(compactions) {
239
- if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
240
- return compactions;
241
- }
242
- const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
243
- const merged = compactions.slice(0, overflowCount);
244
- const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
245
- const mergedIds = merged.flatMap((entry) => entry.coveredMessageIds).slice(0, 500);
246
- const compacted = {
247
- id: generateConversationId("compaction"),
248
- createdAtMs: Date.now(),
249
- summary: mergedSummary,
250
- coveredMessageIds: mergedIds
251
- };
252
- return [compacted, ...compactions.slice(overflowCount)];
253
- }
254
- async function summarizeConversationChunk(messages, conversation, context, deps) {
255
- const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
256
- try {
257
- const result = await deps.completeText({
258
- modelId: botConfig.fastModelId,
259
- temperature: 0,
260
- messages: [
261
- {
262
- role: "user",
263
- content: [
264
- "Summarize the following older Slack thread transcript segment for future assistant turns.",
265
- "Keep the summary factual and concise. Do not invent details.",
266
- "",
267
- "Output exactly three XML sections in this order:",
268
- "<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>",
269
- "<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>",
270
- "<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>",
271
- "",
272
- "Do not output any text outside the three sections.",
273
- "",
274
- transcript
275
- ].join("\n"),
276
- timestamp: Date.now()
277
- }
278
- ],
279
- metadata: {
280
- modelId: botConfig.fastModelId,
281
- threadId: context.threadId ?? "",
282
- channelId: context.channelId ?? "",
283
- actorId: context.actorId ?? "",
284
- runId: context.runId ?? ""
285
- }
286
- });
287
- const summary = result.text.trim();
288
- if (summary.length > 0) {
289
- return summary.slice(0, 3500);
290
- }
291
- } catch (error) {
292
- logWarn(
293
- "conversation_compaction_summary_failed",
294
- {
295
- slackThreadId: context.threadId,
296
- slackUserId: context.actorId,
297
- slackChannelId: context.channelId,
298
- runId: context.runId,
299
- assistantUserName: botConfig.userName,
300
- modelId: botConfig.fastModelId
301
- },
302
- {
303
- "exception.message": error instanceof Error ? error.message : String(error),
304
- "app.compaction_messages_covered": messages.length
305
- },
306
- "Compaction summarization failed; using fallback summary"
307
- );
308
- }
309
- return transcript.slice(0, 2800);
310
- }
311
- async function generateThreadTitleWithDeps(sourceText, deps) {
312
- const result = await deps.completeText({
313
- modelId: botConfig.fastModelId,
314
- temperature: 0,
315
- messages: [
316
- {
317
- role: "user",
318
- content: [
319
- "Generate a concise 5-8 word Slack conversation title from the first user message below.",
320
- "Capture the user's main request.",
321
- "Reply with ONLY the title, with no quotes or trailing punctuation.",
322
- "",
323
- `First user message: ${sourceText.slice(0, 500)}`
324
- ].join("\n"),
325
- timestamp: Date.now()
326
- }
327
- ],
328
- metadata: {
329
- modelId: botConfig.fastModelId
330
- }
331
- });
332
- return result.text.trim().slice(0, 60);
333
- }
334
- function getThreadTitleSourceMessage(conversation) {
335
- let firstMessage;
336
- for (const message of conversation.messages) {
337
- if (!isHumanConversationMessage(message)) {
338
- continue;
339
- }
340
- if (!firstMessage) {
341
- firstMessage = message;
342
- continue;
343
- }
344
- if (message.createdAtMs < firstMessage.createdAtMs) {
345
- firstMessage = message;
346
- continue;
347
- }
348
- if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
349
- firstMessage = message;
350
- }
351
- }
352
- return firstMessage;
353
- }
354
- async function compactConversationIfNeededWithDeps(conversation, context, deps) {
355
- updateConversationStats(conversation);
356
- let estimatedTokens = conversation.stats.estimatedContextTokens;
357
- setSpanAttributes({
358
- "app.context_tokens_estimated": estimatedTokens
359
- });
360
- const triggerTokens = getConversationContextCompactionTriggerTokens();
361
- const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
362
- while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
363
- const compactCount = Math.min(
364
- CONTEXT_COMPACTION_BATCH_SIZE,
365
- conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
366
- );
367
- if (compactCount <= 0) {
368
- break;
369
- }
370
- const compactedChunk = conversation.messages.slice(0, compactCount);
371
- const summary = await summarizeConversationChunk(
372
- compactedChunk,
373
- conversation,
374
- context,
375
- deps
376
- );
377
- conversation.compactions.push({
378
- id: generateConversationId("compaction"),
379
- createdAtMs: Date.now(),
380
- summary,
381
- coveredMessageIds: compactedChunk.map((entry) => entry.id)
382
- });
383
- conversation.compactions = pruneCompactions(conversation.compactions);
384
- conversation.messages = conversation.messages.slice(compactCount);
385
- conversation.stats.compactedMessageCount += compactCount;
386
- updateConversationStats(conversation);
387
- estimatedTokens = conversation.stats.estimatedContextTokens;
388
- setSpanAttributes({
389
- "app.compaction_messages_covered": compactCount,
390
- "app.compaction.trigger_tokens": triggerTokens,
391
- "app.compaction.target_tokens": targetTokens,
392
- "app.context_tokens_estimated": estimatedTokens
393
- });
394
- if (estimatedTokens <= targetTokens) {
395
- break;
396
- }
397
- }
398
- }
399
- function createConversationMemoryService(deps) {
400
- return {
401
- compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps),
402
- generateThreadTitle: async (sourceText) => await generateThreadTitleWithDeps(sourceText, deps)
403
- };
404
- }
405
- function isHumanConversationMessage(message) {
406
- return message.role === "user" && message.author?.isBot !== true;
407
- }
408
- function getConversationMessageSlackTs(message) {
409
- return parseSlackMessageTs(message.meta?.slackTs);
410
- }
55
+ } from "./chunk-7FBGKXPE.js";
411
56
 
412
57
  // src/chat/services/turn-failure-response.ts
413
58
  function requireTurnFailureEventId(eventId, eventName) {
@@ -652,6 +297,10 @@ async function loadConversationContextTranscriptEntries(record) {
652
297
  }
653
298
  const state = await getPersistedThreadState(record.conversationId);
654
299
  const conversation = coerceThreadConversationState(state);
300
+ await hydrateConversationMessages({
301
+ conversation,
302
+ conversationId: record.conversationId
303
+ });
655
304
  const entries = [];
656
305
  for (const message of conversation.messages) {
657
306
  if (message.role !== "user") {
@@ -849,18 +498,6 @@ function buildDeliveredTurnStatePatch(args) {
849
498
  }
850
499
 
851
500
  export {
852
- estimateTextTokens,
853
- getAgentContextCompactionTriggerTokens,
854
- generateConversationId,
855
- normalizeConversationText,
856
- updateConversationStats,
857
- upsertConversationMessage,
858
- markConversationMessage,
859
- buildConversationContext,
860
- getThreadTitleSourceMessage,
861
- createConversationMemoryService,
862
- isHumanConversationMessage,
863
- getConversationMessageSlackTs,
864
501
  requireTurnFailureEventId,
865
502
  getAgentTurnDiagnosticsAttributes,
866
503
  finalizeFailedTurnReply,