@sentry/junior 0.113.0 → 0.114.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 (51) hide show
  1. package/dist/{agent-hooks-BKEMWR3Z.js → agent-hooks-WNBUY4OB.js} +8 -8
  2. package/dist/api/conversations/detail.d.ts +24 -0
  3. package/dist/api/conversations/event-list.d.ts +24 -0
  4. package/dist/api/conversations/events.d.ts +1 -1
  5. package/dist/api/schema/conversation.d.ts +96 -0
  6. package/dist/api/schema.js +1 -1
  7. package/dist/api.js +29 -13
  8. package/dist/app.d.ts +2 -0
  9. package/dist/app.js +30 -21
  10. package/dist/{catalog-runtime-6XYOUVRZ.js → catalog-runtime-6O4M6JGA.js} +5 -5
  11. package/dist/chat/config.d.ts +1 -1
  12. package/dist/chat/conversations/history.d.ts +86 -0
  13. package/dist/chat/conversations/projection.d.ts +20 -1
  14. package/dist/chat/services/context-budget.d.ts +7 -2
  15. package/dist/chat/services/context-compaction.d.ts +28 -1
  16. package/dist/chat/services/conversation-memory.d.ts +1 -0
  17. package/dist/chat/services/provider-error.d.ts +1 -0
  18. package/dist/chat/services/turn-router.d.ts +1 -0
  19. package/dist/chat/tools/sandbox/edit-file.d.ts +1 -1
  20. package/dist/chat/tools/sandbox/text-edits.d.ts +1 -0
  21. package/dist/{chunk-36UJA3OD.js → chunk-4FUEG3QY.js} +1 -1
  22. package/dist/{chunk-NP7TYFYR.js → chunk-5CZ7RETL.js} +1 -1
  23. package/dist/{chunk-DXRPHBOU.js → chunk-6QWPVTMO.js} +72 -33
  24. package/dist/{chunk-WBB22ANL.js → chunk-ABPSDO6M.js} +6 -6
  25. package/dist/{chunk-LLKDNPYV.js → chunk-FIGAF6KL.js} +34 -2
  26. package/dist/{chunk-OXE5ZUTS.js → chunk-K2W4CK7K.js} +1 -1
  27. package/dist/{chunk-K5BXPM74.js → chunk-O4GT7LYB.js} +1 -1
  28. package/dist/{chunk-LR2SKL5Y.js → chunk-OSKEC4XU.js} +1 -1
  29. package/dist/{chunk-ZRSUCJGA.js → chunk-PM3PX42K.js} +1 -1
  30. package/dist/{chunk-QTS66GMR.js → chunk-RD7EJI7H.js} +2 -2
  31. package/dist/{chunk-UKGNSJQQ.js → chunk-S4QDH23A.js} +25 -1
  32. package/dist/{chunk-4WZ7YKDF.js → chunk-TLGM5X2M.js} +2 -2
  33. package/dist/{chunk-YJ44UT7D.js → chunk-VNNBSKCO.js} +5 -5
  34. package/dist/{chunk-3CTIAKSS.js → chunk-W6RDUAO3.js} +1 -1
  35. package/dist/{chunk-H5ON7XTE.js → chunk-WV4UJBAS.js} +369 -79
  36. package/dist/{chunk-4VKFQPCW.js → chunk-XLFBQJUO.js} +4 -4
  37. package/dist/{chunk-HMRVILWZ.js → chunk-YU6PO42Q.js} +1 -1
  38. package/dist/{chunk-RCBUZGIT.js → chunk-ZGXH3REA.js} +12 -19
  39. package/dist/cli/chat.js +19 -19
  40. package/dist/cli/check.js +6 -6
  41. package/dist/cli/plugins.js +11 -11
  42. package/dist/cli/snapshot-warmup.js +8 -8
  43. package/dist/cli/upgrade.js +5 -5
  44. package/dist/{db-2ASK6MV6.js → db-4VLP7KBF.js} +5 -5
  45. package/dist/instrumentation.js +2 -2
  46. package/dist/nitro.js +2 -2
  47. package/dist/{runner-L363K62Z.js → runner-IPXLWQOF.js} +10 -10
  48. package/dist/{skills-VCPG3NOO.js → skills-QCNZISNF.js} +6 -6
  49. package/dist/{validation-GY7AJBLC.js → validation-I2BW3UHD.js} +6 -6
  50. package/dist/version.js +1 -1
  51. package/package.json +5 -5
