@sentry/junior 0.120.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 (69) hide show
  1. package/dist/{agent-hooks-5DDPWRYO.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-5KR5XBMJ.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/sandbox/noninteractive-command.d.ts +1 -0
  16. package/dist/chat/sandbox/session.d.ts +2 -2
  17. package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
  18. package/dist/chat/sandbox/snapshot/resolve.d.ts +1 -0
  19. package/dist/chat/sandbox/workspace.d.ts +3 -1
  20. package/dist/chat/services/assistant-reply.d.ts +16 -0
  21. package/dist/chat/services/empty-output-continuation.d.ts +20 -0
  22. package/dist/chat/services/turn-failure-response.d.ts +1 -3
  23. package/dist/chat/services/turn-result.d.ts +0 -5
  24. package/dist/chat/services/turn-session-record.d.ts +0 -11
  25. package/dist/chat/tool-support/zod-tool.d.ts +11 -6
  26. package/dist/chat/tools/execute-tool.d.ts +1 -33
  27. package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
  28. package/dist/chat/tools/query-conversation-events.d.ts +3 -1
  29. package/dist/chat/tools/sandbox/bash.d.ts +1 -33
  30. package/dist/chat/tools/sandbox/edit-file.d.ts +1 -33
  31. package/dist/chat/tools/sandbox/find-files.d.ts +1 -33
  32. package/dist/chat/tools/sandbox/grep.d.ts +1 -33
  33. package/dist/chat/tools/sandbox/list-dir.d.ts +1 -33
  34. package/dist/chat/tools/sandbox/read-file.d.ts +1 -33
  35. package/dist/chat/tools/sandbox/write-file.d.ts +1 -33
  36. package/dist/{chunk-VURFJVFH.js → chunk-2RFM7STT.js} +11 -22
  37. package/dist/{chunk-6WX5SPI3.js → chunk-32FPGM2V.js} +1 -1
  38. package/dist/{chunk-ALYQOZG7.js → chunk-4SPRQJOW.js} +37 -45
  39. package/dist/{chunk-QOQPPRFS.js → chunk-4TGVC42X.js} +133 -206
  40. package/dist/{chunk-Q3RL6A4E.js → chunk-BDE5RZVF.js} +2 -2
  41. package/dist/{chunk-DJDGDJDF.js → chunk-CYW4M6IV.js} +44 -23
  42. package/dist/{chunk-M243JMZH.js → chunk-DOB5RVMH.js} +2 -2
  43. package/dist/{chunk-EIVE6GL4.js → chunk-E5SRLZQV.js} +14 -29
  44. package/dist/{chunk-YEAOUT5W.js → chunk-GFTZKOTP.js} +1 -1
  45. package/dist/{chunk-XAI2HNAY.js → chunk-GN6WW7WR.js} +16 -31
  46. package/dist/{chunk-DMKHRBI6.js → chunk-ISQYBTPG.js} +3 -3
  47. package/dist/{chunk-DWOH57FW.js → chunk-L2KJ2GNL.js} +1 -1
  48. package/dist/{chunk-ERYVNWNE.js → chunk-NIJG6G6X.js} +621 -735
  49. package/dist/{chunk-JQWP6V22.js → chunk-NNGYYGX5.js} +2061 -2015
  50. package/dist/{chunk-GNNV3UTY.js → chunk-OKUZIXQM.js} +1 -1
  51. package/dist/{chunk-HDFGNU5E.js → chunk-S7G34U75.js} +3 -1
  52. package/dist/{chunk-ALAKN7LR.js → chunk-TZVZCIYL.js} +1 -1
  53. package/dist/{chunk-24B5R6OU.js → chunk-USKP7IM2.js} +49 -92
  54. package/dist/{chunk-BHXW734F.js → chunk-V6XPIKYA.js} +13 -19
  55. package/dist/{chunk-TKUCRFNX.js → chunk-VCC7XFX2.js} +22 -45
  56. package/dist/cli/chat.js +22 -22
  57. package/dist/cli/check.js +6 -6
  58. package/dist/cli/plugins.js +11 -11
  59. package/dist/cli/snapshot-create.js +9 -9
  60. package/dist/cli/upgrade.js +6 -6
  61. package/dist/{db-T4ZY64PB.js → db-E6O2DXUQ.js} +6 -6
  62. package/dist/instrumentation.js +3 -3
  63. package/dist/nitro.js +2 -2
  64. package/dist/{oauth-callback-server-LQ6DFI26.js → oauth-callback-server-6SCPUFFD.js} +15 -15
  65. package/dist/{runner-D3ZU6TJH.js → runner-JFKCMVVY.js} +62 -51
  66. package/dist/{skills-JMXY7LIY.js → skills-RD66F2OB.js} +6 -6
  67. package/dist/{validation-OEPJS7JX.js → validation-XVRNX6IU.js} +6 -6
  68. package/dist/version.js +1 -1
  69. package/package.json +8 -8
@@ -30,15 +30,18 @@ import {
30
30
  toMcpProviderError,
31
31
  toolInvocationDestination,
32
32
  uploadFilesToConversation
33
- } from "./chunk-TKUCRFNX.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-JQWP6V22.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-DJDGDJDF.js";
104
+ } from "./chunk-CYW4M6IV.js";
100
105
  import {
101
106
  incrementStat
102
- } from "./chunk-6WX5SPI3.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-24B5R6OU.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-M243JMZH.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-ALAKN7LR.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-VURFJVFH.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-EIVE6GL4.js";
196
+ } from "./chunk-E5SRLZQV.js";
192
197
  import {
193
198
  pluginCatalogRuntime
194
- } from "./chunk-GNNV3UTY.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-XAI2HNAY.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-ALYQOZG7.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
  })();
@@ -1781,10 +1676,11 @@ var TOOL_POLICY_RULES = [
1781
1676
  "- Resolve provider action targets before calls: explicit target wins; ambient `<configuration>` fills omitted targets. Treat non-target links/references as context.",
1782
1677
  "- Verification source order: conversation/thread context; user-provided attachments, links, and reference files; local/sandbox files when present; loaded skill references; repository/provider tools; public web. Use the nearest authoritative available source before weaker sources.",
1783
1678
  "- For repository or implementation questions, inspect the target repository first: local checkout when present, otherwise the configured GitHub/source provider. Do not treat loaded skill files as repo source unless the user asks about the skill. Cite file paths, symbols, PRs/issues, commits, or URLs that support the answer.",
1679
+ "- After changing files, name the changed paths and summarize the completed result in the final answer.",
1784
1680
  "- If a sandbox-backed tool reports that sandbox execution is unavailable, treat that as a blocker for local file/shell inspection; do not pretend host files were inspected.",
1785
1681
  "- For user-provided URLs, use `webFetch`; for discovery, use `webSearch` then fetch/read promising sources; for current time/date context, use `systemTime`.",
1786
1682
  "- When a tool result includes a subscribable resource, use resource-event subscriptions for high-signal provider changes that serve the user's current intent; do not create scheduled polling tasks for events the subscription can deliver. Use the suggested events when they fit and write a concise intent summary.",
1787
- "- For code changes, debugging or root-cause analysis, broad refactors, and software architecture decisions, use `handoff` before substantive analysis when that tool is available.",
1683
+ "- For code changes, debugging or root-cause analysis, broad refactors, and software architecture decisions, use `handoff` before substantive analysis only when it offers a profile that better matches the task. Do not switch merely because the task involves code.",
1788
1684
  "- Run `jr-rpc config get|set|unset|list` for provider defaults and `jr-rpc plugins list` for installed plugin introspection as standalone bash commands; do not chain them with `cd`, `&&`, pipes, or provider commands.",
1789
1685
  "- If the first result is empty, stale, ambiguous, or incomplete, try a focused alternate query, path, command, or source before concluding the answer cannot be verified."
1790
1686
  ];
@@ -1805,7 +1701,8 @@ var EXECUTION_CONTRACT_RULES = [
1805
1701
  "- Complete the full task, but report only the result and evidence the user needs; do not narrate every step, check, or detail.",
1806
1702
  "- Ask the user only for missing access, approval, or a decision that blocks safe progress. Ask one focused question; otherwise infer conservatively and continue.",
1807
1703
  "- For conflicting evidence, compare sources and state which source is authoritative for the answer.",
1808
- "- For non-trivial or long-running work, call `reportProgress` early when available, then only when the major phase changes. Routine tool calls should stay silent."
1704
+ "- Use `reportProgress` only for work with multiple substantive phases or a materially long wait. Skip short lookups and routine commands; after an initial update, call it again only when the major phase changes.",
1705
+ "- When a tool outcome is unknown and may include side effects, inspect authoritative state before retrying. If state already reflects the intended result, do not repeat the mutation."
1809
1706
  ];
