@sentry/junior 0.121.0 → 0.122.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 (64) hide show
  1. package/dist/{agent-hooks-CQDGDBCP.js → agent-hooks-HJMLQLCB.js} +8 -8
  2. package/dist/api.js +14 -14
  3. package/dist/app.js +604 -997
  4. package/dist/{catalog-runtime-AD3YNTTM.js → catalog-runtime-CYPZUYJD.js} +5 -5
  5. package/dist/chat/agent/index.d.ts +19 -2
  6. package/dist/chat/agent/request.d.ts +8 -8
  7. package/dist/chat/agent/resume.d.ts +1 -3
  8. package/dist/chat/agent/retry-usage.d.ts +8 -0
  9. package/dist/chat/agent/skills.d.ts +0 -9
  10. package/dist/chat/conversations/messages.d.ts +7 -0
  11. package/dist/chat/conversations/projection.d.ts +16 -0
  12. package/dist/chat/logging.d.ts +46 -13
  13. package/dist/chat/runtime/processing-reaction.d.ts +2 -4
  14. package/dist/chat/runtime/slack-runtime.d.ts +2 -2
  15. package/dist/chat/services/assistant-reply.d.ts +16 -0
  16. package/dist/chat/services/empty-output-continuation.d.ts +20 -0
  17. package/dist/chat/services/turn-failure-response.d.ts +1 -3
  18. package/dist/chat/services/turn-result.d.ts +0 -5
  19. package/dist/chat/services/turn-session-record.d.ts +0 -11
  20. package/dist/chat/tool-support/zod-tool.d.ts +11 -6
  21. package/dist/chat/tools/execute-tool.d.ts +1 -33
  22. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  23. package/dist/chat/tools/query-conversation-events.d.ts +3 -1
  24. package/dist/chat/tools/sandbox/bash.d.ts +1 -33
  25. package/dist/chat/tools/sandbox/edit-file.d.ts +1 -33
  26. package/dist/chat/tools/sandbox/find-files.d.ts +1 -33
  27. package/dist/chat/tools/sandbox/grep.d.ts +1 -33
  28. package/dist/chat/tools/sandbox/list-dir.d.ts +1 -33
  29. package/dist/chat/tools/sandbox/read-file.d.ts +1 -33
  30. package/dist/chat/tools/sandbox/write-file.d.ts +1 -33
  31. package/dist/{chunk-IDXLBKPS.js → chunk-2RFM7STT.js} +11 -22
  32. package/dist/{chunk-XDRGYDED.js → chunk-32FPGM2V.js} +1 -1
  33. package/dist/{chunk-JZ47AFSH.js → chunk-4SPRQJOW.js} +37 -45
  34. package/dist/{chunk-KLUMVJRQ.js → chunk-4TGVC42X.js} +133 -206
  35. package/dist/{chunk-UXQ5XSA2.js → chunk-BDE5RZVF.js} +2 -2
  36. package/dist/{chunk-ITMEXX5H.js → chunk-CYW4M6IV.js} +4 -4
  37. package/dist/{chunk-254MFDOF.js → chunk-DOB5RVMH.js} +2 -2
  38. package/dist/{chunk-TRECK7RV.js → chunk-E5SRLZQV.js} +14 -29
  39. package/dist/{chunk-LE77HHF3.js → chunk-GFTZKOTP.js} +1 -1
  40. package/dist/{chunk-ET2SDCNV.js → chunk-GN6WW7WR.js} +16 -31
  41. package/dist/{chunk-D2EMOH3N.js → chunk-ISQYBTPG.js} +3 -3
  42. package/dist/{chunk-IK4QSS5H.js → chunk-L2KJ2GNL.js} +1 -1
  43. package/dist/{chunk-VWBKDPMQ.js → chunk-NIJG6G6X.js} +472 -684
  44. package/dist/{chunk-Z2XXPUZG.js → chunk-NNGYYGX5.js} +2061 -2015
  45. package/dist/{chunk-WUP7NWMP.js → chunk-OKUZIXQM.js} +1 -1
  46. package/dist/{chunk-HDFGNU5E.js → chunk-S7G34U75.js} +3 -1
  47. package/dist/{chunk-UURFXX66.js → chunk-TZVZCIYL.js} +1 -1
  48. package/dist/{chunk-7HYV7KFM.js → chunk-USKP7IM2.js} +49 -92
  49. package/dist/{chunk-JYDIBXFY.js → chunk-V6XPIKYA.js} +13 -19
  50. package/dist/{chunk-3DBMANUI.js → chunk-VCC7XFX2.js} +22 -45
  51. package/dist/cli/chat.js +22 -22
  52. package/dist/cli/check.js +6 -6
  53. package/dist/cli/plugins.js +11 -11
  54. package/dist/cli/snapshot-create.js +9 -9
  55. package/dist/cli/upgrade.js +6 -6
  56. package/dist/{db-7HQT2SXQ.js → db-E6O2DXUQ.js} +6 -6
  57. package/dist/instrumentation.js +3 -3
  58. package/dist/nitro.js +2 -2
  59. package/dist/{oauth-callback-server-VHTUCNHK.js → oauth-callback-server-6SCPUFFD.js} +15 -15
  60. package/dist/{runner-3ONPZRED.js → runner-JFKCMVVY.js} +62 -51
  61. package/dist/{skills-WGAYGGPA.js → skills-RD66F2OB.js} +6 -6
  62. package/dist/{validation-ZIWNBG4D.js → validation-XVRNX6IU.js} +6 -6
  63. package/dist/version.js +1 -1
  64. package/package.json +8 -8
@@ -30,15 +30,18 @@ import {
30
30
  toMcpProviderError,
31
31
  toolInvocationDestination,
32
32
  uploadFilesToConversation
33
- } from "./chunk-3DBMANUI.js";
33
+ } from "./chunk-VCC7XFX2.js";
34
34
  import {
35
35
  CooperativeTurnYieldError,
36
36
  JUNIOR_THREAD_STATE_TTL_MS,
37
+ NO_REPLY_MARKER,
37
38
  TURN_CONTEXT_TAG,
38
39
  TurnInputCommitLostError,
39
40
  TurnSliceLimitExceededError,
40
41
  abandonReplacedPendingAuth,
41
42
  canReusePendingAuthLink,
43
+ containsNoReplyMarker,
44
+ decideReply,
42
45
  estimateTextTokens,
43
46
  extractAssistantText,
44
47
  getAgentContextCompactionTriggerTokens,
@@ -49,6 +52,7 @@ import {
49
52
  historyItemFromPiMessage,
50
53
  isAssistantMessage,
51
54
  isContinuablePiBoundary,
55
+ isNoReplyMarker,
52
56
  isToolResultError,
53
57
  isToolResultMessage,
54
58
  isTurnInputCommitLostError,
@@ -69,10 +73,11 @@ import {
69
73
  recordToolExecutionStarted,
70
74
  recordTurnRoute,
71
75
  retainRuntimeTurnContext,
76
+ sanitizeAssistantText,
72
77
  stripRuntimeTurnContext,
73
78
  trimTrailingAssistantMessages,
74
79
  updateConversationStats
75
- } from "./chunk-Z2XXPUZG.js";
80
+ } from "./chunk-NNGYYGX5.js";
76
81
  import {
77
82
  parseSandboxEgressAuthRequiredSignal,
78
83
  parseSandboxEgressPermissionDeniedSignal,
@@ -96,10 +101,10 @@ import {
96
101
  runNonInteractiveCommand,
97
102
  throwSandboxOperationError,
98
103
  wrapSandboxSetupError
99
- } from "./chunk-ITMEXX5H.js";
104
+ } from "./chunk-CYW4M6IV.js";
100
105
  import {
101
106
  incrementStat
102
- } from "./chunk-XDRGYDED.js";
107
+ } from "./chunk-32FPGM2V.js";
103
108
  import {
104
109
  addAgentTurnUsage,
105
110
  hasAgentTurnUsage
@@ -121,7 +126,7 @@ import {
121
126
  normalizeSlackConversationId,
122
127
  resolveChannelCapabilities,
123
128
  withSlackRetries
124
- } from "./chunk-7HYV7KFM.js";
129
+ } from "./chunk-USKP7IM2.js";
125
130
  import {
126
131
  KNOWN_CONVERSATION_EVENT_TYPES,
127
132
  contextProvenance,
@@ -130,7 +135,7 @@ import {
130
135
  getConversationStore,
131
136
  instructionActors,
132
137
  instructionProvenanceFor
133
- } from "./chunk-254MFDOF.js";
138
+ } from "./chunk-DOB5RVMH.js";
134
139
  import {
135
140
  SANDBOX_DATA_ROOT,
136
141
  SANDBOX_SKILLS_ROOT,
@@ -140,7 +145,7 @@ import {
140
145
  sandboxSkillFile,
141
146
  sandboxSkillPathResolution,
142
147
  sleep
143
- } from "./chunk-UURFXX66.js";
148
+ } from "./chunk-TZVZCIYL.js";
144
149
  import {
145
150
  ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
146
151
  COMPACTION_SUMMARY_PREFIX,
@@ -171,7 +176,7 @@ import {
171
176
  renderCurrentInstruction,
172
177
  resolveGatewayModel,
173
178
  unwrapCurrentInstruction
174
- } from "./chunk-IDXLBKPS.js";
179
+ } from "./chunk-2RFM7STT.js";
175
180
  import {
176
181
  canExposeConversationPayload,
177
182
  parseSlackThreadId,
@@ -182,16 +187,16 @@ import {
182
187
  toGenAiMessagesTraceAttributes,
183
188
  toGenAiPayloadMetadata,
184
189
  toGenAiPayloadTraceAttributes
185
- } from "./chunk-HDFGNU5E.js";
190
+ } from "./chunk-S7G34U75.js";
186
191
  import {
187
192
  discoverSkills,
188
193
  findSkillByName,
189
194
  loadSkillsByName,
190
195
  parseSkillInvocation
191
- } from "./chunk-TRECK7RV.js";
196
+ } from "./chunk-E5SRLZQV.js";
192
197
  import {
193
198
  pluginCatalogRuntime
194
- } from "./chunk-WUP7NWMP.js";
199
+ } from "./chunk-OKUZIXQM.js";
195
200
  import {
196
201
  CredentialUnavailableError,
197
202
  OAuthProviderError,
@@ -199,7 +204,7 @@ import {
199
204
  getOAuthProviderErrorAttributes,
200
205
  resolveAuthTokenPlaceholder,
201
206
  resolvePluginCommandEnv
202
- } from "./chunk-ET2SDCNV.js";
207
+ } from "./chunk-GN6WW7WR.js";
203
208
  import {
204
209
  credentialUserSubjectId
205
210
  } from "./chunk-W3U3YQYI.js";