@@ -9,6 +9,7 @@ import {
9
9
  estimateTextTokens,
10
10
  extractAssistantText,
11
11
  getAgentContextCompactionTriggerTokens,
12
+ getAgentContextInputLimitTokens,
12
13
  getInterruptionMarker,
13
14
  getPiMessageRole,
14
15
  getTerminalAssistantMessages,
@@ -20,6 +21,7 @@ import {
20
21
  isTurnInputCommitLostError,
21
22
  loadConnectedMcpProviders,
22
23
  loadConversationProjection,
24
+ loadTurnRoute,
23
25
  loadTurnSessionRecord,
24
26
  mergeArtifactsState,
25
27
  normalizeToolNameFromResult,
@@ -31,11 +33,12 @@ import {
31
33
  recordAuthorizationRequested,
32
34
  recordMcpProviderConnected,
33
35
  recordToolExecutionStarted,
36
+ recordTurnRoute,
34
37
  retainRuntimeTurnContext,
35
38
  stripRuntimeTurnContext,
36
39
  trimTrailingAssistantMessages,
37
40
  updateConversationStats
38
- } from "./chunk-DXRPHBOU.js";
41
+ } from "./chunk-6QWPVTMO.js";
39
42
  import {
40
43
  buildNonInteractiveShellScript,
41
44
  createSandboxSession,
@@ -52,7 +55,7 @@ import {
52
55
  runNonInteractiveCommand,
53
56
  throwSandboxOperationError,
54
57
  wrapSandboxSetupError
55
- } from "./chunk-4VKFQPCW.js";
58
+ } from "./chunk-XLFBQJUO.js";
56
59
  import {
57
60
  addAgentTurnUsage,
58
61
  hasAgentTurnUsage
@@ -69,7 +72,7 @@ import {
69
72
  getPlugins,
70
73
  getSlackToolContext,
71
74
  resolveChannelCapabilities
72
- } from "./chunk-YJ44UT7D.js";
75
+ } from "./chunk-VNNBSKCO.js";
73
76
  import {
74
77
  SANDBOX_DATA_ROOT,
75
78
  SANDBOX_SKILLS_ROOT,
@@ -78,7 +81,7 @@ import {
78
81
  getStateAdapter,
79
82
  sandboxSkillDir,
80
83
  sandboxSkillFile
81
- } from "./chunk-HMRVILWZ.js";
84
+ } from "./chunk-YU6PO42Q.js";
82
85
  import {
83
86
  SlackActionError,
84
87
  downloadPrivateSlackFile,
@@ -88,7 +91,7 @@ import {
88
91
  isDmChannel,
89
92
  normalizeSlackConversationId,
90
93
  withSlackRetries
91
- } from "./chunk-QTS66GMR.js";
94
+ } from "./chunk-RD7EJI7H.js";
92
95
  import {
93
96
  contextProvenance,
94
97
  getConversationEventStore,
@@ -98,7 +101,7 @@ import {
98
101
  instructionActors,
99
102
  instructionProvenanceFor,
100
103
  parseDestination
101
- } from "./chunk-LLKDNPYV.js";
104
+ } from "./chunk-FIGAF6KL.js";
102
105
  import {
103
106
  sleep
104
107
  } from "./chunk-4ZNGQH7C.js";
@@ -126,11 +129,12 @@ import {
126
129
  modelIdForProfile,
127
130
  modelProfileSchema,
128
131
  normalizeSlackEmojiName,
132
+ parseTurnReasoningLevel,
129
133
  profileConfig,
130
134
  renderCurrentInstruction,
131
135
  resolveGatewayModel,
132
136
  unwrapCurrentInstruction
133
- } from "./chunk-RCBUZGIT.js";
137
+ } from "./chunk-ZGXH3REA.js";
134
138
  import {
135
139
  parseSlackThreadId,
136
140
  resolveConversationPrivacy,
@@ -146,17 +150,17 @@ import {
146
150
  findSkillByName,
147
151
  loadSkillsByName,
148
152
  parseSkillInvocation
149
- } from "./chunk-4WZ7YKDF.js";
153
+ } from "./chunk-TLGM5X2M.js";
150
154
  import {
151
155
  pluginCatalogRuntime
152
- } from "./chunk-LR2SKL5Y.js";
156
+ } from "./chunk-OSKEC4XU.js";
153
157
  import {
154
158
  CredentialUnavailableError,
155
159
  credentialContextSchema,
156
160
  credentialUserSubjectId,
157
161
  resolveAuthTokenPlaceholder,
158
162
  resolvePluginCommandEnv
159
- } from "./chunk-OXE5ZUTS.js";
163
+ } from "./chunk-K2W4CK7K.js";
160
164
  import {
161
165
  isUserActor
162
166
  } from "./chunk-C4MZ3GP5.js";
@@ -177,7 +181,7 @@ import {
177
181
  summarizeMessageText,
178
182
  withLogContext,
179
183
  withSpan
180
- } from "./chunk-36UJA3OD.js";
184
+ } from "./chunk-4FUEG3QY.js";
181
185
  import {
182
186
  isRecord,
183
187
  parseSlackChannelId,
@@ -1098,7 +1102,8 @@ function configuredTurnRoute(profile, reasoningLevel, source) {
1098
1102
  return {
1099
1103
  profile,
1100
1104
  reasoningLevel,
1101
- reason: `configured:${source}`
1105
+ reason: `configured:${source}`,
1106
+ source: source === "profile" ? "inherited" : "configured"
1102
1107
  };
1103
1108
  }
1104
1109
  async function selectTurnRoute(args) {
@@ -1158,7 +1163,7 @@ async function selectTurnRoute(args) {
1158
1163
  "gen_ai.request.reasoning.level_confidence": normalizedSelection.confidence
1159
1164
  } : {}
1160
1165
  });
1161
- return normalizedSelection;
1166
+ return { ...normalizedSelection, source: "router" };
1162
1167
  }
1163
1168
  );
1164
1169
  }
@@ -1204,20 +1209,37 @@ async function classifyTurn(args) {
1204
1209
  confidence: parsed.confidence,
1205
1210
  profile: STANDARD_MODEL_PROFILE,
1206
1211
  reasoningLevel: CLASSIFIER_FALLBACK_REASONING_LEVEL,
1207
- reason: `low_confidence_medium_default:${reason}`
1212
+ reason: `low_confidence_medium_default:${reason}`,
1213
+ source: "router"
1208
1214
  };
