@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.
- package/dist/{agent-hooks-5DDPWRYO.js → agent-hooks-HJMLQLCB.js} +8 -8
- package/dist/api.js +14 -14
- package/dist/app.js +604 -997
- package/dist/{catalog-runtime-5KR5XBMJ.js → catalog-runtime-CYPZUYJD.js} +5 -5
- package/dist/chat/agent/index.d.ts +19 -2
- package/dist/chat/agent/request.d.ts +8 -8
- package/dist/chat/agent/resume.d.ts +1 -3
- package/dist/chat/agent/retry-usage.d.ts +8 -0
- package/dist/chat/agent/skills.d.ts +0 -9
- package/dist/chat/conversations/messages.d.ts +7 -0
- package/dist/chat/conversations/projection.d.ts +16 -0
- package/dist/chat/logging.d.ts +46 -13
- package/dist/chat/runtime/processing-reaction.d.ts +2 -4
- package/dist/chat/runtime/slack-runtime.d.ts +2 -2
- package/dist/chat/sandbox/noninteractive-command.d.ts +1 -0
- package/dist/chat/sandbox/session.d.ts +2 -2
- package/dist/chat/sandbox/snapshot/install.d.ts +2 -2
- package/dist/chat/sandbox/snapshot/resolve.d.ts +1 -0
- package/dist/chat/sandbox/workspace.d.ts +3 -1
- package/dist/chat/services/assistant-reply.d.ts +16 -0
- package/dist/chat/services/empty-output-continuation.d.ts +20 -0
- package/dist/chat/services/turn-failure-response.d.ts +1 -3
- package/dist/chat/services/turn-result.d.ts +0 -5
- package/dist/chat/services/turn-session-record.d.ts +0 -11
- package/dist/chat/tool-support/zod-tool.d.ts +11 -6
- package/dist/chat/tools/execute-tool.d.ts +1 -33
- package/dist/chat/tools/execution/tool-error-handler.d.ts +2 -2
- package/dist/chat/tools/query-conversation-events.d.ts +3 -1
- package/dist/chat/tools/sandbox/bash.d.ts +1 -33
- package/dist/chat/tools/sandbox/edit-file.d.ts +1 -33
- package/dist/chat/tools/sandbox/find-files.d.ts +1 -33
- package/dist/chat/tools/sandbox/grep.d.ts +1 -33
- package/dist/chat/tools/sandbox/list-dir.d.ts +1 -33
- package/dist/chat/tools/sandbox/read-file.d.ts +1 -33
- package/dist/chat/tools/sandbox/write-file.d.ts +1 -33
- package/dist/{chunk-VURFJVFH.js → chunk-2RFM7STT.js} +11 -22
- package/dist/{chunk-6WX5SPI3.js → chunk-32FPGM2V.js} +1 -1
- package/dist/{chunk-ALYQOZG7.js → chunk-4SPRQJOW.js} +37 -45
- package/dist/{chunk-QOQPPRFS.js → chunk-4TGVC42X.js} +133 -206
- package/dist/{chunk-Q3RL6A4E.js → chunk-BDE5RZVF.js} +2 -2
- package/dist/{chunk-DJDGDJDF.js → chunk-CYW4M6IV.js} +44 -23
- package/dist/{chunk-M243JMZH.js → chunk-DOB5RVMH.js} +2 -2
- package/dist/{chunk-EIVE6GL4.js → chunk-E5SRLZQV.js} +14 -29
- package/dist/{chunk-YEAOUT5W.js → chunk-GFTZKOTP.js} +1 -1
- package/dist/{chunk-XAI2HNAY.js → chunk-GN6WW7WR.js} +16 -31
- package/dist/{chunk-DMKHRBI6.js → chunk-ISQYBTPG.js} +3 -3
- package/dist/{chunk-DWOH57FW.js → chunk-L2KJ2GNL.js} +1 -1
- package/dist/{chunk-ERYVNWNE.js → chunk-NIJG6G6X.js} +621 -735
- package/dist/{chunk-JQWP6V22.js → chunk-NNGYYGX5.js} +2061 -2015
- package/dist/{chunk-GNNV3UTY.js → chunk-OKUZIXQM.js} +1 -1
- package/dist/{chunk-HDFGNU5E.js → chunk-S7G34U75.js} +3 -1
- package/dist/{chunk-ALAKN7LR.js → chunk-TZVZCIYL.js} +1 -1
- package/dist/{chunk-24B5R6OU.js → chunk-USKP7IM2.js} +49 -92
- package/dist/{chunk-BHXW734F.js → chunk-V6XPIKYA.js} +13 -19
- package/dist/{chunk-TKUCRFNX.js → chunk-VCC7XFX2.js} +22 -45
- package/dist/cli/chat.js +22 -22
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +11 -11
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +6 -6
- package/dist/{db-T4ZY64PB.js → db-E6O2DXUQ.js} +6 -6
- package/dist/instrumentation.js +3 -3
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-LQ6DFI26.js → oauth-callback-server-6SCPUFFD.js} +15 -15
- package/dist/{runner-D3ZU6TJH.js → runner-JFKCMVVY.js} +62 -51
- package/dist/{skills-JMXY7LIY.js → skills-RD66F2OB.js} +6 -6
- package/dist/{validation-OEPJS7JX.js → validation-XVRNX6IU.js} +6 -6
- package/dist/version.js +1 -1
- package/package.json +8 -8
|
@@ -30,15 +30,18 @@ import {
|
|
|
30
30
|
toMcpProviderError,
|
|
31
31
|
toolInvocationDestination,
|
|
32
32
|
uploadFilesToConversation
|
|
33
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
104
|
+
} from "./chunk-CYW4M6IV.js";
|
|
100
105
|
import {
|
|
101
106
|
incrementStat
|
|
102
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
190
|
+
} from "./chunk-S7G34U75.js";
|
|
186
191
|
import {
|
|
187
192
|
discoverSkills,
|
|
188
193
|
findSkillByName,
|
|
189
194
|
loadSkillsByName,
|
|
190
195
|
parseSkillInvocation
|
|
191
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-E5SRLZQV.js";
|
|
192
197
|
import {
|
|
193
198
|
pluginCatalogRuntime
|
|
194
|
-
} from "./chunk-
|
|
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-
|
|
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-
|
|
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 {
|
|
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 =
|
|
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) =>
|
|
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
|
-
"
|
|
818
|
-
|
|
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
|
-
"
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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
|
|
736
|
+
const suppressedPrimaryText = Boolean(
|
|
848
737
|
rawPrimaryText && !noReplyRequested && !primaryText
|
|
849
738
|
);
|
|
850
|
-
const resolvedOutcome =
|
|
739
|
+
const resolvedOutcome = suppressedPrimaryText ? "execution_failure" : outcome;
|
|
851
740
|
if (shouldTrace) {
|
|
852
|
-
logInfo(
|
|
853
|
-
"
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
"
|
|
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(
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
-
"-
|
|
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
|
-
"-
|
|
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
|
-
"
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
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
|
-
"
|
|
5371
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
8938
|
-
logWarn(
|
|
8939
|
-
"
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
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
|
-
"
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
11305
|
-
"
|
|
11306
|
-
|
|
11307
|
-
|
|
11308
|
-
|
|
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,
|
|
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
|
-
"
|
|
12050
|
-
|
|
12051
|
-
|
|
12052
|
-
|
|
12053
|
-
|
|
12054
|
-
|
|
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
|
-
"
|
|
12066
|
-
|
|
12067
|
-
{
|
|
12068
|
-
|
|
12069
|
-
|
|
12070
|
-
|
|
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
|
-
|
|
12081
|
-
"
|
|
12082
|
-
{},
|
|
12083
|
-
|
|
12084
|
-
|
|
12085
|
-
|
|
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
|
-
"
|
|
12461
|
-
|
|
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
|
-
"
|
|
12528
|
-
|
|
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
|
-
|
|
13123
|
-
|
|
13124
|
-
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
|
|
13129
|
-
|
|
13130
|
-
|
|
13131
|
-
|
|
13132
|
-
|
|
13133
|
-
|
|
13134
|
-
|
|
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
|
-
|
|
13339
|
-
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
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 ? "
|
|
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
|
-
|
|
13418
|
-
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
13540
|
-
{
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
|
|
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
|
|
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
|
-
"
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
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
|
|
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
|
-
"
|
|
13893
|
-
|
|
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
|
-
|
|
13928
|
-
|
|
13929
|
-
|
|
13930
|
-
|
|
13931
|
-
|
|
13932
|
-
|
|
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.
|
|
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
|
-
"
|
|
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
|
-
"
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
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
|
-
"
|
|
14801
|
-
|
|
14802
|
-
|
|
14803
|
-
|
|
14804
|
-
|
|
14805
|
-
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
|
|
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
|
-
|
|
14867
|
-
|
|
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
|
-
"
|
|
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
|
|
15055
|
-
profiles
|
|
15056
|
-
|
|
15057
|
-
|
|
15058
|
-
|
|
15059
|
-
|
|
15060
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
15212
|
-
if (
|
|
15098
|
+
const decision = decideReply(message);
|
|
15099
|
+
if (decision.kind !== "deliver" || !delivery) {
|
|
15213
15100
|
return;
|
|
15214
15101
|
}
|
|
15215
15102
|
try {
|
|
15216
|
-
await delivery
|
|
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
|
-
"
|
|
15257
|
-
|
|
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
|
-
"
|
|
15270
|
-
|
|
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({
|
|
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
|
-
"
|
|
15429
|
-
|
|
15430
|
-
|
|
15431
|
-
|
|
15432
|
-
"gen_ai.
|
|
15433
|
-
|
|
15434
|
-
|
|
15435
|
-
|
|
15436
|
-
|
|
15437
|
-
|
|
15438
|
-
|
|
15439
|
-
|
|
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
|
-
"
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
15573
|
-
|
|
15574
|
-
|
|
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",
|