1810
1707
  var CONVERSATION_RULES = [
1811
1708
  "- In thread follow-ups, answer from prior thread context; do not repeat resolved clarifying questions.",
@@ -1826,7 +1723,7 @@ var SAFETY_RULES = [
1826
1723
  var FAILURE_RULES = [
1827
1724
  "- For tool/runtime failures, run the named check before diagnosing and report the exact failed command plus stderr/exit code.",
1828
1725
  "- If a fact cannot be verified after focused checks, say what you checked and what blocked a stronger answer.",
1829
- "- 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."
1830
1727
  ];
1831
1728
  function renderRuleSection(tag, lines) {
1832
1729
  return [`<${tag}>`, ...lines, `</${tag}>`].join("\n");
@@ -2557,17 +2454,12 @@ ${usage}
2557
2454
  updatedBy: deps.actorId,
2558
2455
  source: "jr-rpc"
2559
2456
  });
2560
- logInfo(
2561
- "jr_rpc_config_set",
2562
- {},
2563
- {
2564
- "app.config.key": entry.key,
2565
- "app.config.scope": entry.scope,
2566
- "app.config.source": entry.source ?? "jr-rpc",
2567
- ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2568
- },
2569
- "Set channel configuration via jr-rpc"
2570
- );
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
+ });
2571
2463
  deps.onConfigurationValueChanged?.(entry.key, entry.value);
2572
2464
  return commandResult({
2573
2465
  stdout: {
@@ -2602,15 +2494,10 @@ ${usage}
2602
2494
  }
2603
2495
  const deleted = await configuration.unset(key);
2604
2496
  if (deleted) {
2605
- logInfo(
2606
- "jr_rpc_config_unset",
2607
- {},
2608
- {
2609
- "app.config.key": key,
2610
- ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2611
- },
2612
- "Unset channel configuration via jr-rpc"
2613
- );
2497
+ logInfo("jr_rpc.config.removed", {
2498
+ "app.config.key": key,
2499
+ ...deps.activeSkill?.name ? { "app.skill.name": deps.activeSkill.name } : {}
2500
+ });
2614
2501
  deps.onConfigurationValueChanged?.(key, void 0);
2615
2502
  }
2616
2503
  return commandResult({
@@ -3259,6 +3146,20 @@ var SNAPSHOT_BOOT_RETRY_DELAY_MS = 1e3;
3259
3146
  var SANDBOX_NAME_PREFIX = "junior-";
3260
3147
  var MAX_KEEPALIVE_INTERVAL_MS = 3e4;
3261
3148
  var MIN_KEEPALIVE_INTERVAL_MS = 1e3;
3149
+ function sandboxFetchOptions(signal) {
3150
+ if (!signal) {
3151
+ return {};
3152
+ }
3153
+ return {
3154
+ fetch: (input, init) => {
3155
+ const requestSignal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
3156
+ return globalThis.fetch(input, {
3157
+ ...init,
3158
+ signal: requestSignal ? AbortSignal.any([requestSignal, signal]) : signal
3159
+ });
3160
+ }
3161
+ };
3162
+ }
3262
3163
  function truncateOutput(output, maxLength) {
3263
3164
  if (output.length <= maxLength) {
3264
3165
  return { value: output, truncated: false };
@@ -3398,9 +3299,10 @@ function createSandboxRuntime(options) {
3398
3299
  }
3399
3300
  );
3400
3301
  };
3401
- const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials, initialSandboxName) => {
3302
+ const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials, initialSandboxName, signal) => {
3402
3303
  const resources = getSandboxResources();
3403
3304
  for (let attempt = 0; attempt < SNAPSHOT_BOOT_RETRY_COUNT; attempt += 1) {
3305
+ signal?.throwIfAborted();
3404
3306
  const sandboxName = attempt === 0 ? initialSandboxName : createSandboxName();
3405
3307
  const networkPolicy = preflightNetworkPolicy(sandboxName);
3406
3308
  try {
@@ -3413,14 +3315,15 @@ function createSandboxRuntime(options) {
3413
3315
  snapshotId
3414
3316
  },
3415
3317
  ...resources ? { resources } : {},
3416
- ...sandboxCredentials ?? {}
3318
+ ...sandboxCredentials ?? {},
3319
+ ...sandboxFetchOptions(signal)
3417
3320
  })
3418
3321
  );
3419
3322
  } catch (error) {
3420
3323
  if (!isSnapshottingError(error) || attempt === SNAPSHOT_BOOT_RETRY_COUNT - 1) {
3421
3324
  throw error;
3422
3325
  }
3423
- await sleep(SNAPSHOT_BOOT_RETRY_DELAY_MS);
3326
+ await sleep(SNAPSHOT_BOOT_RETRY_DELAY_MS, signal);
3424
3327
  }
3425
3328
  }
3426
3329
  throw new Error(`Failed to boot sandbox from snapshot ${snapshotId}`);
@@ -3440,7 +3343,8 @@ function createSandboxRuntime(options) {
3440
3343
  });
3441
3344
  };
3442
3345
  const createSandboxFromResolvedSnapshot = async (params) => {
3443
- const { runtime, snapshot, sandboxCredentials, sandboxName } = params;
3346
+ const { runtime, snapshot, sandboxCredentials, sandboxName, signal } = params;
3347
+ signal?.throwIfAborted();
3444
3348
  if (!snapshot.snapshotId) {
3445
3349
  const networkPolicy = preflightNetworkPolicy(sandboxName);
3446
3350
  const resources = getSandboxResources();
@@ -3450,7 +3354,8 @@ function createSandboxRuntime(options) {
3450
3354
  runtime,
3451
3355
  ...networkPolicy ? { name: sandboxName, persistent: false, networkPolicy } : {},
3452
3356
  ...resources ? { resources } : {},
3453
- ...sandboxCredentials ?? {}
3357
+ ...sandboxCredentials ?? {},
3358
+ ...sandboxFetchOptions(signal)
3454
3359
  })
3455
3360
  );
3456
3361
  }
@@ -3458,7 +3363,8 @@ function createSandboxRuntime(options) {
3458
3363
  return await createSandboxFromSnapshot(
3459
3364
  snapshot.snapshotId,
3460
3365
  sandboxCredentials,
3461
- sandboxName
3366
+ sandboxName,
3367
+ signal
3462
3368
  );
3463
3369
  } catch (error) {
3464
3370
  if (!isMissingError(error)) {
@@ -3471,19 +3377,22 @@ function createSandboxRuntime(options) {
3471
3377
  runtime,
3472
3378
  timeoutMs,
3473
3379
  forceRebuild: true,
3474
- staleSnapshotId: snapshot.snapshotId
3380
+ staleSnapshotId: snapshot.snapshotId,
3381
+ signal
3475
3382
  });
3476
3383
  if (!rebuiltSnapshot.snapshotId) {
3477
3384
  throw error;
3478
3385
  }
3386
+ signal?.throwIfAborted();
3479
3387
  return await createSandboxFromSnapshot(
3480
3388
  rebuiltSnapshot.snapshotId,
3481
3389
  sandboxCredentials,
3482
- sandboxName
3390
+ sandboxName,
3391
+ signal
3483
3392
  );
3484
3393
  }
3485
3394
  };
3486
- const createFreshSandbox = async () => {
3395
+ const createFreshSandbox = async (signal) => {
3487
3396
  const runtime = SANDBOX_RUNTIME;
3488
3397
  const sandboxCredentials = getVercelSandboxCredentials();
3489
3398
  const sandboxName = createSandboxName();
@@ -3500,14 +3409,17 @@ function createSandboxRuntime(options) {
3500
3409
  async () => {
3501
3410
  const snapshot = await resolve({
3502
3411
  runtime,
3503
- timeoutMs
3412
+ timeoutMs,
3413
+ signal
3504
3414
  });
3415
+ signal?.throwIfAborted();
3505
3416
  setSnapshotAttributes(snapshot);
3506
3417
  return await createSandboxFromResolvedSnapshot({
3507
3418
  runtime,
3508
3419
  snapshot,
3509
3420
  sandboxCredentials,
3510
- sandboxName
3421
+ sandboxName,
3422
+ signal
3511
3423
  });
3512
3424
  }
3513
3425
  );
@@ -3536,23 +3448,19 @@ function createSandboxRuntime(options) {
3536
3448
  } : {},
3537
3449
  ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3538
3450
  });
3539
- logInfo(
3540
- "sandbox_hint_discarded_profile_mismatch",
3541
- traceContext,
3542
- {
3543
- ...sandboxRef.profileHash ? {
3544
- "app.sandbox.previous_profile_hash": sandboxRef.profileHash
3545
- } : {},
3546
- ...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
3547
- },
3548
- "Dependency profile changed; discarding sandbox hint and creating fresh session"
3549
- );
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
+ });
3550
3458
  sandboxRef = void 0;
3551
3459
  };
3552
3460
  const tryReuseCachedSandbox = async () => {
3553
3461
  return activeSandbox?.session ?? null;
3554
3462
  };