1209
1215
  }
1210
1216
  return {
1211
1217
  confidence: parsed.confidence,
1212
1218
  profile: parsed.profile,
1213
1219
  reasoningLevel: parsed.reasoning_level,
1214
- reason
1220
+ reason,
1221
+ source: "router"
1215
1222
  };
1216
- } catch {
1223
+ } catch (error) {
1224
+ logWarn(
1225
+ "turn_router_classifier_failed",
1226
+ {
1227
+ messageConversationId: args.metadata.threadId,
1228
+ destinationName: args.metadata.channelId,
1229
+ userId: args.metadata.actorId,
1230
+ runId: args.metadata.runId,
1231
+ modelId: args.fastModelId
1232
+ },
1233
+ {
1234
+ "exception.message": error instanceof Error ? error.message : String(error)
1235
+ },
1236
+ "Turn router classifier failed; using the default route"
1237
+ );
1217
1238
  return {
1218
1239
  profile: STANDARD_MODEL_PROFILE,
1219
1240
  reasoningLevel: CLASSIFIER_FALLBACK_REASONING_LEVEL,
1220
- reason: "classifier_error_default"
1241
+ reason: "classifier_error_default",
1242
+ source: "router"
1221
1243
  };
1222
1244
  }
1223
1245
  }
@@ -5107,6 +5129,16 @@ async function collectFiles(params) {
5107
5129
  }
5108
5130
 
5109
5131
  // src/chat/tools/sandbox/text-edits.ts
5132
+ var MAX_DIFF_LINE_CHARS = 4e3;
5133
+ function truncateDiffLine(value) {
5134
+ if (value.length <= MAX_DIFF_LINE_CHARS) {
5135
+ return { line: value, truncated: false };
5136
+ }
5137
+ return {
5138
+ line: `${value.slice(0, MAX_DIFF_LINE_CHARS)}... [line truncated]`,
5139
+ truncated: true
5140
+ };
5141
+ }
5110
5142
  function detectLineEnding(value) {
5111
5143
  return value.includes("\r\n") ? "\r\n" : "\n";
5112
5144
  }
@@ -5194,27 +5226,35 @@ function buildCompactDiff(oldContent, newContent) {
5194
5226
  const oldContextEnd = Math.min(oldLines.length - 1, oldSuffix + 3);
5195
5227
  const width = String(Math.max(oldLines.length, newLines.length)).length;
5196
5228
  const output = [];
5229
+ let lineTruncated = false;
5230
+ const pushLine = (value) => {
5231
+ const bounded2 = truncateDiffLine(value);
5232
+ output.push(bounded2.line);
5233
+ lineTruncated ||= bounded2.truncated;
5234
+ };
5197
5235
  if (contextStart > 0) {
5198
- output.push(` ${"".padStart(width)} ...`);
5236
+ pushLine(` ${"".padStart(width)} ...`);
5199
5237
  }
5200
5238
  for (let index = contextStart; index < prefix; index += 1) {
5201
- output.push(` ${String(index + 1).padStart(width)} ${oldLines[index]}`);
5239
+ pushLine(` ${String(index + 1).padStart(width)} ${oldLines[index]}`);
5202
5240
  }
5203
5241
  for (let index = prefix; index <= oldSuffix; index += 1) {
5204
- output.push(`-${String(index + 1).padStart(width)} ${oldLines[index]}`);
5242
+ pushLine(`-${String(index + 1).padStart(width)} ${oldLines[index]}`);
5205
5243
  }
5206
5244
  for (let index = prefix; index <= newSuffix; index += 1) {
5207
- output.push(`+${String(index + 1).padStart(width)} ${newLines[index]}`);
5245
+ pushLine(`+${String(index + 1).padStart(width)} ${newLines[index]}`);
5208
5246
  }
5209
5247
  for (let index = newSuffix + 1; index <= newContextEnd; index += 1) {
5210
- output.push(` ${String(index + 1).padStart(width)} ${newLines[index]}`);
5248
+ pushLine(` ${String(index + 1).padStart(width)} ${newLines[index]}`);
5211
5249
  }
5212
5250
  if (newContextEnd < newLines.length - 1 || oldContextEnd < oldLines.length - 1) {
5213
- output.push(` ${"".padStart(width)} ...`);
5251
+ pushLine(` ${"".padStart(width)} ...`);
5214
5252
  }
5253
+ const bounded = truncateText(output.join("\n"), MAX_TEXT_CHARS);
5215
5254
  return {
5216
- diff: output.join("\n"),
5217
- firstChangedLine: firstChangedLine(oldContent, newContent)
5255
+ diff: bounded.content,
5256
+ firstChangedLine: firstChangedLine(oldContent, newContent),
5257
+ truncated: lineTruncated || bounded.truncated
5218
5258
  };
5219
5259
  }