@@ -223,7 +228,7 @@ import {
223
228
  summarizeMessageText,
224
229
  withLogContext,
225
230
  withSpan
226
- } from "./chunk-JZ47AFSH.js";
231
+ } from "./chunk-4SPRQJOW.js";
227
232
  import {
228
233
  parseSlackChannelId,
229
234
  parseSlackMessageTs,
@@ -320,7 +325,9 @@ function shouldPropagateSandboxEgressTrace(host, config = {}) {
320
325
  }
321
326
 
322
327
  // src/chat/agent/index.ts
323
- import { Agent } from "@earendil-works/pi-agent-core";
328
+ import {
329
+ Agent
330
+ } from "@earendil-works/pi-agent-core";
324
331
 
325
332
  // src/chat/sandbox/skill-sandbox.ts
326
333
  import fs from "fs/promises";
@@ -661,103 +668,6 @@ function shouldEmitDevAgentTrace() {
661
668
 
662
669
  // src/chat/services/turn-result.ts
663
670
  import { isRetryableAssistantError } from "@earendil-works/pi-ai";
664
-
665
- // src/chat/no-reply.ts
666
- var NO_REPLY_MARKER = "[[NO_REPLY]]";
667
- function isNoReplyMarker(text) {
668
- return text.trim() === NO_REPLY_MARKER;
669
- }
670
- function containsNoReplyMarker(text) {
671
- return text.includes(NO_REPLY_MARKER);
672
- }
673
-
674
- // src/chat/services/turn-result.ts
675
- function isExecutionDeferralResponse(text) {
676
- return /\b(want me to proceed|do you want me to proceed|shall i proceed|can i proceed|should i proceed|let me do that now|give me a moment|tag me again|fresh invocation)\b/i.test(
677
- text
678
- );
679
- }
680
- function isToolAccessDisclaimerResponse(text) {
681
- return /\b(i (don't|do not) have access to (active )?tool|tool results came back empty|prior results .* empty|cannot access .*tool|need to (run|load) .*tool .* first)\b/i.test(
682
- text
683
- );
684
- }
685
- function isExecutionEscapeResponse(text) {
686
- const trimmed = text.trim();
687
- if (!trimmed) return false;
688
- return isExecutionDeferralResponse(trimmed) || isToolAccessDisclaimerResponse(trimmed);
689
- }
690
- function parseJsonCandidate(text) {
691
- const trimmed = text.trim();
692
- if (!trimmed) return void 0;
693
- try {
694
- return JSON.parse(trimmed);
695
- } catch {
696
- const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
697
- if (!fenced) return void 0;
698
- try {
699
- return JSON.parse(fenced[1]);
700
- } catch {
701
- return void 0;
702
- }
703
- }
704
- }
705
- function isToolPayloadShape(payload) {
706
- if (!payload || typeof payload !== "object") return false;
707
- const record = payload;
708
- const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
709
- if (type.startsWith("tool-")) return true;
710
- if (type === "tool_use" || type === "tool_call" || type === "tool_result" || type === "tool_error")
711
- return true;
712
- const hasToolName = typeof record.toolName === "string" || typeof record.name === "string";
713
- const hasToolInput = Object.prototype.hasOwnProperty.call(record, "input") || Object.prototype.hasOwnProperty.call(record, "args");
714
- if (hasToolName && hasToolInput) return true;
715
- return false;
716
- }
717
- function isRawToolPayloadResponse(text) {
718
- const parsed = parseJsonCandidate(text);
719
- if (Array.isArray(parsed)) {
720
- return parsed.some((entry) => isToolPayloadShape(entry));
721
- }
722
- if (isToolPayloadShape(parsed)) {
723
- return true;
724
- }
725
- return false;
726
- }
727
- var THINKING_XML_BLOCK_PATTERN = /[ \t]*<thinking\b[^>]*>[\s\S]*?<\/thinking>[ \t]*(?:\r?\n)?/gi;
728
- var FENCED_CODE_BLOCK_PATTERN = /```[\s\S]*?```/g;
729
- function stripThinkingXmlBlocks(text) {
730
- let result = "";
731
- let cursor = 0;
732
- for (const match of text.matchAll(FENCED_CODE_BLOCK_PATTERN)) {
733
- const start = match.index;
734
- if (start === void 0) {
735
- continue;
736
- }
737
- result += text.slice(cursor, start).replace(THINKING_XML_BLOCK_PATTERN, "");
738
- result += match[0];
739
- cursor = start + match[0].length;
740
- }
741
- result += text.slice(cursor).replace(THINKING_XML_BLOCK_PATTERN, "");
742
- return result;
743
- }
744
- function getVisibleAssistantText(rawText) {
745
- const text = stripThinkingXmlBlocks(rawText).trim();
746
- if (!text || isNoReplyMarker(text) || containsNoReplyMarker(text) || isRawToolPayloadResponse(text)) {
747
- return void 0;
748
- }
749
- return text;
750
- }
751
- function getAssistantMessageText(message) {
752
- if (message.content.some((part) => part.type === "toolCall")) {
753
- return void 0;
754
- }
755
- const text = getVisibleAssistantText(extractAssistantText(message));
756
- if (!text) {
757
- return void 0;
758
- }
759
- return isExecutionEscapeResponse(text) ? void 0 : text;
760
- }
761
671
  function buildTurnResult(input) {
762
672
  const {
763
673
  newMessages,
@@ -766,74 +676,53 @@ function buildTurnResult(input) {
766
676
  sandboxRef,
767
677
  durationMs,
768
678
  shouldTrace,
769
- spanContext,
770
679
  usage,
771
680
  executionProfile,
772
- assistantUserName,
773
681
  modelId
774
682
  } = input;
775
683
  const toolResults = newMessages.filter(isToolResultMessage);
776
684
  const assistantMessages = newMessages.filter(isAssistantMessage);
777
685
  const terminalAssistantMessages = getTerminalAssistantMessages(newMessages);
778
- const rawPrimaryText = stripThinkingXmlBlocks(
686
+ const rawPrimaryText = sanitizeAssistantText(
779
687
  terminalAssistantMessages.map((message) => extractAssistantText(message)).join("\n\n")
780
688
  ).trim();
781
689
  const exactNoReplyMarker = isNoReplyMarker(rawPrimaryText);
782
690
  const mixedNoReplyMarker = !exactNoReplyMarker && containsNoReplyMarker(rawPrimaryText);
783
691
  const noReplyRequested = exactNoReplyMarker || mixedNoReplyMarker;
784
- const primaryText = noReplyRequested ? "" : terminalAssistantMessages.map((message) => getAssistantMessageText(message)).filter((text) => Boolean(text)).join("\n\n");
692
+ const primaryText = noReplyRequested ? "" : terminalAssistantMessages.map((message) => decideReply(message)).filter(
693
+ (output) => output.kind === "deliver"
694
+ ).map((output) => output.text).join("\n\n");
785
695
  const toolErrorCount = toolResults.filter((result) => result.isError).length;
786
696
  const reactionPerformed = toolResults.some(
787
697
  (result) => !isToolResultError(result) && normalizeToolNameFromResult(result) === "addReaction"
788
698
  );
789
699
  const completedWithoutTerminalText = noReplyRequested;
790
- const resultLogContext = {
791
- ...spanContext,
792
- assistantUserName,
793
- modelId
794
- };
795
700
  const lastAssistant = terminalAssistantMessages.at(-1);
796
701
  const stopReason = typeof lastAssistant?.stopReason === "string" ? lastAssistant.stopReason : void 0;
797
702
  const errorMessage = typeof lastAssistant?.errorMessage === "string" ? lastAssistant.errorMessage : void 0;
798
703
  const isProviderError = stopReason === "error";
799
704
  if (exactNoReplyMarker) {
800
705
  const markerCategory = reactionPerformed ? "reaction" : "none";
801
- const markerContext = resultLogContext;
802
706
  const markerAttributes = {
803
707
  "app.ai.no_reply_marker": true,
804
708
  "app.ai.no_reply_marker_category": markerCategory,
805
709
  "app.ai.no_reply_marker_accepted": !isProviderError
806
710
  };
807
711
  if (!isProviderError) {
808
- logInfo(
809
- "ai_no_reply_marker_accepted",
810
- markerContext,
811
- markerAttributes,
812
- "No-reply marker suppressed visible thread text"
813
- );
712
+ logInfo("ai.no_reply_marker.accepted", markerAttributes);
814
713
  }
815
714
  } else if (mixedNoReplyMarker) {
816
- logWarn(
817
- "ai_no_reply_marker_mixed_text",
818
- resultLogContext,
819
- {
820
- "app.ai.no_reply_marker": true,
821
- "app.ai.no_reply_marker_mode": "mixed"
822
- },
823
- "No-reply marker appeared with visible assistant text"
824
- );
715
+ logWarn("ai.no_reply_marker.mixed_text", {
716
+ "app.ai.no_reply_marker": true,
717
+ "app.ai.no_reply_marker_mode": "mixed"
718
+ });
825
719
  }
826
720
  if (!primaryText && !completedWithoutTerminalText && !isProviderError) {
827
- logWarn(
828
- "ai_model_response_empty",
829
- resultLogContext,
830
- {
831
- "app.ai.tool_results": toolResults.length,
832
- "app.ai.tool_error_results": toolErrorCount,
833
- "app.ai.generated_files": input.generatedFileCount
834
- },
835
- "Model returned empty text response"
836
- );
721
+ logWarn("ai.model_response.empty", {
722
+ "app.ai.tool_results": toolResults.length,
723
+ "app.ai.tool_error_results": toolErrorCount,
724
+ "app.ai.generated_files": input.generatedFileCount
725
+ });
837
726
  }
838
727
  const usedPrimaryText = Boolean(rawPrimaryText);
839
728
  let outcome;
@@ -844,24 +733,19 @@ function buildTurnResult(input) {
844
733
  } else {
845
734
  outcome = "execution_failure";
846
735
  }
847
- const rejectedPrimaryText = Boolean(
736
+ const suppressedPrimaryText = Boolean(
848
737
  rawPrimaryText && !noReplyRequested && !primaryText
849
738
  );
850
- const resolvedOutcome = rejectedPrimaryText ? "execution_failure" : outcome;
739
+ const resolvedOutcome = suppressedPrimaryText ? "execution_failure" : outcome;
851
740
  if (shouldTrace) {
852
- logInfo(
853
- "agent_message_out",
854
- spanContext,
855
- {
856
- "app.message.kind": "assistant_outbound",
857
- "app.message.length": primaryText.length,
858
- "app.message.output": summarizeMessageText(primaryText),
859
- "app.ai.outcome": resolvedOutcome,
860
- "app.ai.assistant_messages": assistantMessages.length,
861
- ...stopReason ? { "gen_ai.response.finish_reasons": [stopReason] } : {}
862
- },
863
- "Agent message sent"
864
- );
741
+ logInfo("agent.message.generated", {
742
+ "app.message.kind": "assistant_outbound",
743
+ "app.message.length": primaryText.length,
744
+ "app.message.output": summarizeMessageText(primaryText),
745
+ "app.ai.outcome": resolvedOutcome,
746
+ "app.ai.assistant_messages": assistantMessages.length,
747
+ ...stopReason ? { "gen_ai.response.finish_reasons": [stopReason] } : {}
748
+ });
865
749
  }
866
750
  const resolvedDiagnostics = {
867
751
  outcome: resolvedOutcome,
@@ -924,6 +808,45 @@ function nextProviderRetry(args) {
924
808
  return { delayMs, messages };
925
809
  }
926
810
 
811
+ // src/chat/services/empty-output-continuation.ts
812
+ function nextEmptyOutputContinuation(args) {
813
+ if (!hasCompactedConversationContext(args.messages)) {
814
+ return { kind: "none" };
815
+ }
816
+ if (args.lastAssistant?.stopReason === "error" || args.lastAssistant?.stopReason === "aborted") {
817
+ return { kind: "none" };
818
+ }
819
+ if (args.lastAssistant && decideReply(args.lastAssistant).kind !== "empty") {
820
+ return { kind: "none" };
821
+ }
822
+ if (args.attempt > 0) {
823
+ return { kind: "exhausted" };
824
+ }
825
+ if (!args.lastAssistant) {
826
+ return isContinuablePiBoundary(args.messages) ? {
827
+ kind: "retry",
828
+ messages: [...args.messages]
829
+ } : { kind: "exhausted" };
830
+ }
831
+ if (args.messages.at(-1) !== args.lastAssistant) {
832
+ return { kind: "exhausted" };
833
+ }
834
+ const messages = args.messages.slice(0, -1);
835
+ return isContinuablePiBoundary(messages) ? { kind: "retry", messages } : { kind: "exhausted" };
836
+ }
837
+
838
+ // src/chat/agent/retry-usage.ts
839
+ function getDiscardedRetryUsage(messages, retryMessages) {
840
+ const keepsExactPrefix = retryMessages.length <= messages.length && retryMessages.every((message, index) => message === messages[index]);
841
+ if (!keepsExactPrefix) {
842
+ throw new Error("Agent retry must retain an exact message prefix");
843
+ }
844
+ const usage = extractGenAiUsageSummary(
845
+ ...messages.slice(retryMessages.length).filter(isAssistantMessage)
846
+ );
847
+ return hasAgentTurnUsage(usage) ? usage : void 0;
848
+ }
849
+
927
850
  // src/chat/tool-support/structured-result.ts
928
851
  import { z } from "zod";
929
852
  var juniorToolContinuationSchema = z.object({
@@ -1262,20 +1185,9 @@ async function classifyTurn(args) {
1262
1185
  source: "router"
1263
1186
  };
1264
1187
  } catch (error) {
1265
- logWarn(
1266
- "turn_router_classifier_failed",
1267
- {
1268
- messageConversationId: args.metadata.threadId,
1269
- destinationName: args.metadata.channelId,
1270
- userId: args.metadata.actorId,
1271
- runId: args.metadata.runId,
1272
- modelId: args.fastModelId
1273
- },
1274
- {
1275
- "exception.message": error instanceof Error ? error.message : String(error)
1276
- },
1277
- "Turn router classifier failed; using the default route"
1278
- );
1188
+ logWarn("turn.router.classifier.failed", {
1189
+ "exception.message": error instanceof Error ? error.message : String(error)
1190
+ });
1279
1191
  return {
1280
1192
  profile: STANDARD_MODEL_PROFILE,
1281
1193
  reasoningLevel: CLASSIFIER_FALLBACK_REASONING_LEVEL,
@@ -1509,18 +1421,13 @@ async function discoverRunSkills(args) {
1509
1421
  const roots = [
1510
1422
  ...new Set(availableSkills.map((skill) => skill.skillPath))
1511
1423
  ].sort();
1512
- logInfo(
1513
- "startup_discovery_summary",
1514
- args.spanContext,
1515
- {
1516
- "app.skill.count": availableSkills.length,
1517
- "app.skill.names": availableSkills.map((skill) => skill.name).sort(),
1518
- "app.file.directories": roots,
1519
- "app.plugin.count": plugins.length,
1520
- "app.plugin.names": plugins.map((plugin) => plugin.manifest.name).sort()
1521
- },
1522
- "Discovered startup SOUL/skills/plugins"
1523
- );
1424
+ logInfo("startup.discovery.completed", {
1425
+ "app.skill.count": availableSkills.length,
1426
+ "app.skill.names": availableSkills.map((skill) => skill.name).sort(),
1427
+ "app.file.directories": roots,
1428
+ "app.plugin.count": plugins.length,
1429
+ "app.plugin.names": plugins.map((plugin) => plugin.manifest.name).sort()
1430
+ });
1524
1431
  }
1525
1432
  return availableSkills;
1526
1433
  }
@@ -1557,7 +1464,7 @@ function getLoggedMarkdownFiles() {
1557
1464
  globalState.__juniorLoggedMarkdownFiles ??= /* @__PURE__ */ new Set();
1558
1465
  return globalState.__juniorLoggedMarkdownFiles;
1559
1466
  }
1560
- function loadOptionalMarkdownFile(candidates, fileName) {
1467
+ function loadOptionalMarkdownFile(candidates, fileName, loadedEventName) {
1561
1468
  for (const resolved of candidates) {
1562
1469
  try {
1563
1470
  const raw = fs2.readFileSync(resolved, "utf8").trim();
@@ -1566,14 +1473,9 @@ function loadOptionalMarkdownFile(candidates, fileName) {
1566
1473
  const logKey = `${fileName}:${resolved}`;
1567
1474
  if (!loggedMarkdownFiles.has(logKey)) {
1568
1475
  loggedMarkdownFiles.add(logKey);
1569
- logInfo(
1570
- `${fileName.toLowerCase()}_loaded`,
1571
- {},
1572
- {
1573
- "file.path": resolved
1574
- },
1575
- `Loaded ${fileName}`
1576
- );
1476
+ logInfo(loadedEventName, {
1477
+ "file.path": resolved
1478
+ });
1577
1479
  }
1578
1480
  return raw;
1579
1481
  }
@@ -1584,35 +1486,33 @@ function loadOptionalMarkdownFile(candidates, fileName) {
1584
1486
  return null;
1585
1487
  }
1586
1488
  function loadSoul() {
1587
- const soul = loadOptionalMarkdownFile(soulPathCandidates(), "SOUL.md");
1489
+ const soul = loadOptionalMarkdownFile(
1490
+ soulPathCandidates(),
1491
+ "SOUL.md",
1492
+ "soul.loaded"
1493
+ );
1588
1494
  if (soul) {
1589
1495
  return soul;
1590
1496
  }
1591
- logWarn(
1592
- "soul_load_fallback",
1593
- {},
1594
- {
1595
- "app.file.candidates": soulPathCandidates()
1596
- },
1597
- "SOUL.md not found; using built-in default personality"
1598
- );
1497
+ logWarn("soul.load.defaulted", {
1498
+ "app.file.candidates": soulPathCandidates()
1499
+ });
1599
1500
  return DEFAULT_SOUL;
1600
1501
  }
1601
1502
  function loadWorld() {
1602
- return loadOptionalMarkdownFile(worldPathCandidates(), "WORLD.md");
1503
+ return loadOptionalMarkdownFile(
1504
+ worldPathCandidates(),
1505
+ "WORLD.md",
1506
+ "world.loaded"
1507
+ );
1603
1508
  }
1604
1509
  var JUNIOR_PERSONALITY = (() => {
1605
1510
  try {
1606
1511
  return loadSoul();
1607
1512
  } catch (error) {
1608
- logWarn(
1609
- "soul_load_failed",
1610
- {},
1611
- {
1612
- "exception.message": error instanceof Error ? error.message : String(error)
1613
- },
1614
- "Failed to load SOUL.md; using built-in default personality"
1615
- );
1513
+ logWarn("soul.load.failed", {
1514
+ "exception.message": error instanceof Error ? error.message : String(error)
1515
+ });
1616
1516
  return DEFAULT_SOUL;
1617
1517
  }
1618
1518
  })();
@@ -1620,14 +1520,9 @@ var JUNIOR_WORLD = (() => {
1620
1520
  try {
1621
1521
  return loadWorld();
1622
1522
  } catch (error) {
1623
- logWarn(
1624
- "world_load_failed",
1625
- {},
1626
- {
1627
- "exception.message": error instanceof Error ? error.message : String(error)
1628
- },
1629
- "Failed to load WORLD.md; omitting world prompt context"
1630
- );
1523
+ logWarn("world.load.failed", {
1524
+ "exception.message": error instanceof Error ? error.message : String(error)
1525
+ });
1631
1526
  return null;
1632
1527
  }
1633
1528
  })();
@@ -1828,7 +1723,7 @@ var SAFETY_RULES = [
1828
1723
  var FAILURE_RULES = [
1829
1724
  "- For tool/runtime failures, run the named check before diagnosing and report the exact failed command plus stderr/exit code.",
1830
1725
  "- If a fact cannot be verified after focused checks, say what you checked and what blocked a stronger answer.",
1831
- "- Do not surface raw tool payloads, execution-escape text, or internal routing metadata as the final answer."
1726
+ "- Keep raw tool payloads and internal routing metadata out of the final answer."
1832
1727
  ];
1833
1728
  function renderRuleSection(tag, lines) {
1834
1729
  return [`<${tag}>`, ...lines, `</${tag}>`].join("\n");
@@ -2559,17 +2454,12 @@ ${usage}
2559
2454
  updatedBy: deps.actorId,
2560
2455
  source: "jr-rpc"
2561
2456
  });
2562
- logInfo(
2563
- "jr_rpc_config_set",
2564
- {},
2565
- {
2566
- "app.config.key": entry.key,
2567
- "app.config.scope": entry.scope,
2568
- "app.config.source": entry.source ?? "jr-rpc",
2569
- ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2570
- },
2571
- "Set channel configuration via jr-rpc"
2572
- );
2457
+ logInfo("jr_rpc.config.updated", {
2458
+ "app.config.key": entry.key,
2459
+ "app.config.scope": entry.scope,
2460
+ "app.config.source": entry.source ?? "jr-rpc",
2461
+ ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2462
+ });
2573
2463
  deps.onConfigurationValueChanged?.(entry.key, entry.value);
2574
2464
  return commandResult({
2575
2465
  stdout: {
@@ -2604,15 +2494,10 @@ ${usage}
2604
2494
  }
2605
2495
  const deleted = await configuration.unset(key);
2606
2496
  if (deleted) {
2607
- logInfo(
2608
- "jr_rpc_config_unset",
2609
- {},
2610
- {
2611
- "app.config.key": key,
2612
- ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2613
- },
2614
- "Unset channel configuration via jr-rpc"
2615
- );
2497
+ logInfo("jr_rpc.config.removed", {
2498
+ "app.config.key": key,
2499
+ ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2500
+ });
2616
2501
  deps.onConfigurationValueChanged?.(key, void 0);
2617
2502
  }
2618
2503
  return commandResult({
@@ -3563,17 +3448,13 @@ function createSandboxRuntime(options) {
3563
3448
  } : {},
3564
3449
  ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3565
3450
  });
3566
- logInfo(
3567
- "sandbox_hint_discarded_profile_mismatch",
3568
- traceContext,
3569
- {
3570
- ...sandboxRef.profileHash ? {
3571
- "app.sandbox.previous_profile_hash": sandboxRef.profileHash
3572
- } : {},
3573
- ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3574
- },
3575
- "Dependency profile changed; discarding sandbox hint and creating fresh session"
3576
- );
3451
+ logInfo("sandbox.hint.discarded", {
3452
+ "app.decision.reason": "dependency_profile_mismatch",
3453
+ ...sandboxRef.profileHash ? {
3454
+ "app.sandbox.previous_profile_hash": sandboxRef.profileHash
3455
+ } : {},
3456
+ ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3457
+ });
3577
3458
  sandboxRef = void 0;
3578
3459
  };
3579
3460
  const tryReuseCachedSandbox = async () => {
@@ -4294,6 +4175,19 @@ import { z as z3 } from "zod";
4294
4175
  function isContentOnlyToolResult(value) {
4295
4176
  return value !== null && typeof value === "object" && Array.isArray(value.content) && !("details" in value);
4296
4177
  }
4178
+ function isStructuredToolResultEnvelope(value) {
4179
+ return value !== null && typeof value === "object" && Array.isArray(value.content) && "details" in value;
4180
+ }
4181
+ function parseToolContent(content) {
4182
+ for (const item of content) {
4183
+ if (!item || typeof item !== "object" || (item.type === "text" ? typeof item.text !== "string" : item.type === "image" ? typeof item.data !== "string" || typeof item.mimeType !== "string" : true)) {
4184
+ throw new TypeError(
4185
+ "zodTool() content must contain valid text or image items."
4186
+ );
4187
+ }
4188
+ }
4189
+ return content;
4190
+ }
4297
4191
  function formatZodPath(path9) {
4298
4192
  return path9.length > 0 ? path9.map(String).join(".") : "root";
4299
4193
  }
@@ -4356,6 +4250,14 @@ function zodTool(definition) {
4356
4250
  "zodTool() content-only tools must return { content } without details."
4357
4251
  );
4358
4252
  }
4253
+ if (isStructuredToolResultEnvelope(result)) {
4254
+ return {
4255
+ content: parseToolContent(result.content),
4256
+ details: outputSchema.parse(
4257
+ juniorToolResultSchema.parse(result.details)
4258
+ )
4259
+ };
4260
+ }
4359
4261
  return outputSchema.parse(juniorToolResultSchema.parse(result));
4360
4262
  }
4361
4263
  } : {}
@@ -5434,15 +5336,10 @@ function createSandbox(options) {
5434
5336
  if (runtime.sandboxRef()) {
5435
5337
  return;
5436
5338
  }
5437
- logInfo(
5438
- "sandbox_boot_requested",
5439
- traceContext,
5440
- {
5441
- "app.sandbox.boot.trigger": trigger,
5442
- ...details
5443
- },
5444
- "Sandbox boot requested"
5445
- );
5339
+ logInfo("sandbox.boot.requested", {
5340
+ "app.sandbox.boot.trigger": trigger,
5341
+ ...details
5342
+ });
5446
5343
  };
5447
5344
  const executeBashTool = async (rawInput, command, context) => {
5448
5345
  const env = parseEnv(rawInput.env);
@@ -6747,12 +6644,7 @@ var McpToolManager = class {
6747
6644
  };
6748
6645
  setSpanAttributes(errorAttributes);
6749
6646
  if (error instanceof McpToolError) {
6750
- logWarn(
6751
- "mcp_tool_call_failed",
6752
- {},
6753
- errorAttributes,
6754
- "MCP tool call failed"
6755
- );
6647
+ logWarn("mcp.tool_call.failed", errorAttributes);
6756
6648
  }
6757
6649
  throw error;
6758
6650
  }
@@ -6908,22 +6800,16 @@ async function enrichImagePrompt(rawPrompt) {
6908
6800
  maxTokens: 1024
6909
6801
  });
6910
6802
  if (text && text.trim().length > 0) {
6911
- logInfo(
6912
- "image_prompt_enriched",
6913
- {},
6914
- { "app.image.enriched_prompt_length": text.trim().length },
6915
- "Image prompt enriched with persona"
6916
- );
6803
+ logInfo("image.prompt.enriched", {
6804
+ "app.image.enriched_prompt_length": text.trim().length
6805
+ });
6917
6806
  return text.trim();
6918
6807
  }
6919
6808
  return rawPrompt;
6920
6809
  } catch (error) {
6921
- logWarn(
6922
- "image_prompt_enrichment_failed",
6923
- {},
6924
- { "exception.message": String(error) },
6925
- "Image prompt enrichment failed, using raw prompt"
6926
- );
6810
+ logWarn("image.prompt.enrichment.failed", {
6811
+ "exception.message": String(error)
6812
+ });
6927
6813
  return rawPrompt;
6928
6814
  }
6929
6815
  }
@@ -9002,18 +8888,13 @@ async function grantChannelCanvasAccess(canvasId, channelId) {
9002
8888
  }
9003
8889
  }
9004
8890
  );
9005
- } catch (error) {
9006
- logWarn(
9007
- "slack_canvas_access_set_failed",
9008
- {},
9009
- {
9010
- "app.slack.action": "canvases.access.set",
9011
- "app.slack.canvas.canvas_id_prefix": canvasId.slice(0, 1),
9012
- "app.slack.canvas.channel_id_prefix": channelId.slice(0, 1),
9013
- "app.slack.canvas.access_level": "write"
9014
- },
9015
- error instanceof Error ? error.message : "Failed to grant channel access to canvas"
9016
- );
8891
+ } catch {
8892
+ logWarn("slack.canvas.access_set.failed", {
8893
+ "app.slack.action": "canvases.access.set",
8894
+ "app.slack.canvas.canvas_id_prefix": canvasId.slice(0, 1),
8895
+ "app.slack.canvas.channel_id_prefix": channelId.slice(0, 1),
8896
+ "app.slack.canvas.access_level": "write"
8897
+ });
9017
8898
  }
9018
8899
  }
9019
8900
  async function writeCanvasMarkdown(input) {
@@ -9150,16 +9031,11 @@ function createSlackCanvasCreateTool(context, state) {
9150
9031
  execute: async ({ title, markdown }) => {
9151
9032
  const targetChannelId = context.destinationChannelId;
9152
9033
  if (!isConversationScopedChannel(targetChannelId)) {
9153
- logError(
9154
- "slack_canvas_create_invalid_context",
9155
- {},
9156
- {
9157
- "gen_ai.tool.name": "slackCanvasCreate",
9158
- "messaging.destination.name": targetChannelId ?? "none",
9159
- "app.slack.canvas.has_channel_context": Boolean(targetChannelId)
9160
- },
9161
- "Canvas create failed due to missing or invalid assistant channel context"
9162
- );
9034
+ logError("slack.canvas.create.context_invalid", {
9035
+ "gen_ai.tool.name": "slackCanvasCreate",
9036
+ "messaging.destination.name": targetChannelId ?? "none",
9037
+ "app.slack.canvas.has_channel_context": Boolean(targetChannelId)
9038
+ });
9163
9039
  throw new Error(
9164
9040
  "Cannot create a canvas without an active assistant channel context (C/G/D)."
9165
9041
  );
@@ -9292,15 +9168,10 @@ function createSlackCanvasEditTool(state) {
9292
9168
  return response;
9293
9169
  } catch (error) {
9294
9170
  const message = error instanceof Error ? error.message : "canvas edit failed";
9295
- logWarn(
9296
- "slack_canvas_edit_failed",
9297
- {},
9298
- {
9299
- "gen_ai.tool.name": "slackCanvasEdit",
9300
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9301
- },
9302
- message
9303
- );
9171
+ logWarn("slack.canvas.edit.failed", {
9172
+ "gen_ai.tool.name": "slackCanvasEdit",
9173
+ "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9174
+ });
9304
9175
  return {
9305
9176
  ok: false,
9306
9177
  status: "error",
@@ -9371,15 +9242,10 @@ function createSlackCanvasReadTool() {
9371
9242
  };
9372
9243
  } catch (error) {
9373
9244
  const message = error instanceof Error ? error.message : "canvas read failed";
9374
- logWarn(
9375
- "slack_canvas_read_failed",
9376
- {},
9377
- {
9378
- "gen_ai.tool.name": "slackCanvasRead",
9379
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9380
- },
9381
- message
9382
- );
9245
+ logWarn("slack.canvas.read.failed", {
9246
+ "gen_ai.tool.name": "slackCanvasRead",
9247
+ "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9248
+ });
9383
9249
  return {
9384
9250
  ok: false,
9385
9251
  status: "error",
@@ -9438,15 +9304,10 @@ function createSlackCanvasWriteTool(state) {
9438
9304
  return response;
9439
9305
  } catch (error) {
9440
9306
  const message = error instanceof Error ? error.message : "canvas write failed";
9441
- logWarn(
9442
- "slack_canvas_write_failed",
9443
- {},
9444
- {
9445
- "gen_ai.tool.name": "slackCanvasWrite",
9446
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9447
- },
9448
- message
9449
- );
9307
+ logWarn("slack.canvas.write.failed", {
9308
+ "gen_ai.tool.name": "slackCanvasWrite",
9309
+ "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9310
+ });
9450
9311
  return {
9451
9312
  ok: false,
9452
9313
  status: "error",
@@ -10494,6 +10355,10 @@ function createSystemTimeTool() {
10494
10355
  import { z as z37 } from "zod";
10495
10356
  var DEFAULT_LIMIT3 = 25;
10496
10357
  var MAX_LIMIT = 100;
10358
+ var MAX_EVENT_DATA_BYTES = 4e3;
10359
+ var MAX_EVENTS_JSON_BYTES = 2e4;
10360
+ var MAX_IDENTITY_CHARS = 256;
10361
+ var textEncoder = new TextEncoder();
10497
10362
  var CONVERSATION_EVENTS_TOOL_SOURCE = {
10498
10363
  id: "conversation-events",
10499
10364
  description: "Inspect Junior's durable conversation event log for debugging runtime behavior."
@@ -10507,7 +10372,7 @@ var projectedEventSchema = z37.object({
10507
10372
  created_at: z37.iso.datetime().describe("When the event was recorded."),
10508
10373
  idempotency_key: z37.string().min(1).optional().describe("Retry-stable event identity, when one was assigned."),
10509
10374
  data: z37.record(z37.string(), z37.unknown()).describe(
10510
- "Event payload. The type field identifies the event; replacement-history events report replacement_history_count instead of replaying the full model context."
10375
+ "Event data. Oversized payloads are replaced by identifying fields, payload_omitted, and payload_json_bytes."
10511
10376
  )
10512
10377
  }).strict();
10513
10378
  var queryConversationEventsOutputSchema = juniorToolResultSchema.extend({
@@ -10518,6 +10383,9 @@ var queryConversationEventsOutputSchema = juniorToolResultSchema.extend({
10518
10383
  ),
10519
10384
  has_newer: z37.boolean().describe(
10520
10385
  "Whether matching events exist after this page; request them with after_seq set to the last returned seq."
10386
+ ),
10387
+ omitted_event_count: z37.number().int().nonnegative().optional().describe(
10388
+ "Fetched events omitted to keep this response bounded. Continue with the pagination cursor for the omitted direction."
10521
10389
  )
10522
10390
  });
10523
10391
  function createQueryConversationEventsTool(context) {
@@ -10597,14 +10465,17 @@ function createQueryConversationEventsTool(context) {
10597
10465
  ...beforeSeq === void 0 ? {} : { beforeSeq },
10598
10466
  ...types === void 0 ? {} : { types }
10599
10467
  });
10600
- const events = projectEventsForTool(page);
10468
+ const newestFirst = afterSeq === void 0;
10469
+ const projected = projectEventsForTool(page, { newestFirst });
10601
10470
  return {
10602
10471
  ok: true,
10603
10472
  status: "success",
10604
10473
  conversation_id: conversationId,
10605
- events,
10606
- has_older: page.hasOlder,
10607
- has_newer: page.hasNewer
10474
+ events: projected.events,
10475
+ has_older: page.hasOlder || newestFirst && projected.omittedEventCount > 0,
10476
+ has_newer: page.hasNewer || !newestFirst && projected.omittedEventCount > 0,
10477
+ truncated: projected.omittedEventCount > 0,
10478
+ ...projected.omittedEventCount > 0 ? { omitted_event_count: projected.omittedEventCount } : {}
10608
10479
  };
10609
10480
  }
10610
10481
  });
@@ -10689,11 +10560,30 @@ function assertCanQueryConversationEvents(args) {
10689
10560
  `Conversation events are not accessible: ${targetConversationId}`
10690
10561
  );
10691
10562
  }
10692
- function projectEventsForTool(page) {
10693
- return page.events.map(projectEvent);
10563
+ function projectEventsForTool(page, options) {
10564
+ const projected = page.events.map(projectEvent);
10565
+ const events = [];
10566
+ let jsonBytes = 2;
10567
+ const candidates = options.newestFirst ? [...projected].reverse() : projected;
10568
+ for (const event of candidates) {
10569
+ const eventBytes = jsonByteLength(event);
10570
+ const separatorBytes = events.length === 0 ? 0 : 1;
10571
+ if (jsonBytes + separatorBytes + eventBytes > MAX_EVENTS_JSON_BYTES) {
10572
+ break;
10573
+ }
10574
+ events.push(event);
10575
+ jsonBytes += separatorBytes + eventBytes;
10576
+ }
10577
+ if (options.newestFirst) {
10578
+ events.reverse();
10579
+ }
10580
+ return {
10581
+ events,
10582
+ omittedEventCount: projected.length - events.length
10583
+ };
10694
10584
  }
10695
10585
  function projectEvent(event) {
10696
- const data = stripReplacementHistory(event.data);
10586
+ const data = boundEventData(stripReplacementHistory(event.data));
10697
10587
  return {
10698
10588
  seq: event.seq,
10699
10589
  history_version: event.historyVersion,
@@ -10702,6 +10592,46 @@ function projectEvent(event) {
10702
10592
  data
10703
10593
  };
10704
10594
  }
10595
+ function boundEventData(data) {
10596
+ const payloadJsonBytes = jsonByteLength(data);
10597
+ if (payloadJsonBytes <= MAX_EVENT_DATA_BYTES) {
10598
+ return data;
10599
+ }
10600
+ return {
10601
+ ...eventIdentity(data),
10602
+ payload_omitted: true,
10603
+ payload_json_bytes: payloadJsonBytes
10604
+ };
10605
+ }
10606
+ function jsonByteLength(value) {
10607
+ return textEncoder.encode(JSON.stringify(value)).byteLength;
10608
+ }
10609
+ function eventIdentity(data) {
10610
+ const identityKeys = [
10611
+ "type",
10612
+ "originalType",
10613
+ "messageId",
10614
+ "role",
10615
+ "turnId",
10616
+ "toolCallId",
10617
+ "toolName",
10618
+ "subagentInvocationId",
10619
+ "subagentKind",
10620
+ "childConversationId",
10621
+ "outcome",
10622
+ "failureCode",
10623
+ "provider",
10624
+ "kind",
10625
+ "modelProfile",
10626
+ "modelId"
10627
+ ];
10628
+ return Object.fromEntries(
10629
+ identityKeys.flatMap((key) => {
10630
+ const value = data[key];
10631
+ return typeof value === "string" ? [[key, value.slice(0, MAX_IDENTITY_CHARS)]] : [];
10632
+ })
10633
+ );
10634
+ }
10705
10635
  function stripReplacementHistory(data) {
10706
10636
  if (data.type === "unknown") {
10707
10637
  return {
@@ -11368,18 +11298,12 @@ function createWebSearchTool(modelId, override) {
11368
11298
  const message = formatSearchFailure(error);
11369
11299
  const timeout = /timed out/i.test(message);
11370
11300
  const retryable = !isAuthFailure(message);
11371
- logException(
11372
- error,
11373
- "web_search_failed",
11374
- {},
11375
- {
11376
- "gen_ai.tool.name": "webSearch",
11377
- "app.web_search.timeout": timeout,
11378
- "app.web_search.retryable": retryable,
11379
- "app.web_search.query": query
11380
- },
11381
- message
11382
- );
11301
+ logException(error, "web.search.failed", {
11302
+ "gen_ai.tool.name": "webSearch",
11303
+ "app.web_search.timeout": timeout,
11304
+ "app.web_search.retryable": retryable,
11305
+ "app.web_search.query": query
11306
+ });
11383
11307
  return {
11384
11308
  ok: false,
11385
11309
  status: "error",
@@ -12097,7 +12021,7 @@ function getToolErrorAttributes(error) {
12097
12021
  ...error.detailRule ? { "app.slack.detail_rule": error.detailRule } : {}
12098
12022
  };
12099
12023
  }
12100
- function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, traceContext, conversationPrivacy, setExecutionSpanAttributes) {
12024
+ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conversationPrivacy, setExecutionSpanAttributes) {
12101
12025
  const errorType = getToolErrorType(error);
12102
12026
  const errorMessage = getMcpAwareTelemetryMessage(error, conversationPrivacy);
12103
12027
  const errorAttributes = {
@@ -12113,50 +12037,34 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
12113
12037
  }
12114
12038
  if (error instanceof PluginCredentialFailureError) {
12115
12039
  if (shouldTrace) {
12116
- logInfo(
12117
- "plugin_credential_rejected",
12118
- traceContext,
12119
- {
12120
- "app.credential.provider": error.provider,
12121
- "gen_ai.operation.name": "execute_tool",
12122
- "gen_ai.tool.name": toolName,
12123
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12124
- "error.type": errorType
12125
- },
12126
- "Plugin credentials were rejected during tool execution"
12127
- );
12040
+ logInfo("plugin.credential.rejected", {
12041
+ "app.credential.provider": error.provider,
12042
+ "gen_ai.operation.name": "execute_tool",
12043
+ "gen_ai.tool.name": toolName,
12044
+ ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12045
+ "error.type": errorType
12046
+ });
12128
12047
  }
12129
12048
  throw error;
12130
12049
  }
12131
12050
  if (shouldTrace) {
12132
- logWarn(
12133
- "agent_tool_call_failed",
12134
- traceContext,
12135
- {
12136
- "gen_ai.operation.name": "execute_tool",
12137
- "gen_ai.tool.name": toolName,
12138
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12139
- ...errorAttributes,
12140
- "exception.message": errorMessage
12141
- },
12142
- "Agent tool call failed"
12143
- );
12051
+ logWarn("agent.tool_call.failed", {
12052
+ "gen_ai.operation.name": "execute_tool",
12053
+ "gen_ai.tool.name": toolName,
12054
+ ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12055
+ ...errorAttributes,
12056
+ "exception.message": errorMessage
12057
+ });
12144
12058
  }
12145
12059
  const isExpectedToolFailure = error instanceof McpToolError || error instanceof ToolInputError || isPluginToolInputError(error);
12146
12060
  if (!isExpectedToolFailure) {
12147
- logException(
12148
- error,
12149
- "agent_tool_call_failed",
12150
- {},
12151
- {
12152
- "gen_ai.operation.name": "execute_tool",
12153
- "gen_ai.tool.name": toolName,
12154
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12155
- ...errorAttributes,
12156
- ...getToolErrorAttributes(error)
12157
- },
12158
- "Agent tool call failed"
12159
- );
12061
+ logException(error, "agent.tool_call.failed", {
12062
+ "gen_ai.operation.name": "execute_tool",
12063
+ "gen_ai.tool.name": toolName,
12064
+ ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12065
+ ...errorAttributes,
12066
+ ...getToolErrorAttributes(error)
12067
+ });
12160
12068
  }
12161
12069
  throw error;
12162
12070
  }
@@ -12524,15 +12432,10 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12524
12432
  try {
12525
12433
  await onToolResult?.(report);
12526
12434
  } catch (error) {
12527
- logWarn(
12528
- "tool_result_observer_failed",
12529
- spanContext,
12530
- {
12531
- "gen_ai.tool.name": report.toolName,
12532
- "exception.message": error instanceof Error ? error.message : String(error)
12533
- },
12534
- "Tool result observer failed"
12535
- );
12435
+ logWarn("tool.result.observer.failed", {
12436
+ "gen_ai.tool.name": report.toolName,
12437
+ "exception.message": error instanceof Error ? error.message : String(error)
12438
+ });
12536
12439
  }
12537
12440
  };
12538
12441
  const toolResultOk = (details) => {
@@ -12591,15 +12494,10 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12591
12494
  projectedPrivateResult = toolDef.privateTraceResult(resultAttributeValue);
12592
12495
  hasProjectedPrivateResult = projectedPrivateResult !== void 0;
12593
12496
  } catch (error) {
12594
- logWarn(
12595
- "tool_private_trace_projection_failed",
12596
- spanContext,
12597
- {
12598
- "error.type": error instanceof Error ? error.name : typeof error,
12599
- "gen_ai.tool.name": toolName
12600
- },
12601
- "Tool private trace projection failed"
12602
- );
12497
+ logWarn("tool.private.trace_projection.failed", {
12498
+ "error.type": error instanceof Error ? error.name : typeof error,
12499
+ "gen_ai.tool.name": toolName
12500
+ });
12603
12501
  }
12604
12502
  }
12605
12503
  const toolResultAttribute = effectiveConversationPrivacy === "private" && toolDef.privateTraceResult && !hasProjectedPrivateResult ? void 0 : serializeToolPayload(
@@ -12713,7 +12611,6 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12713
12611
  executionToolName,
12714
12612
  toolCallId,
12715
12613
  shouldTrace,
12716
- spanContext,
12717
12614
  effectiveConversationPrivacy,
12718
12615
  setSpanAttributes2
12719
12616
  );
@@ -13186,26 +13083,21 @@ function logSandboxEgressUpstreamRequest(input) {
13186
13083
  if (!shouldLogSandboxEgressInfo()) {
13187
13084
  return;
13188
13085
  }
13189
- logInfo(
13190
- "sandbox_egress_upstream_request",
13191
- {},
13192
- {
13193
- ...egressAttributes({
13194
- egressId: input.egressId,
13195
- grantAccess: input.grantAccess,
13196
- grantName: input.grantName,
13197
- grantReason: input.grantReason,
13198
- host: input.upstreamUrl.hostname,
13199
- method: input.request.method,
13200
- path: input.upstreamUrl.pathname,
13201
- provider: input.provider,
13202
- status: input.upstream.status
13203
- }),
13204
- ...routingAttributes(input.request, input.upstreamUrl),
13205
- "app.sandbox.egress.upstream_ok": input.upstream.ok
13206
- },
13207
- `Sandbox egress ${input.request.method} ${input.upstreamUrl.hostname}${displayedUpstreamPath(input.upstreamUrl)} -> ${input.upstream.status}`
13208
- );
13086
+ logInfo("sandbox.egress.upstream.requested", {
13087
+ ...egressAttributes({
13088
+ egressId: input.egressId,
13089
+ grantAccess: input.grantAccess,
13090
+ grantName: input.grantName,
13091
+ grantReason: input.grantReason,
13092
+ host: input.upstreamUrl.hostname,
13093
+ method: input.request.method,
13094
+ path: input.upstreamUrl.pathname,
13095
+ provider: input.provider,
13096
+ status: input.upstream.status
13097
+ }),
13098
+ ...routingAttributes(input.request, input.upstreamUrl),
13099
+ "app.sandbox.egress.upstream_ok": input.upstream.ok
13100
+ });
13209
13101
  }
13210
13102
  async function requestBodyBytes(request) {
13211
13103
  if (request.method === "GET" || request.method === "HEAD" || request.body === null) {
@@ -13402,22 +13294,18 @@ async function executeCredentialedEgressRequest(input) {
13402
13294
  });
13403
13295
  } catch (error) {
13404
13296
  if (isEgressPolicyDenied(error)) {
13405
- logWarn(
13406
- "sandbox_egress_policy_denied",
13407
- {},
13408
- {
13409
- ...egressAttributes({
13410
- egressId: activeEgressId,
13411
- host: upstreamUrl.hostname,
13412
- method: request.method,
13413
- path: upstreamUrl.pathname,
13414
- provider,
13415
- status: 403
13416
- }),
13417
- ...routingAttributes(request, upstreamUrl)
13418
- },
13419
- error.message
13420
- );
13297
+ logWarn("sandbox.egress.policy.denied", {
13298
+ ...egressAttributes({
13299
+ egressId: activeEgressId,
13300
+ host: upstreamUrl.hostname,
13301
+ method: request.method,
13302
+ path: upstreamUrl.pathname,
13303
+ provider,
13304
+ status: 403
13305
+ }),
13306
+ ...routingAttributes(request, upstreamUrl),
13307
+ "app.sandbox.egress.policy.reason": error.message
13308
+ });
13421
13309
  return policyDeniedResponse(error);
13422
13310
  }
13423
13311
  throw error;
@@ -13445,8 +13333,7 @@ async function executeCredentialedEgressRequest(input) {
13445
13333
  });
13446
13334
  const isAuthRequired = error.kind === "auth_required";
13447
13335
  logWarn(
13448
- isAuthRequired ? "sandbox_egress_credential_needed" : "sandbox_egress_credential_unavailable",
13449
- {},
13336
+ isAuthRequired ? "sandbox.egress.credential.needed" : "sandbox.egress.credential.unavailable",
13450
13337
  {
13451
13338
  ...egressAttributes({
13452
13339
  egressId: activeEgressId,
@@ -13460,8 +13347,7 @@ async function executeCredentialedEgressRequest(input) {
13460
13347
  status: 401
13461
13348
  }),
13462
13349
  ...routingAttributes(request, upstreamUrl)
13463
- },
13464
- isAuthRequired ? "Sandbox egress grant needs user authorization before issuing a credential lease" : "Sandbox egress credential lease is unavailable for selected grant"
13350
+ }
13465
13351
  );
13466
13352
  return authRequiredResponse({
13467
13353
  provider: error.provider,
@@ -13481,17 +13367,12 @@ async function executeCredentialedEgressRequest(input) {
13481
13367
  upstreamUrl
13482
13368
  });
13483
13369
  if (!hasSandboxEgressLeaseTransformForHost(lease, upstreamUrl.hostname)) {
13484
- logWarn(
13485
- "sandbox_egress_transform_missing",
13486
- {},
13487
- {
13488
- ...attributes(403),
13489
- "app.sandbox.egress.transform_domains": lease.headerTransforms.map(
13490
- (transform) => transform.domain
13491
- )
13492
- },
13493
- "Sandbox egress credential lease does not cover forwarded host"
13494
- );
13370
+ logWarn("sandbox.egress.transform.missing", {
13371
+ ...attributes(403),
13372
+ "app.sandbox.egress.transform_domains": lease.headerTransforms.map(
13373
+ (transform) => transform.domain
13374
+ )
13375
+ });
13495
13376
  return Response.json(
13496
13377
  { error: "Credential lease does not cover forwarded host" },
13497
13378
  { status: 403 }
@@ -13545,14 +13426,9 @@ async function executeCredentialedEgressRequest(input) {
13545
13426
  upstream,
13546
13427
  upstreamUrl
13547
13428
  });
13548
- logWarn(
13549
- "sandbox_egress_upstream_permission_classified",
13550
- {},
13551
- {
13552
- ...attributes(upstream.status, upstream)
13553
- },
13554
- "Sandbox egress plugin classified upstream response as permission denied"
13555
- );
13429
+ logWarn("sandbox.egress.upstream_permission.classified", {
13430
+ ...attributes(upstream.status, upstream)
13431
+ });
13556
13432
  }
13557
13433
  } catch (error) {
13558
13434
  if (!isEgressAuthRequired(error)) {
@@ -13566,14 +13442,9 @@ async function executeCredentialedEgressRequest(input) {
13566
13442
  authorization: error.authorization ?? lease.authorization,
13567
13443
  message: error.message
13568
13444
  });
13569
- logWarn(
13570
- "sandbox_egress_upstream_auth_required_classified",
13571
- {},
13572
- {
13573
- ...attributes(upstream.status, upstream)
13574
- },
13575
- "Sandbox egress plugin classified upstream response as auth required"
13576
- );
13445
+ logWarn("sandbox.egress.upstream_auth_requirement.classified", {
13446
+ ...attributes(upstream.status, upstream)
13447
+ });
13577
13448
  await upstream.body?.cancel().catch(() => void 0);
13578
13449
  return authRequiredResponse({
13579
13450
  provider,
@@ -13592,28 +13463,18 @@ async function executeCredentialedEgressRequest(input) {
13592
13463
  upstreamUrl
13593
13464
  });
13594
13465
  if (upstream.status >= 400) {
13595
- logWarn(
13596
- "sandbox_egress_upstream_error_response",
13597
- {},
13598
- {
13599
- ...attributes(upstream.status, upstream),
13600
- "error.type": `http_${upstream.status}`
13601
- },
13602
- `Sandbox egress upstream returned HTTP ${upstream.status}`
13603
- );
13466
+ logWarn("sandbox.egress.upstream_response.failed", {
13467
+ ...attributes(upstream.status, upstream),
13468
+ "error.type": `http_${upstream.status}`
13469
+ });
13604
13470
  }
13605
13471
  if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS || upstream.status === UPSTREAM_PERMISSION_REJECTION_STATUS) {
13606
- logWarn(
13607
- "sandbox_egress_upstream_auth_rejected",
13608
- {},
13609
- {
13610
- ...attributes(upstream.status, upstream),
13611
- ...upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? {
13612
- "app.sandbox.egress.www_authenticate": upstream.headers.get("www-authenticate") ?? void 0
13613
- } : {}
13614
- },
13615
- upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? "Sandbox egress upstream auth rejected injected credential" : "Sandbox egress upstream permission denied"
13616
- );
13472
+ logWarn("sandbox.egress.upstream_auth.rejected", {
13473
+ ...attributes(upstream.status, upstream),
13474
+ ...upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? {
13475
+ "app.sandbox.egress.www_authenticate": upstream.headers.get("www-authenticate") ?? void 0
13476
+ } : {}
13477
+ });
13617
13478
  if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS) {
13618
13479
  await clearCredentialLease(provider, lease.grant, credentialContext);
13619
13480
  await recordAuthRequired({
@@ -13712,7 +13573,7 @@ function createPluginEgress(deps) {
13712
13573
  }
13713
13574
 
13714
13575
  // src/chat/agent/tools.ts
13715
- async function tryRecordSkillLoadStat(skill, spanContext) {
13576
+ async function tryRecordSkillLoadStat(skill) {
13716
13577
  if (!process.env.DATABASE_URL) return;
13717
13578
  try {
13718
13579
  await incrementStat({
@@ -13721,16 +13582,11 @@ async function tryRecordSkillLoadStat(skill, spanContext) {
13721
13582
  name: skill.name
13722
13583
  });
13723
13584
  } catch (error) {
13724
- logWarn(
13725
- "skill_load_stat_failed",
13726
- spanContext,
13727
- {
13728
- "app.skill.name": skill.name,
13729
- "app.plugin.name": skill.pluginProvider,
13730
- "exception.message": error instanceof Error ? error.message : String(error)
13731
- },
13732
- "Failed to record skill load stat"
13733
- );
13585
+ logWarn("skill.load.stat.failed", {
13586
+ "app.skill.name": skill.name,
13587
+ "app.plugin.name": skill.pluginProvider,
13588
+ "exception.message": error instanceof Error ? error.message : String(error)
13589
+ });
13734
13590
  }
13735
13591
  }
13736
13592
  async function wireAgentTools(args) {
@@ -13890,7 +13746,7 @@ async function wireAgentTools(args) {
13890
13746
  if (await mcpToolManager.activateForSkill(effective)) {
13891
13747
  await args.recordConnectedMcpProvider(effective.pluginProvider);
13892
13748
  }
13893
- await tryRecordSkillLoadStat(effective, args.spanContext);
13749
+ await tryRecordSkillLoadStat(effective);
13894
13750
  if (mcpAuth.getPendingPause()) {
13895
13751
  return void 0;
13896
13752
  }
@@ -13956,15 +13812,10 @@ async function wireAgentTools(args) {
13956
13812
  toolName
13957
13813
  });
13958
13814
  } catch (error) {
13959
- logWarn(
13960
- "tool_invocation_observer_failed",
13961
- args.spanContext,
13962
- {
13963
- "gen_ai.tool.name": toolName,
13964
- "exception.message": error instanceof Error ? error.message : String(error)
13965
- },
13966
- "Tool invocation observer failed"
13967
- );
13815
+ logWarn("tool.invocation.observer.failed", {
13816
+ "gen_ai.tool.name": toolName,
13817
+ "exception.message": error instanceof Error ? error.message : String(error)
13818
+ });
13968
13819
  }
13969
13820
  };
13970
13821
  const agentTools = createPiAgentTools(
@@ -13991,18 +13842,13 @@ async function wireAgentTools(args) {
13991
13842
  try {
13992
13843
  await mcpToolManager.close();
13993
13844
  } catch (closeError) {
13994
- logWarn(
13995
- "mcp_tool_manager_close_failed",
13996
- {},
13997
- {
13998
- ...getMcpProviderErrorAttributes(closeError),
13999
- "exception.message": getMcpAwareTelemetryMessage(
14000
- closeError,
14001
- args.conversationPrivacy
14002
- )
14003
- },
14004
- "Failed to close MCP tool manager"
14005
- );
13845
+ logWarn("mcp.tool_manager.close.failed", {
13846
+ ...getMcpProviderErrorAttributes(closeError),
13847
+ "exception.message": getMcpAwareTelemetryMessage(
13848
+ closeError,
13849
+ args.conversationPrivacy
13850
+ )
13851
+ });
14006
13852
  }
14007
13853
  },
14008
13854
  toolGuidance,
@@ -14035,7 +13881,6 @@ function createResumeState(args) {
14035
13881
  source: args.runSource,
14036
13882
  sessionId: args.turnId,
14037
13883
  loadedSkillNames: args.getLoadedSkillNames(),
14038
- logContext: args.logContext,
14039
13884
  modelId: args.getModelId(),
14040
13885
  ...args.getReasoningLevel() ? { reasoningLevel: args.getReasoningLevel() } : {},
14041
13886
  actor: args.actor,
@@ -14216,6 +14061,7 @@ function createResumeState(args) {
14216
14061
  }
14217
14062
 
14218
14063
  // src/chat/services/context-compaction.ts
14064
+ import { estimateContextTokens } from "@earendil-works/pi-agent-core";
14219
14065
  var RETAINED_USER_MESSAGE_TOKENS = 2e4;
14220
14066
  var MAX_SUMMARY_INPUT_CHARS = 8e4;
14221
14067
  var MAX_VISIBLE_CONTEXT_CHARS = 2e4;
@@ -14409,10 +14255,7 @@ async function summarizeContext(args, deps) {
14409
14255
  return summary.slice(0, MAX_SUMMARY_CHARS);
14410
14256
  }
14411
14257
  function estimateHistoryTokens(messages) {
14412
- return messages.reduce(
14413
- (total, message) => total + estimateTextTokens(JSON.stringify(message) ?? ""),
14414
- 0
14415
- );
14258
+ return estimateContextTokens(messages).tokens;
14416
14259
  }
14417
14260
  function buildReplacementProvenance(args) {
14418
14261
  return [
@@ -14469,21 +14312,9 @@ async function maybeCompactWithDeps(args, deps) {
14469
14312
  deps
14470
14313
  );
14471
14314
  } catch (error) {
14472
- logWarn(
14473
- "context_compaction_summary_failed",
14474
- {
14475
- messageConversationId: args.metadata?.threadId,
14476
- userId: args.metadata?.actorId,
14477
- destinationName: args.metadata?.channelId,
14478
- runId: args.metadata?.runId,
14479
- assistantUserName: botConfig.userName,
14480
- modelId: botConfig.fastModelId
14481
- },
14482
- {
14483
- "exception.message": error instanceof Error ? error.message : String(error)
14484
- },
14485
- "Context compaction failed; continuing with prior history"
14486
- );
14315
+ logWarn("context_compaction.summary.failed", {
14316
+ "exception.message": error instanceof Error ? error.message : String(error)
14317
+ });
14487
14318
  return { compacted: false, reason: "summary_failed" };
14488
14319
  }
14489
14320
  return await writeCompactedThreadContext(args, source.messages, summary, {
@@ -14623,23 +14454,11 @@ async function compactActiveContextIfNeeded(args, deps) {
14623
14454
  inputLimitTokens
14624
14455
  );
14625
14456
  }
14626
- logWarn(
14627
- "active_context_compaction_summary_failed",
14628
- {
14629
- messageConversationId: args.metadata?.threadId,
14630
- userId: args.metadata?.actorId,
14631
- destinationName: args.metadata?.channelId,
14632
- runId: args.metadata?.runId,
14633
- assistantUserName: botConfig.userName,
14634
- modelId: args.modelId
14635
- },
14636
- {
14637
- "app.context_input_limit_tokens": inputLimitTokens,
14638
- "app.context_tokens_estimated": source.estimatedTokens,
14639
- "exception.message": error instanceof Error ? error.message : String(error)
14640
- },
14641
- "Active context compaction failed below the hard input limit"
14642
- );
14457
+ logWarn("context_compaction.active.summary.failed", {
14458
+ "app.context_input_limit_tokens": inputLimitTokens,
14459
+ "app.context_tokens_estimated": source.estimatedTokens,
14460
+ "exception.message": error instanceof Error ? error.message : String(error)
14461
+ });
14643
14462
  return { compacted: false, reason: "summary_failed" };
14644
14463
  }
14645
14464
  const summaryMessage = userMessage(
@@ -14741,7 +14560,7 @@ function waitForAbortSettlement(promise, timeoutMs) {
14741
14560
  );
14742
14561
  });
14743
14562
  }
14744
- async function executeAgentRun(request) {
14563
+ async function executeAgentRun(request, streamFn) {
14745
14564
  if (!request.routing.destination) {
14746
14565
  throw new TypeError("Assistant reply generation requires a destination");
14747
14566
  }
@@ -14776,12 +14595,13 @@ async function executeAgentRun(request) {
14776
14595
  () => executeAgentRunInPrivacyContext(
14777
14596
  request,
14778
14597
  conversationPrivacy,
14779
- runLogContext
14598
+ runLogContext,
14599
+ streamFn
14780
14600
  )
14781
14601
  )
14782
14602
  );
14783
14603
  }
14784
- async function executeAgentRunInPrivacyContext(request, conversationPrivacy, runLogContext) {
14604
+ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, runLogContext, streamFn) {
14785
14605
  const { conversationId, input, routing, runId, turnId } = request;
14786
14606
  const policy = request.policy ?? {};
14787
14607
  const signal = policy.signal;
@@ -14819,19 +14639,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14819
14639
  const slackDestination = routing.destination.platform === "slack" ? routing.destination : void 0;
14820
14640
  const slackActor = actor?.platform === "slack" ? actor : void 0;
14821
14641
  const userInput = input.messageText;
14822
- const credentialActor = routing.credentialContext?.actor;
14823
- const credentialActorLogContext = credentialActor ? {
14824
- actorType: "type" in credentialActor ? credentialActor.type : "system",
14825
- actorId: "type" in credentialActor ? credentialActor.userId : credentialActor.name
14826
- } : {};
14827
- const sessionRecordLogContext = {
14828
- threadId: slackSource ? conversationId : void 0,
14829
- actorId: slackActor?.userId,
14830
- channelId: slackDestination?.channelId,
14831
- runId,
14832
- ...credentialActorLogContext,
14833
- assistantUserName: botConfig.userName
14834
- };
14835
14642
  const recordConnectedMcpProvider = async (provider) => {
14836
14643
  if (connectedMcpProviders.has(provider)) {
14837
14644
  return;
@@ -14858,27 +14665,21 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14858
14665
  const shouldTrace = shouldEmitDevAgentTrace();
14859
14666
  const spanContext = { modelId: activeModelId };
14860
14667
  const availableSkills = await discoverRunSkills({
14861
- skillDirs: policy.skillDirs,
14862
- spanContext
14668
+ skillDirs: policy.skillDirs
14863
14669
  });
14864
14670
  if (shouldTrace) {
14865
14671
  const inboundAttachmentCount = input.inboundAttachmentCount ?? 0;
14866
14672
  const promptAttachmentCount = input.userAttachments?.length ?? 0;
14867
- logInfo(
14868
- "agent_message_in",
14869
- spanContext,
14870
- {
14871
- "app.message.kind": "user_inbound",
14872
- "app.message.length": userInput.length,
14873
- "app.message.input": summarizeMessageText(userInput),
14874
- // Log both counts so image uploads filtered by vision/config do not
14875
- // look indistinguishable from Slack ingress dropping attachments.
14876
- "app.message.attachment_count": inboundAttachmentCount,
14877
- "app.message.prompt_attachment_count": promptAttachmentCount,
14878
- "messaging.message.id": slackSource?.messageTs ?? ""
14879
- },
14880
- "Agent message received"
14881
- );
14673
+ logInfo("agent.message.received", {
14674
+ "app.message.kind": "user_inbound",
14675
+ "app.message.length": userInput.length,
14676
+ "app.message.input": summarizeMessageText(userInput),
14677
+ // Log both counts so image uploads filtered by vision/config do not
14678
+ // look indistinguishable from Slack ingress dropping attachments.
14679
+ "app.message.attachment_count": inboundAttachmentCount,
14680
+ "app.message.prompt_attachment_count": promptAttachmentCount,
14681
+ "messaging.message.id": slackSource?.messageTs ?? ""
14682
+ });
14882
14683
  }
14883
14684
  const skillInvocation = parseSkillInvocation(userInput, availableSkills);
14884
14685
  const invokedSkill = skillInvocation ? findSkillByName(skillInvocation.skillName, availableSkills) : null;
@@ -14910,7 +14711,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14910
14711
  durability,
14911
14712
  getLoadedSkillNames: () => loadedSkillNamesForResume,
14912
14713
  getReasoningLevel: () => turnRoute?.reasoningLevel,
14913
- logContext: sessionRecordLogContext,
14914
14714
  getModelId: () => activeModelId,
14915
14715
  recordActiveMcpProviders,
14916
14716
  actor,
@@ -14930,15 +14730,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14930
14730
  toolName: event.toolName
14931
14731
  });
14932
14732
  } catch (error) {
14933
- logException(
14934
- error,
14935
- "agent_turn_session_log_append_failed",
14936
- spanContext,
14937
- {
14938
- "gen_ai.tool.name": event.toolName
14939
- },
14940
- "Failed to record host-only tool execution start"
14941
- );
14733
+ logException(error, "agent.turn.session_log_append.failed", {
14734
+ "gen_ai.tool.name": event.toolName
14735
+ });
14942
14736
  }
14943
14737
  };
14944
14738
  const persistedConfigurationValues = policy.channelConfiguration ? await policy.channelConfiguration.resolveValues() : {};
@@ -15073,14 +14867,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15073
14867
  void (async () => {
15074
14868
  await observers.onStatus?.({ text: "Switching models" });
15075
14869
  })().catch((error) => {
15076
- logWarn(
15077
- "assistant_status_observer_failed",
15078
- {},
15079
- {
15080
- "exception.message": error instanceof Error ? error.message : String(error)
15081
- },
15082
- "Failed to report assistant status"
15083
- );
14870
+ logWarn("assistant.status.observer.failed", {
14871
+ "exception.message": error instanceof Error ? error.message : String(error)
14872
+ });
15084
14873
  });
15085
14874
  const handoffMessages = await compactContextForHandoff(
15086
14875
  {
@@ -15306,12 +15095,12 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15306
15095
  };
15307
15096
  let assistantMessageDeliveryError;
15308
15097
  const deliverAssistantMessage = async (message) => {
15309
- const text = getAssistantMessageText(message);
15310
- if (!text || !delivery) {
15098
+ const decision = decideReply(message);
15099
+ if (decision.kind !== "deliver" || !delivery) {
15311
15100
  return;
15312
15101
  }
15313
15102
  try {
15314
- await delivery.onAssistantMessage({ text });
15103
+ await delivery(message);
15315
15104
  } catch (error) {
15316
15105
  assistantMessageDeliveryError = new AssistantMessageDeliveryError(
15317
15106
  error
@@ -15350,27 +15139,17 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15350
15139
  steeredMessageCount += piMessages.length;
15351
15140
  });
15352
15141
  if (steeredMessageCount > 0) {
15353
- logInfo(
15354
- "agent_turn_steering_messages_accepted",
15355
- spanContext,
15356
- {
15357
- "app.ai.steering_message_count": steeredMessageCount
15358
- },
15359
- "Agent turn steering messages accepted"
15360
- );
15142
+ logInfo("agent.turn.steering_messages.accepted", {
15143
+ "app.ai.steering_message_count": steeredMessageCount
15144
+ });
15361
15145
  }
15362
15146
  } catch (error) {
15363
15147
  if (isTurnInputCommitLostError(error)) {
15364
15148
  throw error;
15365
15149
  }
15366
- logWarn(
15367
- "agent_turn_steering_messages_drain_failed",
15368
- spanContext,
15369
- {
15370
- "exception.message": error instanceof Error ? error.message : String(error)
15371
- },
15372
- "Agent turn steering message drain failed"
15373
- );
15150
+ logWarn("agent.turn.steering_messages_drain.failed", {
15151
+ "exception.message": error instanceof Error ? error.message : String(error)
15152
+ });
15374
15153
  }
15375
15154
  return acceptedMessages;
15376
15155
  };
@@ -15378,7 +15157,10 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15378
15157
  let pendingPiHookError;
15379
15158
  agent = new Agent({
15380
15159
  ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
15381
- streamFn: createTracedStreamFn({ conversationPrivacy }),
15160
+ streamFn: createTracedStreamFn({
15161
+ conversationPrivacy,
15162
+ ...streamFn ? { base: streamFn } : {}
15163
+ }),
15382
15164
  steeringMode: "all",
15383
15165
  beforeToolCall: async ({ assistantMessage }) => {
15384
15166
  const toolCalls2 = assistantMessage.content.filter(
@@ -15522,37 +15304,27 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15522
15304
  );
15523
15305
  } catch (error) {
15524
15306
  if (runResume.timedOut) {
15525
- logWarn(
15526
- "agent_turn_timeout",
15527
- {},
15528
- {
15529
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
15530
- "gen_ai.operation.name": "invoke_agent",
15531
- "gen_ai.request.model": activeModelId,
15532
- ...turnRoute ? {
15533
- "gen_ai.request.reasoning.level": turnRoute.reasoningLevel
15534
- } : {},
15535
- "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
15536
- "app.ai.turn_deadline_remaining_ms": Math.max(
15537
- 0,
15538
- turnDeadlineAtMs - Date.now()
15539
- )
15540
- },
15541
- "Agent turn timed out and was aborted"
15542
- );
15307
+ logWarn("agent.turn.timed_out", {
15308
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
15309
+ "gen_ai.operation.name": "invoke_agent",
15310
+ "gen_ai.request.model": activeModelId,
15311
+ ...turnRoute ? {
15312
+ "gen_ai.request.reasoning.level": turnRoute.reasoningLevel
15313
+ } : {},
15314
+ "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
15315
+ "app.ai.turn_deadline_remaining_ms": Math.max(
15316
+ 0,
15317
+ turnDeadlineAtMs - Date.now()
15318
+ )
15319
+ });
15543
15320
  const settled = await waitForAbortSettlement(
15544
15321
  run2,
15545
15322
  AGENT_ABORT_SETTLE_GRACE_MS
15546
15323
  );
15547
15324
  if (!settled) {
15548
- logWarn(
15549
- "agent_turn_abort_settle_timeout",
15550
- {},
15551
- {
15552
- "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS
15553
- },
15554
- "Timed-out agent run did not settle after abort before resume snapshot"
15555
- );
15325
+ logWarn("agent.turn.abort_settle.timed_out", {
15326
+ "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS
15327
+ });
15556
15328
  }
15557
15329
  runResume.captureResumeSnapshot(
15558
15330
  runResume.getResumeSnapshot(currentAgentMessages())
@@ -15616,9 +15388,20 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15616
15388
  ]);
15617
15389
  }
15618
15390
  run = shouldPromptAgent && !capacityUpdate ? agent.prompt(freshPromptMessage) : agent.continue();
15619
- let retryUsage;
15391
+ let discardedRetryUsage;
15392
+ let providerRetryAttempt = 0;
15393
+ let emptyOutputAttempt = 0;
15394
+ const prepareRetry = async (messages) => {
15395
+ discardedRetryUsage = addAgentTurnUsage(
15396
+ discardedRetryUsage,
15397
+ getDiscardedRetryUsage(agent.state.messages, messages)
15398
+ );
15399
+ agent.state.messages = messages;
15400
+ await runResume.persistSafeBoundary(messages);
15401
+ signal?.throwIfAborted();
15402
+ };
15620
15403
  try {
15621
- for (let attempt = 0; ; attempt += 1) {
15404
+ for (; ; ) {
15622
15405
  await runAgentStep(run);
15623
15406
  if (assistantMessageDeliveryError) {
15624
15407
  throw assistantMessageDeliveryError;
@@ -15635,7 +15418,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15635
15418
  const usageSummary = extractGenAiUsageSummary(...outputMessages);
15636
15419
  const currentUsage = hasAgentTurnUsage(usageSummary) ? usageSummary : void 0;
15637
15420
  const currentPhaseUsage = addAgentTurnUsage(
15638
- retryUsage,
15421
+ discardedRetryUsage,
15639
15422
  currentUsage
15640
15423
  );
15641
15424
  turnUsage = addAgentTurnUsage(priorPhaseUsage, currentPhaseUsage);
@@ -15650,7 +15433,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15650
15433
  normalizeGenAiFinishReason(lastAssistant.stopReason)
15651
15434
  ]
15652
15435
  } : {},
15653
- ...extractGenAiUsageAttributes(usageSummary)
15436
+ ...extractGenAiUsageAttributes(currentPhaseUsage)
15654
15437
  });
15655
15438
  const pendingAuthPause = getPendingAuthPause();
15656
15439
  if (pendingAuthPause) {
@@ -15659,25 +15442,37 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15659
15442
  );
15660
15443
  throw pendingAuthPause;
15661
15444
  }
15445
+ const emptyOutputContinuation = nextEmptyOutputContinuation({
15446
+ attempt: emptyOutputAttempt,
15447
+ lastAssistant,
15448
+ messages: agent.state.messages
15449
+ });
15450
+ if (emptyOutputContinuation.kind === "retry") {
15451
+ emptyOutputAttempt += 1;
15452
+ await prepareRetry(emptyOutputContinuation.messages);
15453
+ logWarn("agent.turn.empty_output.retrying", {
15454
+ "app.ai.empty_output.attempt": emptyOutputAttempt
15455
+ });
15456
+ run = agent.continue();
15457
+ continue;
15458
+ }
15459
+ if (emptyOutputContinuation.kind === "exhausted") {
15460
+ logWarn("agent.turn.empty_output.exhausted", {
15461
+ "app.ai.empty_output.attempt": emptyOutputAttempt
15462
+ });
15463
+ }
15662
15464
  const providerRetry = nextProviderRetry({
15663
- attempt,
15465
+ attempt: providerRetryAttempt,
15664
15466
  failure: lastAssistant,
15665
15467
  messages: agent.state.messages
15666
15468
  });
15667
15469
  if (!providerRetry) {
15668
15470
  break;
15669
15471
  }
15670
- retryUsage = currentPhaseUsage;
15671
- agent.state.messages = providerRetry.messages;
15672
- await runResume.persistSafeBoundary(providerRetry.messages);
15673
- logWarn(
15674
- "agent_turn_provider_retry",
15675
- spanContext,
15676
- {},
15677
- "Retrying transient provider failure"
15678
- );
15472
+ providerRetryAttempt += 1;
15473
+ await prepareRetry(providerRetry.messages);
15474
+ logWarn("agent.turn.provider.retrying");
15679
15475
  await sleep(providerRetry.delayMs, signal);
15680
- signal?.throwIfAborted();
15681
15476
  run = agent.continue();
15682
15477
  }
15683
15478
  } catch (error) {
@@ -15722,7 +15517,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15722
15517
  durationMs: Date.now() - replyStartedAtMs,
15723
15518
  generatedFileCount: generatedFiles.length,
15724
15519
  shouldTrace,
15725
- spanContext,
15726
15520
  usage: turnUsage,
15727
15521
  executionProfile: turnRoute,
15728
15522
  assistantUserName: botConfig.userName,
@@ -15770,13 +15564,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15770
15564
  if (durability.onInputCommitted && !resume?.inputCommitted) {
15771
15565
  throw error;
15772
15566
  }
15773
- logException(
15774
- error,
15775
- "assistant_reply_generation_failed",
15776
- { modelId: activeModelId },
15777
- {},
15778
- "executeAgentRun failed"
15779
- );
15567
+ logException(error, "assistant.reply.generation.failed");
15780
15568
  const message = error instanceof Error ? error.message : String(error);
15781
15569
  return {
15782
15570
  status: "completed",