3555
- const tryRestoreHintedSandbox = async () => {
3463
+ const tryRestoreHintedSandbox = async (signal) => {
3556
3464
  const ref = sandboxRef;
3557
3465
  if (!ref) {
3558
3466
  return null;
@@ -3571,7 +3479,8 @@ function createSandboxRuntime(options) {
3571
3479
  await Sandbox.get({
3572
3480
  name: ref.id,
3573
3481
  resume: true,
3574
- ...sandboxCredentials ?? {}
3482
+ ...sandboxCredentials ?? {},
3483
+ ...sandboxFetchOptions(signal)
3575
3484
  })
3576
3485
  )
3577
3486
  );
@@ -3599,7 +3508,7 @@ function createSandboxRuntime(options) {
3599
3508
  return failSetup(error);
3600
3509
  }
3601
3510
  };
3602
- const acquireSandbox = async () => {
3511
+ const acquireSandbox = async (signal) => {
3603
3512
  return await withSandboxSpan(
3604
3513
  "sandbox.acquire",
3605
3514
  "sandbox.acquire",
@@ -3615,25 +3524,63 @@ function createSandboxRuntime(options) {
3615
3524
  if (cachedSandbox) {
3616
3525
  return cachedSandbox;
3617
3526
  }
3618
- const hintedSandbox = await tryRestoreHintedSandbox();
3527
+ signal?.throwIfAborted();
3528
+ const hintedSandbox = await tryRestoreHintedSandbox(signal);
3619
3529
  if (hintedSandbox) {
3620
3530
  return hintedSandbox;
3621
3531
  }
3622
- return await createFreshSandbox();
3532
+ return await createFreshSandbox(signal);
3623
3533
  }
3624
3534
  );
3625
3535
  };
3626
- const getOrAcquireSandbox = async () => {
3627
- if (acquiringSandbox) {
3628
- return await acquiringSandbox;
3629
- }
3630
- const nextSandbox = acquireSandbox();
3631
- acquiringSandbox = nextSandbox;
3632
- try {
3633
- return await nextSandbox;
3634
- } finally {
3635
- if (acquiringSandbox === nextSandbox) {
3636
- acquiringSandbox = void 0;
3536
+ const getOrAcquireSandbox = async (signal) => {
3537
+ for (; ; ) {
3538
+ signal?.throwIfAborted();
3539
+ let acquisition = acquiringSandbox;
3540
+ if (!acquisition) {
3541
+ const controller = new AbortController();
3542
+ const startedAcquisition = {
3543
+ controller,
3544
+ promise: acquireSandbox(signal ? controller.signal : void 0),
3545
+ settled: false,
3546
+ waiters: 0
3547
+ };
3548
+ const markSettled = () => {
3549
+ startedAcquisition.settled = true;
3550
+ if (acquiringSandbox === startedAcquisition) {
3551
+ acquiringSandbox = void 0;
3552
+ }
3553
+ };
3554
+ void startedAcquisition.promise.then(markSettled, markSettled);
3555
+ acquiringSandbox = startedAcquisition;
3556
+ acquisition = startedAcquisition;
3557
+ }
3558
+ acquisition.waiters += 1;
3559
+ let removeAbortListener;
3560
+ try {
3561
+ if (!signal) {
3562
+ return await acquisition.promise;
3563
+ }
3564
+ const aborted = new Promise((_resolve, reject) => {
3565
+ const onAbort = () => reject(signal.reason);
3566
+ signal.addEventListener("abort", onAbort, { once: true });
3567
+ removeAbortListener = () => signal.removeEventListener("abort", onAbort);
3568
+ });
3569
+ return await Promise.race([acquisition.promise, aborted]);
3570
+ } catch (error) {
3571
+ signal?.throwIfAborted();
3572
+ if (!acquisition.controller.signal.aborted) {
3573
+ throw error;
3574
+ }
3575
+ if (acquiringSandbox === acquisition) {
3576
+ acquiringSandbox = void 0;
3577
+ }
3578
+ } finally {
3579
+ removeAbortListener?.();
3580
+ acquisition.waiters -= 1;
3581
+ if (acquisition.waiters === 0 && !acquisition.settled) {
3582
+ acquisition.controller.abort(signal?.reason);
3583
+ }
3637
3584
  }
3638
3585
  }
3639
3586
  };
@@ -3782,9 +3729,11 @@ function createSandboxRuntime(options) {
3782
3729
  runCommand: async (input) => await sandboxInstance.runCommand(input)
3783
3730
  };
3784
3731
  };
3785
- const ensureReadySandbox = async () => {
3786
- const activeSandbox2 = await getOrAcquireSandbox();
3732
+ const ensureReadySandbox = async (signal) => {
3733
+ const activeSandbox2 = await getOrAcquireSandbox(signal);
3734
+ signal?.throwIfAborted();
3787
3735
  await probeSession(activeSandbox2);
3736
+ signal?.throwIfAborted();
3788
3737
  await extendKeepAlive(activeSandbox2);
3789
3738
  startKeepAlive(activeSandbox2);
3790
3739
  return activeSandbox2;
@@ -3793,11 +3742,11 @@ function createSandboxRuntime(options) {
3793
3742
  sandboxRef() {
3794
3743
  return sandboxRef ? { ...sandboxRef } : void 0;
3795
3744
  },
3796
- async acquire() {
3797
- return await getOrAcquireSandbox();
3745
+ async acquire(signal) {
3746
+ return await getOrAcquireSandbox(signal);
3798
3747
  },
3799
- async tools() {
3800
- return createToolExecutors(await ensureReadySandbox());
3748
+ async tools(signal) {
3749
+ return createToolExecutors(await ensureReadySandbox(signal));
3801
3750
  },
3802
3751
  async refreshNetworkPolicy(traceHeaders) {
3803
3752
  const active = activeSandbox;
@@ -4226,6 +4175,19 @@ import { z as z3 } from "zod";
4226
4175
  function isContentOnlyToolResult(value) {
4227
4176
  return value !== null && typeof value === "object" && Array.isArray(value.content) && !("details" in value);
4228
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
+ }
4229
4191
  function formatZodPath(path9) {
4230
4192
  return path9.length > 0 ? path9.map(String).join(".") : "root";
4231
4193
  }
@@ -4288,6 +4250,14 @@ function zodTool(definition) {
4288
4250
  "zodTool() content-only tools must return { content } without details."
4289
4251
  );
4290
4252
  }
4253
+ if (isStructuredToolResultEnvelope(result)) {
4254
+ return {
4255
+ content: parseToolContent(result.content),
4256
+ details: outputSchema.parse(
4257
+ juniorToolResultSchema.parse(result.details)
4258
+ )
4259
+ };
4260
+ }
4291
4261
  return outputSchema.parse(juniorToolResultSchema.parse(result));
4292
4262
  }
4293
4263
  } : {}
@@ -5319,14 +5289,14 @@ function createSandbox(options) {
5319
5289
  setToolCallSpanAttributes,
5320
5290
  fileSystem() {
5321
5291
  signal?.throwIfAborted();
5322
- return fileSystemPromise ??= runtime.tools().then(({ fs: fs5 }) => {
5292
+ return fileSystemPromise ??= runtime.tools(signal).then(({ fs: fs5 }) => {
5323
5293
  signal?.throwIfAborted();
5324
5294
  return bindSandboxFileSystem(fs5, signal);
5325
5295
  });
5326
5296
  },
5327
5297
  commandRunner() {
5328
5298
  signal?.throwIfAborted();
5329
- return commandRunnerPromise ??= runtime.tools().then(({ runCommand }) => {
5299
+ return commandRunnerPromise ??= runtime.tools(signal).then(({ runCommand }) => {
5330
5300
  signal?.throwIfAborted();
5331
5301
  return async (input) => await runCommand({
5332
5302
  ...input,
@@ -5366,15 +5336,10 @@ function createSandbox(options) {
5366
5336
  if (runtime.sandboxRef()) {
5367
5337
  return;
5368
5338
  }
5369
- logInfo(
5370
- "sandbox_boot_requested",
5371
- traceContext,
5372
- {
5373
- "app.sandbox.boot.trigger": trigger,
5374
- ...details
5375
- },
5376
- "Sandbox boot requested"
5377
- );
5339
+ logInfo("sandbox.boot.requested", {
5340
+ "app.sandbox.boot.trigger": trigger,
5341
+ ...details
5342
+ });
5378
5343
  };
5379
5344
  const executeBashTool = async (rawInput, command, context) => {
5380
5345
  const env = parseEnv(rawInput.env);
@@ -5382,7 +5347,7 @@ function createSandbox(options) {
5382
5347
  logSandboxBootRequest("tool.bash", {
5383
5348
  "app.sandbox.command_length": command.length
5384
5349
  });
5385
- const { bash: executeBash, sessionId: activeSessionId } = await runtime.tools();
5350
+ const { bash: executeBash, sessionId: activeSessionId } = await runtime.tools(context.signal);
5386
5351
  await clearEgressSignals(activeSessionId);
5387
5352
  const result = await withSandboxToolSpan(
5388
5353
  "bash",
@@ -6679,12 +6644,7 @@ var McpToolManager = class {
6679
6644
  };
6680
6645
  setSpanAttributes(errorAttributes);
6681
6646
  if (error instanceof McpToolError) {
6682
- logWarn(
6683
- "mcp_tool_call_failed",
6684
- {},
6685
- errorAttributes,
6686
- "MCP tool call failed"
6687
- );
6647
+ logWarn("mcp.tool_call.failed", errorAttributes);
6688
6648
  }
6689
6649
  throw error;
6690
6650
  }
@@ -6840,22 +6800,16 @@ async function enrichImagePrompt(rawPrompt) {
6840
6800
  maxTokens: 1024
6841
6801
  });
6842
6802
  if (text && text.trim().length > 0) {
6843
- logInfo(
6844
- "image_prompt_enriched",
6845
- {},
6846
- { "app.image.enriched_prompt_length": text.trim().length },
6847
- "Image prompt enriched with persona"
6848
- );
6803
+ logInfo("image.prompt.enriched", {
6804
+ "app.image.enriched_prompt_length": text.trim().length
6805
+ });
6849
6806
  return text.trim();
6850
6807
  }
6851
6808
  return rawPrompt;
6852
6809
  } catch (error) {
6853
- logWarn(
6854
- "image_prompt_enrichment_failed",
6855
- {},
6856
- { "exception.message": String(error) },
6857
- "Image prompt enrichment failed, using raw prompt"
6858
- );
6810
+ logWarn("image.prompt.enrichment.failed", {
6811
+ "exception.message": String(error)
6812
+ });
6859
6813
  return rawPrompt;
6860
6814
  }
6861
6815
  }
@@ -7526,7 +7480,7 @@ function createViewImageTool(sandbox, supportsImageInput, deps = {}) {
7526
7480
  import { z as z16 } from "zod";
7527
7481
  function createReportProgressTool() {
7528
7482
  return zodTool({
7529
- description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks'). Skip trivial direct answers, generic filler, and minor substeps.",
7483
+ description: "Update the user-visible assistant loading message with a short progress phase. Use this only for work with multiple substantive phases or a materially long wait. Skip short lookups, routine commands, generic filler, and minor substeps. After an initial update, call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks').",
7530
7484
  inputSchema: z16.object({
7531
7485
  message: z16.string().min(1).describe("Short user-facing progress message.")
7532
7486
  }),
@@ -8934,18 +8888,13 @@ async function grantChannelCanvasAccess(canvasId, channelId) {
8934
8888
  }
8935
8889
  }
8936
8890
  );
8937
- } catch (error) {
8938
- logWarn(
8939
- "slack_canvas_access_set_failed",
8940
- {},
8941
- {
8942
- "app.slack.action": "canvases.access.set",
8943
- "app.slack.canvas.canvas_id_prefix": canvasId.slice(0, 1),
8944
- "app.slack.canvas.channel_id_prefix": channelId.slice(0, 1),
8945
- "app.slack.canvas.access_level": "write"
8946
- },
8947
- error instanceof Error ? error.message : "Failed to grant channel access to canvas"
8948
- );
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
+ });
8949
8898
  }
8950
8899
  }
8951
8900
  async function writeCanvasMarkdown(input) {
@@ -9082,16 +9031,11 @@ function createSlackCanvasCreateTool(context, state) {
9082
9031
  execute: async ({ title, markdown }) => {
9083
9032
  const targetChannelId = context.destinationChannelId;
9084
9033
  if (!isConversationScopedChannel(targetChannelId)) {
9085
- logError(
9086
- "slack_canvas_create_invalid_context",
9087
- {},
9088
- {
9089
- "gen_ai.tool.name": "slackCanvasCreate",
9090
- "messaging.destination.name": targetChannelId ?? "none",
9091
- "app.slack.canvas.has_channel_context": Boolean(targetChannelId)
9092
- },
9093
- "Canvas create failed due to missing or invalid assistant channel context"
9094
- );
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
+ });
9095
9039
  throw new Error(
9096
9040
  "Cannot create a canvas without an active assistant channel context (C/G/D)."
9097
9041
  );
@@ -9224,15 +9168,10 @@ function createSlackCanvasEditTool(state) {
9224
9168
  return response;
9225
9169
  } catch (error) {
9226
9170
  const message = error instanceof Error ? error.message : "canvas edit failed";
9227
- logWarn(
9228
- "slack_canvas_edit_failed",
9229
- {},
9230
- {
9231
- "gen_ai.tool.name": "slackCanvasEdit",
9232
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9233
- },
9234
- message
9235
- );
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
+ });
9236
9175
  return {
9237
9176
  ok: false,
9238
9177
  status: "error",
@@ -9303,15 +9242,10 @@ function createSlackCanvasReadTool() {
9303
9242
  };
9304
9243
  } catch (error) {
9305
9244
  const message = error instanceof Error ? error.message : "canvas read failed";
9306
- logWarn(
9307
- "slack_canvas_read_failed",
9308
- {},
9309
- {
9310
- "gen_ai.tool.name": "slackCanvasRead",
9311
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9312
- },
9313
- message
9314
- );
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
+ });
9315
9249
  return {
9316
9250
  ok: false,
9317
9251
  status: "error",
@@ -9370,15 +9304,10 @@ function createSlackCanvasWriteTool(state) {
9370
9304
  return response;
9371
9305
  } catch (error) {
9372
9306
  const message = error instanceof Error ? error.message : "canvas write failed";
9373
- logWarn(
9374
- "slack_canvas_write_failed",
9375
- {},
9376
- {
9377
- "gen_ai.tool.name": "slackCanvasWrite",
9378
- "app.slack.canvas.canvas_id_prefix": target.canvasId.slice(0, 1)
9379
- },
9380
- message
9381
- );
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
+ });
9382
9311
  return {
9383
9312
  ok: false,
9384
9313
  status: "error",
@@ -10426,6 +10355,10 @@ function createSystemTimeTool() {
10426
10355
  import { z as z37 } from "zod";
10427
10356
  var DEFAULT_LIMIT3 = 25;
10428
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();
10429
10362
  var CONVERSATION_EVENTS_TOOL_SOURCE = {
10430
10363
  id: "conversation-events",
10431
10364
  description: "Inspect Junior's durable conversation event log for debugging runtime behavior."
@@ -10439,7 +10372,7 @@ var projectedEventSchema = z37.object({
10439
10372
  created_at: z37.iso.datetime().describe("When the event was recorded."),
10440
10373
  idempotency_key: z37.string().min(1).optional().describe("Retry-stable event identity, when one was assigned."),
10441
10374
  data: z37.record(z37.string(), z37.unknown()).describe(
10442
- "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."
10443
10376
  )
10444
10377
  }).strict();
10445
10378
  var queryConversationEventsOutputSchema = juniorToolResultSchema.extend({
@@ -10450,6 +10383,9 @@ var queryConversationEventsOutputSchema = juniorToolResultSchema.extend({
10450
10383
  ),
10451
10384
  has_newer: z37.boolean().describe(
10452
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."
10453
10389
  )
10454
10390
  });
10455
10391
  function createQueryConversationEventsTool(context) {
@@ -10529,14 +10465,17 @@ function createQueryConversationEventsTool(context) {
10529
10465
  ...beforeSeq === void 0 ? {} : { beforeSeq },
10530
10466
  ...types === void 0 ? {} : { types }
10531
10467
  });
10532
- const events = projectEventsForTool(page);
10468
+ const newestFirst = afterSeq === void 0;
10469
+ const projected = projectEventsForTool(page, { newestFirst });
10533
10470
  return {
10534
10471
  ok: true,
10535
10472
  status: "success",
10536
10473
  conversation_id: conversationId,
10537
- events,
10538
- has_older: page.hasOlder,
10539
- 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 } : {}
10540
10479
  };
10541
10480
  }
10542
10481
  });
@@ -10621,11 +10560,30 @@ function assertCanQueryConversationEvents(args) {
10621
10560
  `Conversation events are not accessible: ${targetConversationId}`
10622
10561
  );
10623
10562
  }
10624
- function projectEventsForTool(page) {
10625
- 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
+ };
10626
10584
  }
10627
10585
  function projectEvent(event) {
10628
- const data = stripReplacementHistory(event.data);
10586
+ const data = boundEventData(stripReplacementHistory(event.data));
10629
10587
  return {
10630
10588
  seq: event.seq,
10631
10589
  history_version: event.historyVersion,
@@ -10634,6 +10592,46 @@ function projectEvent(event) {
10634
10592
  data
10635
10593
  };
10636
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
+ }
10637
10635
  function stripReplacementHistory(data) {
10638
10636
  if (data.type === "unknown") {
10639
10637
  return {
@@ -10666,7 +10664,7 @@ var HANDOFF_TOOL_NAME = "handoff";
10666
10664
  function createHandoffTool(handoff) {
10667
10665
  const profileSchema = z38.enum(handoff.profiles);
10668
10666
  const handoffResultSchema = juniorToolResultSchema.extend({
10669
- model_profile: profileSchema
10667
+ model_profile: modelProfileSchema
10670
10668
  });
10671
10669
  const profileNames = handoff.profiles.map((profile) => `\`${profile}\``).join(", ");
10672
10670
  return zodTool({
@@ -11300,18 +11298,12 @@ function createWebSearchTool(modelId, override) {
11300
11298
  const message = formatSearchFailure(error);
11301
11299
  const timeout = /timed out/i.test(message);
11302
11300
  const retryable = !isAuthFailure(message);
11303
- logException(
11304
- error,
11305
- "web_search_failed",
11306
- {},
11307
- {
11308
- "gen_ai.tool.name": "webSearch",
11309
- "app.web_search.timeout": timeout,
11310
- "app.web_search.retryable": retryable,
11311
- "app.web_search.query": query
11312
- },
11313
- message
11314
- );
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
+ });
11315
11307
  return {
11316
11308
  ok: false,
11317
11309
  status: "error",
@@ -12029,7 +12021,7 @@ function getToolErrorAttributes(error) {
12029
12021
  ...error.detailRule ? { "app.slack.detail_rule": error.detailRule } : {}
12030
12022
  };
12031
12023
  }
12032
- function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, traceContext, conversationPrivacy, setExecutionSpanAttributes) {
12024
+ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conversationPrivacy, setExecutionSpanAttributes) {
12033
12025
  const errorType = getToolErrorType(error);
12034
12026
  const errorMessage = getMcpAwareTelemetryMessage(error, conversationPrivacy);
12035
12027
  const errorAttributes = {
@@ -12045,50 +12037,34 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
12045
12037
  }
12046
12038
  if (error instanceof PluginCredentialFailureError) {
12047
12039
  if (shouldTrace) {
12048
- logInfo(
12049
- "plugin_credential_rejected",
12050
- traceContext,
12051
- {
12052
- "app.credential.provider": error.provider,
12053
- "gen_ai.operation.name": "execute_tool",
12054
- "gen_ai.tool.name": toolName,
12055
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12056
- "error.type": errorType
12057
- },
12058
- "Plugin credentials were rejected during tool execution"
12059
- );
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
+ });
12060
12047
  }
12061
12048
  throw error;
12062
12049
  }
12063
12050
  if (shouldTrace) {
12064
- logWarn(
12065
- "agent_tool_call_failed",
12066
- traceContext,
12067
- {
12068
- "gen_ai.operation.name": "execute_tool",
12069
- "gen_ai.tool.name": toolName,
12070
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12071
- ...errorAttributes,
12072
- "exception.message": errorMessage
12073
- },
12074
- "Agent tool call failed"
12075
- );
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
+ });
12076
12058
  }
12077
12059
  const isExpectedToolFailure = error instanceof McpToolError || error instanceof ToolInputError || isPluginToolInputError(error);
12078
12060
  if (!isExpectedToolFailure) {
12079
- logException(
12080
- error,
12081
- "agent_tool_call_failed",
12082
- {},
12083
- {
12084
- "gen_ai.operation.name": "execute_tool",
12085
- "gen_ai.tool.name": toolName,
12086
- ...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
12087
- ...errorAttributes,
12088
- ...getToolErrorAttributes(error)
12089
- },
12090
- "Agent tool call failed"
12091
- );
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
+ });
12092
12068
  }
12093
12069
  throw error;
12094
12070
  }
@@ -12456,15 +12432,10 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12456
12432
  try {
12457
12433
  await onToolResult?.(report);
12458
12434
  } catch (error) {
12459
- logWarn(
12460
- "tool_result_observer_failed",
12461
- spanContext,
12462
- {
12463
- "gen_ai.tool.name": report.toolName,
12464
- "exception.message": error instanceof Error ? error.message : String(error)
12465
- },
12466
- "Tool result observer failed"
12467
- );
12435
+ logWarn("tool.result.observer.failed", {
12436
+ "gen_ai.tool.name": report.toolName,
12437
+ "exception.message": error instanceof Error ? error.message : String(error)
12438
+ });
12468
12439
  }
12469
12440
  };
12470
12441
  const toolResultOk = (details) => {
@@ -12523,15 +12494,10 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12523
12494
  projectedPrivateResult = toolDef.privateTraceResult(resultAttributeValue);
12524
12495
  hasProjectedPrivateResult = projectedPrivateResult !== void 0;
12525
12496
  } catch (error) {
12526
- logWarn(
12527
- "tool_private_trace_projection_failed",
12528
- spanContext,
12529
- {
12530
- "error.type": error instanceof Error ? error.name : typeof error,
12531
- "gen_ai.tool.name": toolName
12532
- },
12533
- "Tool private trace projection failed"
12534
- );
12497
+ logWarn("tool.private.trace_projection.failed", {
12498
+ "error.type": error instanceof Error ? error.name : typeof error,
12499
+ "gen_ai.tool.name": toolName
12500
+ });
12535
12501
  }
12536
12502
  }
12537
12503
  const toolResultAttribute = effectiveConversationPrivacy === "private" && toolDef.privateTraceResult && !hasProjectedPrivateResult ? void 0 : serializeToolPayload(
@@ -12645,7 +12611,6 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
12645
12611
  executionToolName,
12646
12612
  toolCallId,
12647
12613
  shouldTrace,
12648
- spanContext,
12649
12614
  effectiveConversationPrivacy,
12650
12615
  setSpanAttributes2
12651
12616
  );
@@ -13118,26 +13083,21 @@ function logSandboxEgressUpstreamRequest(input) {
13118
13083
  if (!shouldLogSandboxEgressInfo()) {
13119
13084
  return;
13120
13085
  }
13121
- logInfo(
13122
- "sandbox_egress_upstream_request",
13123
- {},
13124
- {
13125
- ...egressAttributes({
13126
- egressId: input.egressId,
13127
- grantAccess: input.grantAccess,
13128
- grantName: input.grantName,
13129
- grantReason: input.grantReason,
13130
- host: input.upstreamUrl.hostname,
13131
- method: input.request.method,
13132
- path: input.upstreamUrl.pathname,
13133
- provider: input.provider,
13134
- status: input.upstream.status
13135
- }),
13136
- ...routingAttributes(input.request, input.upstreamUrl),
13137
- "app.sandbox.egress.upstream_ok": input.upstream.ok
13138
- },
13139
- `Sandbox egress ${input.request.method} ${input.upstreamUrl.hostname}${displayedUpstreamPath(input.upstreamUrl)} -> ${input.upstream.status}`
13140
- );
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
+ });
13141
13101
  }
13142
13102
  async function requestBodyBytes(request) {
13143
13103
  if (request.method === "GET" || request.method === "HEAD" || request.body === null) {
@@ -13334,22 +13294,18 @@ async function executeCredentialedEgressRequest(input) {
13334
13294
  });
13335
13295
  } catch (error) {
13336
13296
  if (isEgressPolicyDenied(error)) {
13337
- logWarn(
13338
- "sandbox_egress_policy_denied",
13339
- {},
13340
- {
13341
- ...egressAttributes({
13342
- egressId: activeEgressId,
13343
- host: upstreamUrl.hostname,
13344
- method: request.method,
13345
- path: upstreamUrl.pathname,
13346
- provider,
13347
- status: 403
13348
- }),
13349
- ...routingAttributes(request, upstreamUrl)
13350
- },
13351
- error.message
13352
- );
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
+ });
13353
13309
  return policyDeniedResponse(error);
13354
13310
  }
13355
13311
  throw error;
@@ -13377,8 +13333,7 @@ async function executeCredentialedEgressRequest(input) {
13377
13333
  });
13378
13334
  const isAuthRequired = error.kind === "auth_required";
13379
13335
  logWarn(
13380
- isAuthRequired ? "sandbox_egress_credential_needed" : "sandbox_egress_credential_unavailable",
13381
- {},
13336
+ isAuthRequired ? "sandbox.egress.credential.needed" : "sandbox.egress.credential.unavailable",
13382
13337
  {
13383
13338
  ...egressAttributes({
13384
13339
  egressId: activeEgressId,
@@ -13392,8 +13347,7 @@ async function executeCredentialedEgressRequest(input) {
13392
13347
  status: 401
13393
13348
  }),
13394
13349
  ...routingAttributes(request, upstreamUrl)
13395
- },
13396
- isAuthRequired ? "Sandbox egress grant needs user authorization before issuing a credential lease" : "Sandbox egress credential lease is unavailable for selected grant"
13350
+ }
13397
13351
  );
13398
13352
  return authRequiredResponse({
13399
13353
  provider: error.provider,
@@ -13413,17 +13367,12 @@ async function executeCredentialedEgressRequest(input) {
13413
13367
  upstreamUrl
13414
13368
  });
13415
13369
  if (!hasSandboxEgressLeaseTransformForHost(lease, upstreamUrl.hostname)) {
13416
- logWarn(
13417
- "sandbox_egress_transform_missing",
13418
- {},
13419
- {
13420
- ...attributes(403),
13421
- "app.sandbox.egress.transform_domains": lease.headerTransforms.map(
13422
- (transform) => transform.domain
13423
- )
13424
- },
13425
- "Sandbox egress credential lease does not cover forwarded host"
13426
- );
13370
+ logWarn("sandbox.egress.transform.missing", {
13371
+ ...attributes(403),
13372
+ "app.sandbox.egress.transform_domains": lease.headerTransforms.map(
13373
+ (transform) => transform.domain
13374
+ )
13375
+ });
13427
13376
  return Response.json(
13428
13377
  { error: "Credential lease does not cover forwarded host" },
13429
13378
  { status: 403 }
@@ -13477,14 +13426,9 @@ async function executeCredentialedEgressRequest(input) {
13477
13426
  upstream,
13478
13427
  upstreamUrl
13479
13428
  });
13480
- logWarn(
13481
- "sandbox_egress_upstream_permission_classified",
13482
- {},
13483
- {
13484
- ...attributes(upstream.status, upstream)
13485
- },
13486
- "Sandbox egress plugin classified upstream response as permission denied"
13487
- );
13429
+ logWarn("sandbox.egress.upstream_permission.classified", {
13430
+ ...attributes(upstream.status, upstream)
13431
+ });
13488
13432
  }
13489
13433
  } catch (error) {
13490
13434
  if (!isEgressAuthRequired(error)) {
@@ -13498,14 +13442,9 @@ async function executeCredentialedEgressRequest(input) {
13498
13442
  authorization: error.authorization ?? lease.authorization,
13499
13443
  message: error.message
13500
13444
  });
13501
- logWarn(
13502
- "sandbox_egress_upstream_auth_required_classified",
13503
- {},
13504
- {
13505
- ...attributes(upstream.status, upstream)
13506
- },
13507
- "Sandbox egress plugin classified upstream response as auth required"
13508
- );
13445
+ logWarn("sandbox.egress.upstream_auth_requirement.classified", {
13446
+ ...attributes(upstream.status, upstream)
13447
+ });
13509
13448
  await upstream.body?.cancel().catch(() => void 0);
13510
13449
  return authRequiredResponse({
13511
13450
  provider,
@@ -13524,28 +13463,18 @@ async function executeCredentialedEgressRequest(input) {
13524
13463
  upstreamUrl
13525
13464
  });
13526
13465
  if (upstream.status >= 400) {
13527
- logWarn(
13528
- "sandbox_egress_upstream_error_response",
13529
- {},
13530
- {
13531
- ...attributes(upstream.status, upstream),
13532
- "error.type": `http_${upstream.status}`
13533
- },
13534
- `Sandbox egress upstream returned HTTP ${upstream.status}`
13535
- );
13466
+ logWarn("sandbox.egress.upstream_response.failed", {
13467
+ ...attributes(upstream.status, upstream),
13468
+ "error.type": `http_${upstream.status}`
13469
+ });
13536
13470
  }
13537
13471
  if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS || upstream.status === UPSTREAM_PERMISSION_REJECTION_STATUS) {
13538
- logWarn(
13539
- "sandbox_egress_upstream_auth_rejected",
13540
- {},
13541
- {
13542
- ...attributes(upstream.status, upstream),
13543
- ...upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? {
13544
- "app.sandbox.egress.www_authenticate": upstream.headers.get("www-authenticate") ?? void 0
13545
- } : {}
13546
- },
13547
- upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS ? "Sandbox egress upstream auth rejected injected credential" : "Sandbox egress upstream permission denied"
13548
- );
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
+ });
13549
13478
  if (upstream.status === UPSTREAM_TOKEN_REJECTION_STATUS) {
13550
13479
  await clearCredentialLease(provider, lease.grant, credentialContext);
13551
13480
  await recordAuthRequired({
@@ -13644,7 +13573,7 @@ function createPluginEgress(deps) {
13644
13573
  }
13645
13574
 
13646
13575
  // src/chat/agent/tools.ts
13647
- async function tryRecordSkillLoadStat(skill, spanContext) {
13576
+ async function tryRecordSkillLoadStat(skill) {
13648
13577
  if (!process.env.DATABASE_URL) return;
13649
13578
  try {
13650
13579
  await incrementStat({
@@ -13653,16 +13582,11 @@ async function tryRecordSkillLoadStat(skill, spanContext) {
13653
13582
  name: skill.name
13654
13583
  });
13655
13584
  } catch (error) {
13656
- logWarn(
13657
- "skill_load_stat_failed",
13658
- spanContext,
13659
- {
13660
- "app.skill.name": skill.name,
13661
- "app.plugin.name": skill.pluginProvider,
13662
- "exception.message": error instanceof Error ? error.message : String(error)
13663
- },
13664
- "Failed to record skill load stat"
13665
- );
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
+ });
13666
13590
  }
13667
13591
  }
13668
13592
  async function wireAgentTools(args) {
@@ -13822,7 +13746,7 @@ async function wireAgentTools(args) {
13822
13746
  if (await mcpToolManager.activateForSkill(effective)) {
13823
13747
  await args.recordConnectedMcpProvider(effective.pluginProvider);
13824
13748
  }
13825
- await tryRecordSkillLoadStat(effective, args.spanContext);
13749
+ await tryRecordSkillLoadStat(effective);
13826
13750
  if (mcpAuth.getPendingPause()) {
13827
13751
  return void 0;
13828
13752
  }
@@ -13888,15 +13812,10 @@ async function wireAgentTools(args) {
13888
13812
  toolName
13889
13813
  });
13890
13814
  } catch (error) {
13891
- logWarn(
13892
- "tool_invocation_observer_failed",
13893
- args.spanContext,
13894
- {
13895
- "gen_ai.tool.name": toolName,
13896
- "exception.message": error instanceof Error ? error.message : String(error)
13897
- },
13898
- "Tool invocation observer failed"
13899
- );
13815
+ logWarn("tool.invocation.observer.failed", {
13816
+ "gen_ai.tool.name": toolName,
13817
+ "exception.message": error instanceof Error ? error.message : String(error)
13818
+ });
13900
13819
  }
13901
13820
  };
13902
13821
  const agentTools = createPiAgentTools(
@@ -13923,18 +13842,13 @@ async function wireAgentTools(args) {
13923
13842
  try {
13924
13843
  await mcpToolManager.close();
13925
13844
  } catch (closeError) {
13926
- logWarn(
13927
- "mcp_tool_manager_close_failed",
13928
- {},
13929
- {
13930
- ...getMcpProviderErrorAttributes(closeError),
13931
- "exception.message": getMcpAwareTelemetryMessage(
13932
- closeError,
13933
- args.conversationPrivacy
13934
- )
13935
- },
13936
- "Failed to close MCP tool manager"
13937
- );
13845
+ logWarn("mcp.tool_manager.close.failed", {
13846
+ ...getMcpProviderErrorAttributes(closeError),
13847
+ "exception.message": getMcpAwareTelemetryMessage(
13848
+ closeError,
13849
+ args.conversationPrivacy
13850
+ )
13851
+ });
13938
13852
  }
13939
13853
  },
13940
13854
  toolGuidance,
@@ -13967,7 +13881,6 @@ function createResumeState(args) {
13967
13881
  source: args.runSource,
13968
13882
  sessionId: args.turnId,
13969
13883
  loadedSkillNames: args.getLoadedSkillNames(),
13970
- logContext: args.logContext,
13971
13884
  modelId: args.getModelId(),
13972
13885
  ...args.getReasoningLevel() ? { reasoningLevel: args.getReasoningLevel() } : {},
13973
13886
  actor: args.actor,
@@ -14148,6 +14061,7 @@ function createResumeState(args) {
14148
14061
  }
14149
14062
 
14150
14063
  // src/chat/services/context-compaction.ts
14064
+ import { estimateContextTokens } from "@earendil-works/pi-agent-core";
14151
14065
  var RETAINED_USER_MESSAGE_TOKENS = 2e4;
14152
14066
  var MAX_SUMMARY_INPUT_CHARS = 8e4;
14153
14067
  var MAX_VISIBLE_CONTEXT_CHARS = 2e4;
@@ -14341,10 +14255,7 @@ async function summarizeContext(args, deps) {
14341
14255
  return summary.slice(0, MAX_SUMMARY_CHARS);
14342
14256
  }
14343
14257
  function estimateHistoryTokens(messages) {
14344
- return messages.reduce(
14345
- (total, message) => total + estimateTextTokens(JSON.stringify(message) ?? ""),
14346
- 0
14347
- );
14258
+ return estimateContextTokens(messages).tokens;
14348
14259
  }
14349
14260
  function buildReplacementProvenance(args) {
14350
14261
  return [
@@ -14401,21 +14312,9 @@ async function maybeCompactWithDeps(args, deps) {
14401
14312
  deps
14402
14313
  );
14403
14314
  } catch (error) {
14404
- logWarn(
14405
- "context_compaction_summary_failed",
14406
- {
14407
- messageConversationId: args.metadata?.threadId,
14408
- userId: args.metadata?.actorId,
14409
- destinationName: args.metadata?.channelId,
14410
- runId: args.metadata?.runId,
14411
- assistantUserName: botConfig.userName,
14412
- modelId: botConfig.fastModelId
14413
- },
14414
- {
14415
- "exception.message": error instanceof Error ? error.message : String(error)
14416
- },
14417
- "Context compaction failed; continuing with prior history"
14418
- );
14315
+ logWarn("context_compaction.summary.failed", {
14316
+ "exception.message": error instanceof Error ? error.message : String(error)
14317
+ });
14419
14318
  return { compacted: false, reason: "summary_failed" };
14420
14319
  }
14421
14320
  return await writeCompactedThreadContext(args, source.messages, summary, {
@@ -14555,23 +14454,11 @@ async function compactActiveContextIfNeeded(args, deps) {
14555
14454
  inputLimitTokens
14556
14455
  );
14557
14456
  }
14558
- logWarn(
14559
- "active_context_compaction_summary_failed",
14560
- {
14561
- messageConversationId: args.metadata?.threadId,
14562
- userId: args.metadata?.actorId,
14563
- destinationName: args.metadata?.channelId,
14564
- runId: args.metadata?.runId,
14565
- assistantUserName: botConfig.userName,
14566
- modelId: args.modelId
14567
- },
14568
- {
14569
- "app.context_input_limit_tokens": inputLimitTokens,
14570
- "app.context_tokens_estimated": source.estimatedTokens,
14571
- "exception.message": error instanceof Error ? error.message : String(error)
14572
- },
14573
- "Active context compaction failed below the hard input limit"
14574
- );
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
+ });
14575
14462
  return { compacted: false, reason: "summary_failed" };
14576
14463
  }
14577
14464
  const summaryMessage = userMessage(
@@ -14673,7 +14560,7 @@ function waitForAbortSettlement(promise, timeoutMs) {
14673
14560
  );
14674
14561
  });
14675
14562
  }
14676
- async function executeAgentRun(request) {
14563
+ async function executeAgentRun(request, streamFn) {
14677
14564
  if (!request.routing.destination) {
14678
14565
  throw new TypeError("Assistant reply generation requires a destination");
14679
14566
  }
@@ -14708,12 +14595,13 @@ async function executeAgentRun(request) {
14708
14595
  () => executeAgentRunInPrivacyContext(
14709
14596
  request,
14710
14597
  conversationPrivacy,
14711
- runLogContext
14598
+ runLogContext,
14599
+ streamFn
14712
14600
  )
14713
14601
  )
14714
14602
  );
14715
14603
  }
14716
- async function executeAgentRunInPrivacyContext(request, conversationPrivacy, runLogContext) {
14604
+ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, runLogContext, streamFn) {
14717
14605
  const { conversationId, input, routing, runId, turnId } = request;
14718
14606
  const policy = request.policy ?? {};
14719
14607
  const signal = policy.signal;
@@ -14751,19 +14639,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14751
14639
  const slackDestination = routing.destination.platform === "slack" ? routing.destination : void 0;
14752
14640
  const slackActor = actor?.platform === "slack" ? actor : void 0;
14753
14641
  const userInput = input.messageText;
14754
- const credentialActor = routing.credentialContext?.actor;
14755
- const credentialActorLogContext = credentialActor ? {
14756
- actorType: "type" in credentialActor ? credentialActor.type : "system",
14757
- actorId: "type" in credentialActor ? credentialActor.userId : credentialActor.name
14758
- } : {};
14759
- const sessionRecordLogContext = {
14760
- threadId: slackSource ? conversationId : void 0,
14761
- actorId: slackActor?.userId,
14762
- channelId: slackDestination?.channelId,
14763
- runId,
14764
- ...credentialActorLogContext,
14765
- assistantUserName: botConfig.userName
14766
- };
14767
14642
  const recordConnectedMcpProvider = async (provider) => {
14768
14643
  if (connectedMcpProviders.has(provider)) {
14769
14644
  return;
@@ -14790,27 +14665,21 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14790
14665
  const shouldTrace = shouldEmitDevAgentTrace();
14791
14666
  const spanContext = { modelId: activeModelId };
14792
14667
  const availableSkills = await discoverRunSkills({
14793
- skillDirs: policy.skillDirs,
14794
- spanContext
14668
+ skillDirs: policy.skillDirs
14795
14669
  });
14796
14670
  if (shouldTrace) {
14797
14671
  const inboundAttachmentCount = input.inboundAttachmentCount ?? 0;
14798
14672
  const promptAttachmentCount = input.userAttachments?.length ?? 0;
14799
- logInfo(
14800
- "agent_message_in",
14801
- spanContext,
14802
- {
14803
- "app.message.kind": "user_inbound",
14804
- "app.message.length": userInput.length,
14805
- "app.message.input": summarizeMessageText(userInput),
14806
- // Log both counts so image uploads filtered by vision/config do not
14807
- // look indistinguishable from Slack ingress dropping attachments.
14808
- "app.message.attachment_count": inboundAttachmentCount,
14809
- "app.message.prompt_attachment_count": promptAttachmentCount,
14810
- "messaging.message.id": slackSource?.messageTs ?? ""
14811
- },
14812
- "Agent message received"
14813
- );
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
+ });
14814
14683
  }
14815
14684
  const skillInvocation = parseSkillInvocation(userInput, availableSkills);
14816
14685
  const invokedSkill = skillInvocation ? findSkillByName(skillInvocation.skillName, availableSkills) : null;
@@ -14842,7 +14711,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14842
14711
  durability,
14843
14712
  getLoadedSkillNames: () => loadedSkillNamesForResume,
14844
14713
  getReasoningLevel: () => turnRoute?.reasoningLevel,
14845
- logContext: sessionRecordLogContext,
14846
14714
  getModelId: () => activeModelId,
14847
14715
  recordActiveMcpProviders,
14848
14716
  actor,
@@ -14862,15 +14730,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
14862
14730
  toolName: event.toolName
14863
14731
  });
14864
14732
  } catch (error) {
14865
- logException(
14866
- error,
14867
- "agent_turn_session_log_append_failed",
14868
- spanContext,
14869
- {
14870
- "gen_ai.tool.name": event.toolName
14871
- },
14872
- "Failed to record host-only tool execution start"
14873
- );
14733
+ logException(error, "agent.turn.session_log_append.failed", {
14734
+ "gen_ai.tool.name": event.toolName
14735
+ });
14874
14736
  }
14875
14737
  };
14876
14738
  const persistedConfigurationValues = policy.channelConfiguration ? await policy.channelConfiguration.resolveValues() : {};
@@ -15005,14 +14867,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15005
14867
  void (async () => {
15006
14868
  await observers.onStatus?.({ text: "Switching models" });
15007
14869
  })().catch((error) => {
15008
- logWarn(
15009
- "assistant_status_observer_failed",
15010
- {},
15011
- {
15012
- "exception.message": error instanceof Error ? error.message : String(error)
15013
- },
15014
- "Failed to report assistant status"
15015
- );
14870
+ logWarn("assistant.status.observer.failed", {
14871
+ "exception.message": error instanceof Error ? error.message : String(error)
14872
+ });
15016
14873
  });
15017
14874
  const handoffMessages = await compactContextForHandoff(
15018
14875
  {
@@ -15051,15 +14908,21 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15051
14908
  activeModelProfile = args.profile;
15052
14909
  activeModelId = target.modelId;
15053
14910
  };
15054
- const requestHandoff = {
15055
- profiles: handoffProfiles,
15056
- execute: async (profile, options) => await scheduleHandoff({
15057
- profile,
15058
- signal: options.signal,
15059
- sourceMessages: [...agent.state.messages],
15060
- triggeringToolCallId: options.toolCallId
15061
- })
14911
+ const handoffControlFor = (activeProfile) => {
14912
+ const profiles = handoffProfiles.filter(
14913
+ (profile) => profile !== activeProfile
14914
+ );
14915
+ return profiles.length > 0 ? {
14916
+ profiles,
14917
+ execute: async (profile, options) => await scheduleHandoff({
14918
+ profile,
14919
+ signal: options.signal,
14920
+ sourceMessages: [...agent.state.messages],
14921
+ triggeringToolCallId: options.toolCallId
14922
+ })
14923
+ } : void 0;
15062
14924
  };
14925
+ const requestHandoff = handoffControlFor(activeModelProfile);
15063
14926
  setTags({
15064
14927
  ...runLogContext,
15065
14928
  modelId: activeModelId
@@ -15103,7 +14966,30 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15103
14966
  mcpToolManager = wiring.mcpToolManager;
15104
14967
  closeTools = wiring.close;
15105
14968
  const getPendingAuthPause = wiring.getPendingAuthPause;
15106
- const toolsAfterHandoff = wiring.agentTools;
14969
+ const toolsWithoutHandoff = wiring.agentTools.filter(
14970
+ (tool) => tool.name !== HANDOFF_TOOL_NAME
14971
+ );
14972
+ const handoffAgentTool = wiring.agentTools.find(
14973
+ (tool) => tool.name === HANDOFF_TOOL_NAME
14974
+ );
14975
+ const toolsForActiveProfile = () => {
14976
+ const handoff = handoffControlFor(activeModelProfile);
14977
+ if (!handoff) {
14978
+ return toolsWithoutHandoff;
14979
+ }
14980
+ if (!handoffAgentTool) {
14981
+ throw new Error("Handoff control is missing its Pi tool");
14982
+ }
14983
+ const definition = createHandoffTool(handoff);
14984
+ return wiring.agentTools.map(
14985
+ (tool) => tool.name === HANDOFF_TOOL_NAME ? {
14986
+ ...handoffAgentTool,
14987
+ description: definition.description,
14988
+ parameters: definition.inputSchema,
14989
+ prepareArguments: definition.prepareArguments
14990
+ } : tool
14991
+ );
14992
+ };
15107
14993
  const {
15108
14994
  baseInstructions,
15109
14995
  inputMessages,
@@ -15141,7 +15027,8 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15141
15027
  agent.state.messages = replacement;
15142
15028
  agent.state.model = model;
15143
15029
  agent.state.thinkingLevel = thinkingLevel;
15144
- agent.state.tools = toolsAfterHandoff;
15030
+ const tools = toolsForActiveProfile();
15031
+ agent.state.tools = tools;
15145
15032
  runResume.setBeforeMessageCount(replacement.length);
15146
15033
  runResume.setTurnStartMessageIndex(0);
15147
15034
  runResume.adoptCommittedBoundary(replacement);
@@ -15154,7 +15041,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15154
15041
  context: {
15155
15042
  systemPrompt: baseInstructions,
15156
15043
  messages: replacement,
15157
- tools: toolsAfterHandoff
15044
+ tools
15158
15045
  },
15159
15046
  model,
15160
15047
  thinkingLevel
@@ -15208,12 +15095,12 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15208
15095
  };
15209
15096
  let assistantMessageDeliveryError;
15210
15097
  const deliverAssistantMessage = async (message) => {
15211
- const text = getAssistantMessageText(message);
15212
- if (!text || !delivery) {
15098
+ const decision = decideReply(message);
15099
+ if (decision.kind !== "deliver" || !delivery) {
15213
15100
  return;
15214
15101
  }
15215
15102
  try {
15216
- await delivery.onAssistantMessage({ text });
15103
+ await delivery(message);
15217
15104
  } catch (error) {
15218
15105
  assistantMessageDeliveryError = new AssistantMessageDeliveryError(
15219
15106
  error
@@ -15252,27 +15139,17 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15252
15139
  steeredMessageCount += piMessages.length;
15253
15140
  });
15254
15141
  if (steeredMessageCount > 0) {
15255
- logInfo(
15256
- "agent_turn_steering_messages_accepted",
15257
- spanContext,
15258
- {
15259
- "app.ai.steering_message_count": steeredMessageCount
15260
- },
15261
- "Agent turn steering messages accepted"
15262
- );
15142
+ logInfo("agent.turn.steering_messages.accepted", {
15143
+ "app.ai.steering_message_count": steeredMessageCount
15144
+ });
15263
15145
  }
15264
15146
  } catch (error) {
15265
15147
  if (isTurnInputCommitLostError(error)) {
15266
15148
  throw error;
15267
15149
  }
15268
- logWarn(
15269
- "agent_turn_steering_messages_drain_failed",
15270
- spanContext,
15271
- {
15272
- "exception.message": error instanceof Error ? error.message : String(error)
15273
- },
15274
- "Agent turn steering message drain failed"
15275
- );
15150
+ logWarn("agent.turn.steering_messages_drain.failed", {
15151
+ "exception.message": error instanceof Error ? error.message : String(error)
15152
+ });
15276
15153
  }
15277
15154
  return acceptedMessages;
15278
15155
  };
@@ -15280,7 +15157,10 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15280
15157
  let pendingPiHookError;
15281
15158
  agent = new Agent({
15282
15159
  ...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
15283
- streamFn: createTracedStreamFn({ conversationPrivacy }),
15160
+ streamFn: createTracedStreamFn({
15161
+ conversationPrivacy,
15162
+ ...streamFn ? { base: streamFn } : {}
15163
+ }),
15284
15164
  steeringMode: "all",
15285
15165
  beforeToolCall: async ({ assistantMessage }) => {
15286
15166
  const toolCalls2 = assistantMessage.content.filter(
@@ -15424,37 +15304,27 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15424
15304
  );
15425
15305
  } catch (error) {
15426
15306
  if (runResume.timedOut) {
15427
- logWarn(
15428
- "agent_turn_timeout",
15429
- {},
15430
- {
15431
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
15432
- "gen_ai.operation.name": "invoke_agent",
15433
- "gen_ai.request.model": activeModelId,
15434
- ...turnRoute ? {
15435
- "gen_ai.request.reasoning.level": turnRoute.reasoningLevel
15436
- } : {},
15437
- "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
15438
- "app.ai.turn_deadline_remaining_ms": Math.max(
15439
- 0,
15440
- turnDeadlineAtMs - Date.now()
15441
- )
15442
- },
15443
- "Agent turn timed out and was aborted"
15444
- );
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
+ });
15445
15320
  const settled = await waitForAbortSettlement(
15446
15321
  run2,
15447
15322
  AGENT_ABORT_SETTLE_GRACE_MS
15448
15323
  );
15449
15324
  if (!settled) {
15450
- logWarn(
15451
- "agent_turn_abort_settle_timeout",
15452
- {},
15453
- {
15454
- "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS
15455
- },
15456
- "Timed-out agent run did not settle after abort before resume snapshot"
15457
- );
15325
+ logWarn("agent.turn.abort_settle.timed_out", {
15326
+ "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS
15327
+ });
15458
15328
  }
15459
15329
  runResume.captureResumeSnapshot(
15460
15330
  runResume.getResumeSnapshot(currentAgentMessages())
@@ -15518,9 +15388,20 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15518
15388
  ]);
15519
15389
  }
15520
15390
  run = shouldPromptAgent && !capacityUpdate ? agent.prompt(freshPromptMessage) : agent.continue();
15521
- 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
+ };
15522
15403
  try {
15523
- for (let attempt = 0; ; attempt += 1) {
15404
+ for (; ; ) {
15524
15405
  await runAgentStep(run);
15525
15406
  if (assistantMessageDeliveryError) {
15526
15407
  throw assistantMessageDeliveryError;
@@ -15537,7 +15418,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15537
15418
  const usageSummary = extractGenAiUsageSummary(...outputMessages);
15538
15419
  const currentUsage = hasAgentTurnUsage(usageSummary) ? usageSummary : void 0;
15539
15420
  const currentPhaseUsage = addAgentTurnUsage(
15540
- retryUsage,
15421
+ discardedRetryUsage,
15541
15422
  currentUsage
15542
15423
  );
15543
15424
  turnUsage = addAgentTurnUsage(priorPhaseUsage, currentPhaseUsage);
@@ -15552,7 +15433,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15552
15433
  normalizeGenAiFinishReason(lastAssistant.stopReason)
15553
15434
  ]
15554
15435
  } : {},
15555
- ...extractGenAiUsageAttributes(usageSummary)
15436
+ ...extractGenAiUsageAttributes(currentPhaseUsage)
15556
15437
  });
15557
15438
  const pendingAuthPause = getPendingAuthPause();
15558
15439
  if (pendingAuthPause) {
@@ -15561,25 +15442,37 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15561
15442
  );
15562
15443
  throw pendingAuthPause;
15563
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
+ }
15564
15464
  const providerRetry = nextProviderRetry({
15565
- attempt,
15465
+ attempt: providerRetryAttempt,
15566
15466
  failure: lastAssistant,
15567
15467
  messages: agent.state.messages
15568
15468
  });
15569
15469
  if (!providerRetry) {
15570
15470
  break;
15571
15471
  }
15572
- retryUsage = currentPhaseUsage;
15573
- agent.state.messages = providerRetry.messages;
15574
- await runResume.persistSafeBoundary(providerRetry.messages);
15575
- logWarn(
15576
- "agent_turn_provider_retry",
15577
- spanContext,
15578
- {},
15579
- "Retrying transient provider failure"
15580
- );
15472
+ providerRetryAttempt += 1;
15473
+ await prepareRetry(providerRetry.messages);
15474
+ logWarn("agent.turn.provider.retrying");
15581
15475
  await sleep(providerRetry.delayMs, signal);
15582
- signal?.throwIfAborted();
15583
15476
  run = agent.continue();
15584
15477
  }
15585
15478
  } catch (error) {
@@ -15624,7 +15517,6 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15624
15517
  durationMs: Date.now() - replyStartedAtMs,
15625
15518
  generatedFileCount: generatedFiles.length,
15626
15519
  shouldTrace,
15627
- spanContext,
15628
15520
  usage: turnUsage,
15629
15521
  executionProfile: turnRoute,
15630
15522
  assistantUserName: botConfig.userName,
@@ -15672,13 +15564,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
15672
15564
  if (durability.onInputCommitted && !resume?.inputCommitted) {
15673
15565
  throw error;
15674
15566
  }
15675
- logException(
15676
- error,
15677
- "assistant_reply_generation_failed",
15678
- { modelId: activeModelId },
15679
- {},
15680
- "executeAgentRun failed"
15681
- );
15567
+ logException(error, "assistant.reply.generation.failed");
15682
15568
  const message = error instanceof Error ? error.message : String(error);
15683
15569
  return {
15684
15570
  status: "completed",