5220
5260
  function validateAndApplyTextEdits(content, edits, targetName) {
@@ -5443,7 +5483,6 @@ async function editFile(params) {
5443
5483
  status: "success",
5444
5484
  target: params.path,
5445
5485
  data: {
5446
- diff: diff.diff,
5447
5486
  first_changed_line: diff.firstChangedLine,
5448
5487
  path: params.path,
5449
5488
  replacements: params.edits.length
@@ -5451,7 +5490,8 @@ async function editFile(params) {
5451
5490
  diff: diff.diff,
5452
5491
  first_changed_line: diff.firstChangedLine,
5453
5492
  path: params.path,
5454
- replacements: params.edits.length
5493
+ replacements: params.edits.length,
5494
+ truncated: diff.truncated
5455
5495
  });
5456
5496
  }
5457
5497
  var editReplacementSchema = z5.object({
@@ -14860,16 +14900,24 @@ function createResumeState(args) {
14860
14900
  }
14861
14901
 
14862
14902
  // src/chat/services/context-compaction.ts
14863
- import {
14864
- estimateContextTokens,
14865
- estimateTokens
14866
- } from "@earendil-works/pi-agent-core";
14867
14903
  var RETAINED_USER_MESSAGE_TOKENS = 2e4;
14868
14904
  var MAX_SUMMARY_INPUT_CHARS = 8e4;
14869
14905
  var MAX_VISIBLE_CONTEXT_CHARS = 2e4;
14870
14906
  var MAX_SUMMARY_CHARS = 6e3;
14871
14907
  var MAX_RENDERED_MESSAGE_CHARS = 4e3;
14872
14908
  var OMITTED_OLDER_CONTEXT_NOTICE = "[older context omitted]";
14909
+ var ContextInputLimitExceededError = class extends Error {
14910
+ constructor(estimatedTokens, inputLimitTokens) {
14911
+ super(
14912
+ `Agent context is ${estimatedTokens} estimated input tokens, above the ${inputLimitTokens} token limit`
14913
+ );
14914
+ this.estimatedTokens = estimatedTokens;
14915
+ this.inputLimitTokens = inputLimitTokens;
14916
+ this.name = "ContextInputLimitExceededError";
14917
+ }
14918
+ estimatedTokens;
14919
+ inputLimitTokens;
14920
+ };
14873
14921
  function textPart(value) {
14874
14922
  if (value && typeof value === "object" && value.type === "text" && typeof value.text === "string") {
14875
14923
  return value.text;
@@ -15030,13 +15078,10 @@ async function summarizeContext(args, deps) {
15030
15078
  return summary.slice(0, MAX_SUMMARY_CHARS);
15031
15079
  }
15032
15080
  function estimateHistoryTokens(messages) {
15033
- const stripped = stripRuntimeTurnContext(messages);
15034
- const usageEstimate = estimateContextTokens(stripped).tokens;
15035
- const structuralEstimate = stripped.reduce(
15036
- (total, message) => total + estimateTokens(message),
15081
+ return messages.reduce(
15082
+ (total, message) => total + estimateTextTokens(JSON.stringify(message) ?? ""),
15037
15083
  0
15038
15084
  );
15039
- return Math.max(usageEstimate, structuralEstimate);
15040
15085
  }
15041
15086
  function buildReplacementProvenance(args) {
15042
15087
  return [
@@ -15060,11 +15105,11 @@ async function maybeCompactWithDeps(args, deps) {
15060
15105
  if ("reason" in source) {
15061
15106
  return { compacted: false, reason: source.reason };
15062
15107
  }
15063
- const triggerTokens = deps.autoCompactionTriggerTokens ?? getAgentContextCompactionTriggerTokens();
15108
+ const triggerTokens = deps.autoCompactionTriggerTokens ?? getAgentContextCompactionTriggerTokens(args.modelId);
15064
15109
  if (source.estimatedTokens <= triggerTokens) {
15065
15110
  return { compacted: false, reason: "below_threshold" };
15066
15111
  }
15067
- args.onCompactionStart?.();
15112
+ await args.onCompactionStart?.();
15068
15113
  let summary;
15069
15114
  try {
15070
15115
  summary = await summarizeContext(
@@ -15095,7 +15140,8 @@ async function maybeCompactWithDeps(args, deps) {
15095
15140
  }
15096
15141
  return await writeCompactedThreadContext(args, source.messages, summary, {
15097
15142
  estimatedTokens: source.estimatedTokens,
15098
- triggerTokens
15143
+ triggerTokens,
15144
+ inputLimitTokens: getAgentContextInputLimitTokens(args.modelId)
15099
15145
  });
15100
15146
  }
15101
15147
  async function writeCompactedThreadContext(args, sourceMessages, summary, context) {
@@ -15111,6 +15157,7 @@ async function writeCompactedThreadContext(args, sourceMessages, summary, contex
15111
15157
  userMessage(`${COMPACTION_SUMMARY_PREFIX}
15112
15158
  ${summary}`)
15113
15159
  ];
15160
+ const replacementInputTokens = estimateHistoryTokens(replacement);
15114
15161
  const replacementProvenance = buildReplacementProvenance({
15115
15162
  retained,
15116
15163
  sourceProvenance: sourceProjection.provenance
@@ -15121,6 +15168,16 @@ ${summary}`)
15121
15168
  type: "compaction",
15122
15169
  modelProfile: sourceProjection.modelProfile,
15123
15170
  modelId: modelIdForProfile(botConfig, sourceProjection.modelProfile),
15171
+ details: {
15172
+ reason: "capacity",
15173
+ estimatedInputTokens: context.estimatedTokens,
15174
+ replacementInputTokens,
15175
+ triggerTokens: context.triggerTokens ?? getAgentContextCompactionTriggerTokens(args.modelId),
15176
+ inputLimitTokens: context.inputLimitTokens,
15177
+ inputMessageCount: sourceMessages.length,
15178
+ retainedMessageCount: replacement.length - 1,
15179
+ summaryChars: summary.length
15180
+ },
15124
15181
  replacementHistory: replacement.map((message, index) => {
15125
15182
  const sourceEventSeq = index < retained.length ? sourceProjection.seqs[retained[index].sourceIndex] : void 0;
15126
15183
  return {
@@ -15182,6 +15239,114 @@ ${await summarizeContext(args, deps)}`;
15182
15239
  });
15183
15240
  return messages;
15184
15241
  }
15242
+ async function compactActiveContextIfNeeded(args, deps) {
15243
+ const pendingMessages = args.pendingMessages ?? [];
15244
+ const source = loadCompactionSource([
15245
+ ...args.piMessages,
15246
+ ...pendingMessages.map((entry) => entry.message)
15247
+ ]);
15248
+ if ("reason" in source) {
15249
+ return { compacted: false, reason: source.reason };
15250
+ }
15251
+ const triggerTokens = getAgentContextCompactionTriggerTokens(args.modelId);
15252
+ if (source.estimatedTokens <= triggerTokens) {
15253
+ return { compacted: false, reason: "below_threshold" };
15254
+ }
15255
+ await args.onCompactionStart?.();
15256
+ const inputLimitTokens = getAgentContextInputLimitTokens(args.modelId);
15257
+ let summary;
15258
+ try {
15259
+ summary = await summarizeContext(
15260
+ {
15261
+ ...args,
15262
+ piMessages: args.piMessages
15263
+ },
15264
+ deps
15265
+ );
15266
+ } catch (error) {
15267
+ if (source.estimatedTokens >= inputLimitTokens) {
15268
+ throw new ContextInputLimitExceededError(
15269
+ source.estimatedTokens,
15270
+ inputLimitTokens
15271
+ );
15272
+ }
15273
+ logWarn(
15274
+ "active_context_compaction_summary_failed",
15275
+ {
15276
+ messageConversationId: args.metadata?.threadId,
15277
+ userId: args.metadata?.actorId,
15278
+ destinationName: args.metadata?.channelId,
15279
+ runId: args.metadata?.runId,
15280
+ assistantUserName: botConfig.userName,
15281
+ modelId: args.modelId
15282
+ },
15283
+ {
15284
+ "app.context_input_limit_tokens": inputLimitTokens,
15285
+ "app.context_tokens_estimated": source.estimatedTokens,
15286
+ "exception.message": error instanceof Error ? error.message : String(error)
15287
+ },
15288
+ "Active context compaction failed below the hard input limit"
15289
+ );
15290
+ return { compacted: false, reason: "summary_failed" };
15291
+ }
15292
+ const summaryText = `${COMPACTION_SUMMARY_PREFIX}
15293
+ ${summary}`;
15294
+ const runtimeMessage = retainRuntimeTurnContext(args.piMessages).at(-1);
15295
+ const summaryMessages = runtimeMessage ? [
15296
+ {
15297
+ ...runtimeMessage,
15298
+ content: [
15299
+ ...runtimeMessage.content,
15300
+ { type: "text", text: renderCurrentInstruction(summaryText) }
15301
+ ]
15302
+ }
15303
+ ] : [userMessage(renderCurrentInstruction(summaryText))];
15304
+ const messages = [
15305
+ ...summaryMessages,
15306
+ ...pendingMessages.map((entry) => entry.message)
15307
+ ];
15308
+ const replacementInputTokens = estimateHistoryTokens(messages);
15309
+ if (replacementInputTokens >= inputLimitTokens) {
15310
+ throw new ContextInputLimitExceededError(
15311
+ replacementInputTokens,
15312
+ inputLimitTokens
15313
+ );
15314
+ }
15315
+ const replacementMessages = stripRuntimeTurnContext(messages);
15316
+ args.signal?.throwIfAborted();
15317
+ await getConversationEventStore().replaceHistory(args.conversationId, {
15318
+ createdAtMs: Date.now(),
15319
+ data: {
15320
+ type: "compaction",
15321
+ modelProfile: args.modelProfile,
15322
+ modelId: args.modelId,
15323
+ details: {
15324
+ reason: "capacity",
15325
+ estimatedInputTokens: source.estimatedTokens,
15326
+ replacementInputTokens,
15327
+ triggerTokens,
15328
+ inputLimitTokens,
15329
+ inputMessageCount: source.messages.length,
15330
+ retainedMessageCount: pendingMessages.length,
15331
+ summaryChars: summary.length
15332
+ },
15333
+ replacementHistory: replacementMessages.map((message, index) => ({
15334
+ message,
15335
+ provenance: index === 0 ? contextProvenance : pendingMessages[index - 1]?.provenance ?? contextProvenance
15336
+ }))
15337
+ }
15338
+ });
15339
+ setSpanAttributes({
15340
+ "app.compaction.input_messages": source.messages.length,
15341
+ "app.compaction.replacement_tokens": replacementInputTokens,
15342
+ "app.compaction.retained_messages": pendingMessages.length,
15343
+ "app.compaction.summary_chars": summary.length,
15344
+ "app.compaction.trigger_tokens": triggerTokens,
15345
+ "app.context_input_limit_tokens": inputLimitTokens,
15346
+ "app.context_tokens_estimated": source.estimatedTokens
15347
+ });
15348
+ return { compacted: true, piMessages: messages };
15349
+ }
15185
15350
 
15186
15351
  // src/chat/agent/index.ts
15187
15352
  var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
@@ -15280,7 +15445,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15280
15445
  let mcpToolManager;
15281
15446
  let connectedMcpProviders = /* @__PURE__ */ new Set();
15282
15447
  let turnUsage;
15283
- let handoffPhaseUsage;
15448
+ let priorPhaseUsage;
15284
15449
  const configuredReasoningLevel = policy.reasoningLevel ?? botConfig.reasoningLevel;
15285
15450
  let turnRoute = configuredReasoningLevel ? configuredTurnRoute(
15286
15451
  STANDARD_MODEL_PROFILE,
@@ -15331,6 +15496,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15331
15496
  const projection = await openConversationProjection({ conversationId });
15332
15497
  activeModelProfile = projection.modelProfile;
15333
15498
  activeModelId = modelIdForProfile(botConfig, activeModelProfile);
15499
+ let durableModelProfile = projection.modelProfile;
15334
15500
  const shouldTrace = shouldEmitDevAgentTrace();
15335
15501
  const spanContext = { modelId: activeModelId };
15336
15502
  const availableSkills = await discoverRunSkills({
@@ -15435,7 +15601,30 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15435
15601
  });
15436
15602
  const { routerBlocks, userContentParts } = buildPromptInput(input);
15437
15603
  const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...input.piMessages ?? []];
15438
- if (activeModelProfile === STANDARD_MODEL_PROFILE) {
15604
+ const storedTurnRoute = await loadTurnRoute({ conversationId, turnId });
15605
+ if (storedTurnRoute) {
15606
+ const resumedAfterHandoff = activeModelProfile !== STANDARD_MODEL_PROFILE && activeModelProfile !== storedTurnRoute.modelProfile;
15607
+ if (resumedAfterHandoff) {
15608
+ const activeProfileConfig = profileConfig(
15609
+ botConfig,
15610
+ activeModelProfile
15611
+ );
15612
+ const resumedReasoningLevel = existingSessionRecord?.reasoningLevel ? parseTurnReasoningLevel(existingSessionRecord.reasoningLevel) : void 0;
15613
+ turnRoute = {
15614
+ profile: activeModelProfile,
15615
+ reasoningLevel: activeProfileConfig.reasoningLevel ?? resumedReasoningLevel ?? storedTurnRoute.reasoningLevel,
15616
+ reason: `resumed_handoff:${storedTurnRoute.modelProfile}:${activeModelProfile}`
15617
+ };
15618
+ } else {
15619
+ turnRoute = {
15620
+ profile: storedTurnRoute.modelProfile,
15621
+ reasoningLevel: storedTurnRoute.reasoningLevel,
15622
+ ...storedTurnRoute.confidence !== void 0 ? { confidence: storedTurnRoute.confidence } : {},
15623
+ reason: `persisted:${storedTurnRoute.source}`,
15624
+ source: storedTurnRoute.source
15625
+ };
15626
+ }
15627
+ } else if (activeModelProfile === STANDARD_MODEL_PROFILE) {
15439
15628
  turnRoute = await selectTurnRoute({
15440
15629
  completeObject,
15441
15630
  conversationContext: input.conversationContext,
@@ -15450,7 +15639,11 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15450
15639
  messageText: userInput,
15451
15640
  profiles: botConfig.profiles
15452
15641
  });
15453
- if (configuredReasoningLevel) {
15642
+ const routedProfileReasoningLevel = profileConfig(
15643
+ botConfig,
15644
+ turnRoute.profile
15645
+ ).reasoningLevel;
15646
+ if (configuredReasoningLevel && !routedProfileReasoningLevel) {
15454
15647
  turnRoute = {
15455
15648
  ...turnRoute,
15456
15649
  reasoningLevel: configuredReasoningLevel,
@@ -15459,12 +15652,29 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15459
15652
  }
15460
15653
  } else {
15461
15654
  const activeProfileConfig = profileConfig(botConfig, activeModelProfile);
15462
- turnRoute = configuredTurnRoute(
15463
- activeModelProfile,
15464
- activeProfileConfig.reasoningLevel ?? policy.reasoningLevel ?? botConfig.reasoningLevel ?? "medium",
15465
- activeProfileConfig.reasoningLevel ? "profile" : policy.reasoningLevel ? "agent_config" : "default"
15466
- );
15655
+ const reasoningSource = activeProfileConfig.reasoningLevel ? "profile" : policy.reasoningLevel ? "agent_config" : "default";
15656
+ turnRoute = {
15657
+ profile: activeModelProfile,
15658
+ reasoningLevel: activeProfileConfig.reasoningLevel ?? policy.reasoningLevel ?? botConfig.reasoningLevel ?? "medium",
15659
+ reason: `inherited:${reasoningSource}`,
15660
+ source: "inherited"
15661
+ };
15467
15662
  }
15663
+ const routedModelProfile = turnRoute.profile;
15664
+ const routedModelId = modelIdForProfile(botConfig, routedModelProfile);
15665
+ if (!storedTurnRoute) {
15666
+ await recordTurnRoute({
15667
+ conversationId,
15668
+ turnId,
15669
+ modelProfile: routedModelProfile,
15670
+ modelId: routedModelId,
15671
+ reasoningLevel: turnRoute.reasoningLevel,
15672
+ ...turnRoute.confidence !== void 0 ? { confidence: turnRoute.confidence } : {},
15673
+ source: turnRoute.source ?? "configured"
15674
+ });
15675
+ }
15676
+ activeModelProfile = routedModelProfile;
15677
+ activeModelId = routedModelId;
15468
15678
  const generatedFiles = [];
15469
15679
  const artifactStatePatch = {};
15470
15680
  const toolCalls = [];
@@ -15477,17 +15687,20 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15477
15687
  (profile) => profile !== STANDARD_MODEL_PROFILE && profile !== DEFAULT_HANDOFF_MODEL_PROFILE
15478
15688
  ).sort()
15479
15689
  ];
15690
+ const usageSinceCurrentBoundary = (messages) => {
15691
+ const usage = extractGenAiUsageSummary(
15692
+ ...messages.slice(runResume.beforeMessageCount).filter(isAssistantMessage)
15693
+ );
15694
+ return hasAgentTurnUsage(usage) ? usage : void 0;
15695
+ };
15480
15696
  const scheduleHandoff = async (args) => {
15481
- if (args.profile === activeModelProfile) {
15697
+ if (args.profile === durableModelProfile) {
15482
15698
  return;
15483
15699
  }
15484
15700
  const runtimeContext = retainRuntimeTurnContext(
15485
15701
  args.runtimeContextSourceMessages ?? args.sourceMessages
15486
15702
  );
15487
- const standardPhaseUsage = extractGenAiUsageSummary(
15488
- ...args.sourceMessages.slice(runResume.beforeMessageCount).filter(isAssistantMessage)
15489
- );
15490
- const phaseUsage = hasAgentTurnUsage(standardPhaseUsage) ? standardPhaseUsage : void 0;
15703
+ const phaseUsage = usageSinceCurrentBoundary(args.sourceMessages);
15491
15704
  const selectedProfile = profileConfig(botConfig, args.profile);
15492
15705
  const handoffReasoningLevel = selectedProfile.reasoningLevel ?? turnRoute.reasoningLevel;
15493
15706
  const target = {
@@ -15525,8 +15738,11 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15525
15738
  runId
15526
15739
  }
15527
15740
  },
15528
- { completeText }
15741
+ {
15742
+ completeText: (args2) => completeText(args2)
15743
+ }
15529
15744
  );
15745
+ durableModelProfile = args.profile;
15530
15746
  if (handoffReasoningLevel !== turnRoute.reasoningLevel) {
15531
15747
  turnRoute = {
15532
15748
  ...turnRoute,
@@ -15534,7 +15750,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15534
15750
  reason: `profile_reasoning_override:${args.profile}:${turnRoute.reason}`
15535
15751
  };
15536
15752
  }
15537
- handoffPhaseUsage = phaseUsage;
15753
+ priorPhaseUsage = addAgentTurnUsage(priorPhaseUsage, phaseUsage);
15538
15754
  pendingHandoff = {
15539
15755
  messages: handoffMessages,
15540
15756
  model: handoffModel,
@@ -15647,6 +15863,52 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15647
15863
  thinkingLevel
15648
15864
  };
15649
15865
  };
15866
+ const applyActiveContextCompaction = async (messages, hookSignal, pendingMessages, clearSteeringQueueOnCommit = false) => {
15867
+ const compaction = await compactActiveContextIfNeeded(
15868
+ {
15869
+ conversationContext: input.conversationContext,
15870
+ conversationId,
15871
+ metadata: {
15872
+ threadId: conversationId,
15873
+ channelId: slackDestination?.channelId,
15874
+ actorId: slackActor?.userId,
15875
+ runId
15876
+ },
15877
+ modelId: activeModelId,
15878
+ modelProfile: activeModelProfile,
15879
+ onCompactionStart: () => observers.onStatus?.({ text: "Compacting context" }),
15880
+ pendingMessages,
15881
+ piMessages: messages,
15882
+ signal: hookSignal
15883
+ },
15884
+ {
15885
+ completeText: (args) => completeText(args)
15886
+ }
15887
+ );
15888
+ if (!compaction.compacted || !compaction.piMessages) {
15889
+ return void 0;
15890
+ }
15891
+ priorPhaseUsage = addAgentTurnUsage(
15892
+ priorPhaseUsage,
15893
+ usageSinceCurrentBoundary(messages)
15894
+ );
15895
+ const replacement = [...compaction.piMessages];
15896
+ await runResume.requireDurableInputCheckpoint(replacement);
15897
+ agent.state.messages = replacement;
15898
+ runResume.setBeforeMessageCount(replacement.length);
15899
+ runResume.setTurnStartMessageIndex(0);
15900
+ runResume.adoptCommittedBoundary(replacement);
15901
+ if (clearSteeringQueueOnCommit && pendingMessages?.length) {
15902
+ agent.clearSteeringQueue();
15903
+ }
15904
+ return {
15905
+ context: {
15906
+ systemPrompt: baseInstructions,
15907
+ messages: replacement,
15908
+ tools: agent.state.tools
15909
+ }
15910
+ };
15911
+ };
15650
15912
  let assistantMessageDeliveryError;
15651
15913
  const deliverAssistantMessage = async (message) => {
15652
15914
  const text = getAssistantMessageText(message);
@@ -15664,8 +15926,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15664
15926
  };
15665
15927
  const drainSteeringMessages = async () => {
15666
15928
  if (!durability.drainSteeringMessages) {
15667
- return;
15929
+ return [];
15668
15930
  }
15931
+ const acceptedMessages = [];
15669
15932
  try {
15670
15933
  let steeredMessageCount = 0;
15671
15934
  await durability.drainSteeringMessages(async (messages) => {
@@ -15683,6 +15946,12 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15683
15946
  for (const message of piMessages) {
15684
15947
  agent.steer(message);
15685
15948
  }
15949
+ acceptedMessages.push(
15950
+ ...piMessages.map((message, index) => ({
15951
+ message,
15952
+ provenance: messages[index].provenance
15953
+ }))
15954
+ );
15686
15955
  steeredMessageCount += piMessages.length;
15687
15956
  });
15688
15957
  if (steeredMessageCount > 0) {
@@ -15708,6 +15977,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15708
15977
  "Agent turn steering message drain failed"
15709
15978
  );
15710
15979
  }
15980
+ return acceptedMessages;
15711
15981
  };
15712
15982
  const apiKeyOverride = getPiGatewayApiKey();
15713
15983
  let pendingPiHookError;
@@ -15731,15 +16001,27 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15731
16001
  return void 0;
15732
16002
  },
15733
16003
  afterToolCall: async ({ result: result2 }, signal2) => runResume.timedOut && signal2?.aborted ? annotateTurnDeadlineToolResult(result2) : void 0,
15734
- prepareNextTurn: async () => {
15735
- const update = applyPendingHandoff();
15736
- await drainSteeringMessages();
15737
- const yieldError = runResume.prepareYieldIfDue(currentAgentMessages());
15738
- if (yieldError) {
15739
- pendingPiHookError = yieldError;
15740
- throw yieldError;
16004
+ prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
16005
+ try {
16006
+ const handoffUpdate = applyPendingHandoff();
16007
+ const pendingMessages = await drainSteeringMessages();
16008
+ const capacityUpdate = await applyActiveContextCompaction(
16009
+ handoffUpdate ? currentAgentMessages() : nextTurn.context.messages,
16010
+ hookSignal,
16011
+ pendingMessages,
16012
+ true
16013
+ );
16014
+ const yieldError = runResume.prepareYieldIfDue(
16015
+ currentAgentMessages()
16016
+ );
16017
+ if (yieldError) {
16018
+ throw yieldError;
16019
+ }
16020
+ return capacityUpdate && handoffUpdate ? { ...handoffUpdate, ...capacityUpdate } : capacityUpdate ?? handoffUpdate;
16021
+ } catch (error) {
16022
+ pendingPiHookError = error instanceof Error ? error : new Error(String(error));
16023
+ throw error;
15741
16024
  }
15742
- return update;
15743
16025
  },
15744
16026
  initialState: {
15745
16027
  systemPrompt: baseInstructions,
@@ -15904,6 +16186,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15904
16186
  };
15905
16187
  const requestedProfile = activeModelProfile === STANDARD_MODEL_PROFILE ? turnRoute.profile : void 0;
15906
16188
  let run;
16189
+ let handoffApplied = false;
15907
16190
  if (requestedProfile && requestedProfile !== STANDARD_MODEL_PROFILE) {
15908
16191
  const handoffAbortController = new AbortController();
15909
16192
  await runAgentStep(
@@ -15915,19 +16198,29 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15915
16198
  }),
15916
16199
  () => handoffAbortController.abort()
15917
16200
  );
15918
- applyPendingHandoff();
15919
- if (shouldPromptAgent) {
15920
- await runResume.requireDurableInputCheckpoint([
15921
- ...agent.state.messages,
15922
- freshPromptMessage
15923
- ]);
15924
- run = agent.prompt(freshPromptMessage);
15925
- } else {
15926
- run = agent.continue();
15927
- }
15928
- } else {
15929
- run = shouldPromptAgent ? agent.prompt(freshPromptMessage) : agent.continue();
16201
+ handoffApplied = Boolean(applyPendingHandoff());
15930
16202
  }
16203
+ const compactionAbortController = new AbortController();
16204
+ const capacityUpdate = await runAgentStep(
16205
+ applyActiveContextCompaction(
16206
+ [...agent.state.messages],
16207
+ compactionAbortController.signal,
16208
+ shouldPromptAgent ? [
16209
+ {
16210
+ message: freshPromptMessage,
16211
+ provenance: instructionProvenanceFor(actor)
16212
+ }
16213
+ ] : void 0
16214
+ ),
16215
+ () => compactionAbortController.abort()
16216
+ );
16217
+ if (shouldPromptAgent && handoffApplied && !capacityUpdate) {
16218
+ await runResume.requireDurableInputCheckpoint([
16219
+ ...agent.state.messages,
16220
+ freshPromptMessage
16221
+ ]);
16222
+ }
16223
+ run = shouldPromptAgent && !capacityUpdate ? agent.prompt(freshPromptMessage) : agent.continue();
15931
16224
  let retryUsage;
15932
16225
  try {
15933
16226
  for (let attempt = 0; ; attempt += 1) {
@@ -15950,10 +16243,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15950
16243
  retryUsage,
15951
16244
  currentUsage
15952
16245
  );
15953
- turnUsage = addAgentTurnUsage(
15954
- handoffPhaseUsage,
15955
- currentPhaseUsage
15956
- );
16246
+ turnUsage = addAgentTurnUsage(priorPhaseUsage, currentPhaseUsage);
15957
16247
  setSpanAttributes({
15958
16248
  ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
15959
16249
  ...toGenAiMessagesTraceAttributes(