@sentry/junior 0.15.2 → 0.17.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/bin/junior.mjs +0 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +760 -725
- package/dist/{chunk-KTBQH6L5.js → chunk-4XWTSMRF.js} +1 -1
- package/dist/{chunk-5JHLDXBN.js → chunk-DTOS5CG4.js} +13 -4
- package/dist/{chunk-ESPIOJPM.js → chunk-XYOKYK6U.js} +1 -1
- package/dist/cli/check.js +2 -2
- package/dist/cli/init.js +1 -1
- package/dist/cli/snapshot-warmup.js +2 -2
- package/dist/nitro.js +90 -65
- package/package.json +7 -2
package/dist/app.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
logCapabilityCatalogLoadedOnce,
|
|
8
8
|
parseSkillInvocation,
|
|
9
9
|
stripFrontmatter
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-4XWTSMRF.js";
|
|
11
11
|
import {
|
|
12
12
|
SANDBOX_SKILLS_ROOT,
|
|
13
13
|
SANDBOX_WORKSPACE_ROOT,
|
|
@@ -26,13 +26,14 @@ import {
|
|
|
26
26
|
runNonInteractiveCommand,
|
|
27
27
|
sandboxSkillDir,
|
|
28
28
|
toOptionalTrimmed
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-XYOKYK6U.js";
|
|
30
30
|
import {
|
|
31
31
|
CredentialUnavailableError,
|
|
32
32
|
buildOAuthTokenRequest,
|
|
33
33
|
createPluginBroker,
|
|
34
34
|
createRequestContext,
|
|
35
35
|
extractGenAiUsageAttributes,
|
|
36
|
+
getActiveTraceId,
|
|
36
37
|
getPluginDefinition,
|
|
37
38
|
getPluginMcpProviders,
|
|
38
39
|
getPluginOAuthConfig,
|
|
@@ -54,7 +55,7 @@ import {
|
|
|
54
55
|
toOptionalString,
|
|
55
56
|
withContext,
|
|
56
57
|
withSpan
|
|
57
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-DTOS5CG4.js";
|
|
58
59
|
import "./chunk-Z3YD6NHK.js";
|
|
59
60
|
import {
|
|
60
61
|
aboutPathCandidates,
|
|
@@ -224,8 +225,8 @@ async function GET3() {
|
|
|
224
225
|
html += `
|
|
225
226
|
</div>`;
|
|
226
227
|
const endpoints = [
|
|
227
|
-
{ method: "GET", path: "/
|
|
228
|
-
{ method: "GET", path: "/api/
|
|
228
|
+
{ method: "GET", path: "/health" },
|
|
229
|
+
{ method: "GET", path: "/api/info" },
|
|
229
230
|
{ method: "GET", path: "/api/oauth/callback/mcp/:provider" },
|
|
230
231
|
{ method: "GET", path: "/api/oauth/callback/:provider" },
|
|
231
232
|
{ method: "POST", path: "/api/webhooks/:platform" }
|
|
@@ -1157,21 +1158,44 @@ function summarizeMessageText(text) {
|
|
|
1157
1158
|
}
|
|
1158
1159
|
return normalized.length > 1200 ? `${normalized.slice(0, 1200)}...` : normalized;
|
|
1159
1160
|
}
|
|
1160
|
-
function buildUserTurnText(userInput, conversationContext) {
|
|
1161
|
+
function buildUserTurnText(userInput, conversationContext, metadata) {
|
|
1161
1162
|
const trimmedContext = conversationContext?.trim();
|
|
1162
|
-
|
|
1163
|
+
const hasSessionContext = Boolean(metadata?.sessionContext?.conversationId);
|
|
1164
|
+
const hasTurnContext = Boolean(metadata?.turnContext?.traceId);
|
|
1165
|
+
if (!trimmedContext && !hasSessionContext && !hasTurnContext) {
|
|
1163
1166
|
return userInput;
|
|
1164
1167
|
}
|
|
1165
|
-
|
|
1168
|
+
const sections = [
|
|
1166
1169
|
"<current-message>",
|
|
1167
1170
|
userInput,
|
|
1168
|
-
"</current-message>"
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1171
|
+
"</current-message>"
|
|
1172
|
+
];
|
|
1173
|
+
if (trimmedContext) {
|
|
1174
|
+
sections.push(
|
|
1175
|
+
"",
|
|
1176
|
+
"<thread-conversation-context>",
|
|
1177
|
+
"Use this context for continuity across prior thread turns.",
|
|
1178
|
+
trimmedContext,
|
|
1179
|
+
"</thread-conversation-context>"
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
if (metadata?.sessionContext?.conversationId) {
|
|
1183
|
+
sections.push(
|
|
1184
|
+
"",
|
|
1185
|
+
"<session-context>",
|
|
1186
|
+
`- gen_ai.conversation.id: ${metadata.sessionContext.conversationId}`,
|
|
1187
|
+
"</session-context>"
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
if (metadata?.turnContext?.traceId) {
|
|
1191
|
+
sections.push(
|
|
1192
|
+
"",
|
|
1193
|
+
"<turn-context>",
|
|
1194
|
+
`- trace_id: ${metadata.turnContext.traceId}`,
|
|
1195
|
+
"</turn-context>"
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
return sections.join("\n");
|
|
1175
1199
|
}
|
|
1176
1200
|
function encodeNonImageAttachmentForPrompt(attachment) {
|
|
1177
1201
|
const base64 = attachment.data.toString("base64");
|
|
@@ -1913,9 +1937,7 @@ function createChannelConfigurationService(storage) {
|
|
|
1913
1937
|
};
|
|
1914
1938
|
const resolveValues = async (options = {}) => {
|
|
1915
1939
|
const keys = Array.isArray(options.keys) ? options.keys.map((entry) => entry.trim()).filter((entry) => entry.length > 0) : void 0;
|
|
1916
|
-
const entries = await list({
|
|
1917
|
-
...options.prefix ? { prefix: options.prefix } : {}
|
|
1918
|
-
});
|
|
1940
|
+
const entries = options.prefix ? await list({ prefix: options.prefix }) : await list({});
|
|
1919
1941
|
const filtered = keys ? entries.filter((entry) => keys.includes(entry.key)) : entries;
|
|
1920
1942
|
const resolved = {};
|
|
1921
1943
|
for (const entry of filtered) {
|
|
@@ -2152,14 +2174,15 @@ function resolveGatewayModel(modelId) {
|
|
|
2152
2174
|
return matched;
|
|
2153
2175
|
}
|
|
2154
2176
|
async function completeText(params) {
|
|
2155
|
-
const startedAt = Date.now();
|
|
2156
2177
|
const model = resolveGatewayModel(params.modelId);
|
|
2157
2178
|
const apiKey = getPiGatewayApiKeyOverride();
|
|
2158
2179
|
const requestMessagesAttribute = serializeGenAiAttribute(params.messages);
|
|
2180
|
+
const systemInstructionsAttribute = params.system ? serializeGenAiAttribute([{ type: "text", content: params.system }]) : void 0;
|
|
2159
2181
|
const startAttributes = {
|
|
2160
2182
|
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
2161
2183
|
"gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
|
|
2162
2184
|
"gen_ai.request.model": params.modelId,
|
|
2185
|
+
...systemInstructionsAttribute ? { "gen_ai.system_instructions": systemInstructionsAttribute } : {},
|
|
2163
2186
|
...requestMessagesAttribute ? { "gen_ai.input.messages": requestMessagesAttribute } : {},
|
|
2164
2187
|
"app.ai.auth_mode": apiKey ? "oidc" : "api_key"
|
|
2165
2188
|
};
|
|
@@ -2192,8 +2215,7 @@ async function completeText(params) {
|
|
|
2192
2215
|
"gen_ai.request.model": params.modelId,
|
|
2193
2216
|
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
2194
2217
|
...usageAttributes,
|
|
2195
|
-
"
|
|
2196
|
-
"app.ai.stop_reason": message.stopReason ?? "unknown"
|
|
2218
|
+
...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {}
|
|
2197
2219
|
};
|
|
2198
2220
|
setSpanAttributes(endAttributes);
|
|
2199
2221
|
if (message.stopReason === "error") {
|
|
@@ -2783,24 +2805,6 @@ function formatLoadedSkillsForPrompt(skills) {
|
|
|
2783
2805
|
lines.push("</loaded_skills>");
|
|
2784
2806
|
return lines.join("\n");
|
|
2785
2807
|
}
|
|
2786
|
-
function formatLoadedToolsForPrompt(tools) {
|
|
2787
|
-
if (tools.length === 0) {
|
|
2788
|
-
return "<loaded_tools>\n</loaded_tools>";
|
|
2789
|
-
}
|
|
2790
|
-
const lines = ["<loaded_tools>"];
|
|
2791
|
-
for (const tool2 of tools) {
|
|
2792
|
-
lines.push(
|
|
2793
|
-
` <tool name="${escapeXml(tool2.tool_name)}" provider="${escapeXml(tool2.provider)}">`
|
|
2794
|
-
);
|
|
2795
|
-
lines.push(` <description>${escapeXml(tool2.description)}</description>`);
|
|
2796
|
-
lines.push(
|
|
2797
|
-
` <arguments_summary>${escapeXml(tool2.input_schema_summary)}</arguments_summary>`
|
|
2798
|
-
);
|
|
2799
|
-
lines.push(" </tool>");
|
|
2800
|
-
}
|
|
2801
|
-
lines.push("</loaded_tools>");
|
|
2802
|
-
return lines.join("\n");
|
|
2803
|
-
}
|
|
2804
2808
|
function formatProviderCatalogForPrompt() {
|
|
2805
2809
|
const providers = listCapabilityProviders();
|
|
2806
2810
|
if (providers.length === 0) {
|
|
@@ -2834,7 +2838,7 @@ function baseSystemPrompt() {
|
|
|
2834
2838
|
"- Never claim you cannot access tools in this turn. If prior results are empty, run tools now.",
|
|
2835
2839
|
"- If critical input is missing and cannot be discovered with tools, ask one direct clarifying question.",
|
|
2836
2840
|
"- Always gather evidence from available sources (tools or skills) before answering factual questions.",
|
|
2837
|
-
"- When a loaded skill exposes MCP capabilities,
|
|
2841
|
+
"- When a loaded skill exposes MCP capabilities, those tools are registered as callable tools. Call them directly by name.",
|
|
2838
2842
|
"- Use `searchTools` only when you need to rediscover or filter active MCP tools.",
|
|
2839
2843
|
"- Never guess. If you cannot verify with available sources, say it is unverified.",
|
|
2840
2844
|
"- Never claim a lookup succeeded unless a tool result supports it.",
|
|
@@ -2875,10 +2879,8 @@ function buildSystemPrompt(params) {
|
|
|
2875
2879
|
"Loaded skills for this turn:",
|
|
2876
2880
|
formatLoadedSkillsForPrompt(activeSkills)
|
|
2877
2881
|
].join("\n");
|
|
2878
|
-
const
|
|
2879
|
-
|
|
2880
|
-
formatLoadedToolsForPrompt(activeTools ?? [])
|
|
2881
|
-
].join("\n");
|
|
2882
|
+
const activeToolNames = (activeTools ?? []).map((tool2) => tool2.tool_name);
|
|
2883
|
+
const activeToolsSection = activeToolNames.length > 0 ? `Active MCP tools registered for this turn: ${activeToolNames.join(", ")}. Call them directly by name.` : "";
|
|
2882
2884
|
const configurationKeys = Object.keys(configuration ?? {}).sort(
|
|
2883
2885
|
(a, b) => a.localeCompare(b)
|
|
2884
2886
|
);
|
|
@@ -3004,9 +3006,9 @@ function buildSystemPrompt(params) {
|
|
|
3004
3006
|
"- Do not use reaction-based progress signals; Assistants API status already covers in-progress UX.",
|
|
3005
3007
|
"- Prefer `webSearch` before `webFetch` when the user gave no URL.",
|
|
3006
3008
|
"- Never call side-effecting tools when the user only asked for analysis or options.",
|
|
3007
|
-
"- `loadSkill`
|
|
3009
|
+
"- `loadSkill` activates MCP tools when the loaded skill exposes them. After loading, call them directly by name (for example `mcp__provider__tool_name`).",
|
|
3008
3010
|
"- `searchTools` searches active MCP tools exposed by currently loaded skills when you need to rediscover or filter them.",
|
|
3009
|
-
"-
|
|
3011
|
+
"- When the user asks for their conversation ID, trace ID, or a reference for Sentry lookup, use the IDs from `<session-context>` and `<turn-context>` in the user turn."
|
|
3010
3012
|
].join("\n")
|
|
3011
3013
|
),
|
|
3012
3014
|
renderTag(
|
|
@@ -3021,7 +3023,7 @@ function buildSystemPrompt(params) {
|
|
|
3021
3023
|
"- Never apply skill-specific behavior unless the skill is present in <loaded_skills> or `loadSkill` succeeded in this turn.",
|
|
3022
3024
|
"- Load only the best matching skill first; do not load multiple skills upfront.",
|
|
3023
3025
|
"- After `loadSkill`, use `skill_dir` as the root for any referenced files you read via `bash`.",
|
|
3024
|
-
"- If a loaded skill exposes MCP tools,
|
|
3026
|
+
"- If a loaded skill exposes MCP tools, they are registered as callable tools after `loadSkill` returns. Call them directly by name.",
|
|
3025
3027
|
"- Use `searchTools` only when you need to rediscover or filter the currently exposed MCP tools.",
|
|
3026
3028
|
"- If no skill is a clear fit, continue with normal tool usage."
|
|
3027
3029
|
].join("\n")
|
|
@@ -3042,7 +3044,7 @@ function buildSystemPrompt(params) {
|
|
|
3042
3044
|
),
|
|
3043
3045
|
availableSkillsSection,
|
|
3044
3046
|
activeSkillsSection,
|
|
3045
|
-
activeToolsSection,
|
|
3047
|
+
...activeToolsSection ? [activeToolsSection] : [],
|
|
3046
3048
|
renderTag(
|
|
3047
3049
|
"invocation-context",
|
|
3048
3050
|
invocation ? `Explicit skill trigger detected: /${invocation.skillName}` : "No explicit skill trigger detected."
|
|
@@ -3735,9 +3737,7 @@ ${usage}
|
|
|
3735
3737
|
exitCode: 2
|
|
3736
3738
|
});
|
|
3737
3739
|
}
|
|
3738
|
-
const entries = await configuration.list({
|
|
3739
|
-
...prefixResult.prefix ? { prefix: prefixResult.prefix } : {}
|
|
3740
|
-
});
|
|
3740
|
+
const entries = prefixResult.prefix ? await configuration.list({ prefix: prefixResult.prefix }) : await configuration.list({});
|
|
3741
3741
|
return commandResult({
|
|
3742
3742
|
stdout: {
|
|
3743
3743
|
ok: true,
|
|
@@ -3927,65 +3927,6 @@ async function maybeExecuteJrRpcCustomCommand(command, deps) {
|
|
|
3927
3927
|
};
|
|
3928
3928
|
}
|
|
3929
3929
|
|
|
3930
|
-
// src/chat/services/channel-intent.ts
|
|
3931
|
-
function isExplicitChannelPostIntent(text) {
|
|
3932
|
-
if (!/\bchannel\b/i.test(text)) {
|
|
3933
|
-
return false;
|
|
3934
|
-
}
|
|
3935
|
-
const directChannelVerb = /\b(show|post|send|share|say|announce|broadcast)\b[\s\S]{0,80}\b(?:the\s+)?channel\b/i;
|
|
3936
|
-
if (directChannelVerb.test(text)) {
|
|
3937
|
-
return true;
|
|
3938
|
-
}
|
|
3939
|
-
const scopedChannelVerb = /\b(post|send|share|say|announce|broadcast)\b[\s\S]{0,80}\b(?:in|to)\b[\s\S]{0,40}\b(?:the\s+)?channel\b/i;
|
|
3940
|
-
return scopedChannelVerb.test(text);
|
|
3941
|
-
}
|
|
3942
|
-
|
|
3943
|
-
// src/chat/services/reply-delivery-plan.ts
|
|
3944
|
-
var REACTION_ONLY_ACK_RE = /^(?::[a-z0-9_+-]+:|[\p{Extended_Pictographic}\uFE0F\u200D]+)$/u;
|
|
3945
|
-
var REDUNDANT_REACTION_ACK_TEXT = ["done", "got it", "ok", "okay"];
|
|
3946
|
-
var REACTION_ALIAS_PREFIX_RE = /^:[a-z0-9_+-]*$/i;
|
|
3947
|
-
function normalizeReactionAckText(text) {
|
|
3948
|
-
return text.trim().toLowerCase().replace(/[!.]+$/g, "");
|
|
3949
|
-
}
|
|
3950
|
-
function isRedundantReactionAckText(text) {
|
|
3951
|
-
const trimmed = text.trim();
|
|
3952
|
-
if (!trimmed) {
|
|
3953
|
-
return false;
|
|
3954
|
-
}
|
|
3955
|
-
if (REACTION_ONLY_ACK_RE.test(trimmed)) {
|
|
3956
|
-
return true;
|
|
3957
|
-
}
|
|
3958
|
-
const normalized = normalizeReactionAckText(text);
|
|
3959
|
-
return REDUNDANT_REACTION_ACK_TEXT.includes(
|
|
3960
|
-
normalized
|
|
3961
|
-
);
|
|
3962
|
-
}
|
|
3963
|
-
function isPotentialRedundantReactionAckText(text) {
|
|
3964
|
-
const trimmed = text.trim();
|
|
3965
|
-
if (!trimmed) {
|
|
3966
|
-
return true;
|
|
3967
|
-
}
|
|
3968
|
-
if (REACTION_ONLY_ACK_RE.test(trimmed) || REACTION_ALIAS_PREFIX_RE.test(trimmed)) {
|
|
3969
|
-
return true;
|
|
3970
|
-
}
|
|
3971
|
-
const normalized = normalizeReactionAckText(text);
|
|
3972
|
-
return REDUNDANT_REACTION_ACK_TEXT.some(
|
|
3973
|
-
(candidate) => candidate.startsWith(normalized)
|
|
3974
|
-
);
|
|
3975
|
-
}
|
|
3976
|
-
function buildReplyDeliveryPlan(args) {
|
|
3977
|
-
const mode = args.explicitChannelPostIntent && args.channelPostPerformed ? "channel_only" : "thread";
|
|
3978
|
-
let attachFiles = "none";
|
|
3979
|
-
if (args.hasFiles && mode === "thread") {
|
|
3980
|
-
attachFiles = args.streamingThreadReply ? "followup" : "inline";
|
|
3981
|
-
}
|
|
3982
|
-
return {
|
|
3983
|
-
mode,
|
|
3984
|
-
postThreadText: mode === "thread",
|
|
3985
|
-
attachFiles
|
|
3986
|
-
};
|
|
3987
|
-
}
|
|
3988
|
-
|
|
3989
3930
|
// src/chat/sandbox/skill-sandbox.ts
|
|
3990
3931
|
import fs2 from "fs/promises";
|
|
3991
3932
|
import path2 from "path";
|
|
@@ -4185,9 +4126,6 @@ var SkillSandbox = class {
|
|
|
4185
4126
|
}
|
|
4186
4127
|
};
|
|
4187
4128
|
|
|
4188
|
-
// src/chat/mcp/tool-manager.ts
|
|
4189
|
-
import { validateToolArguments } from "@mariozechner/pi-ai";
|
|
4190
|
-
|
|
4191
4129
|
// src/chat/mcp/client.ts
|
|
4192
4130
|
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
4193
4131
|
import {
|
|
@@ -4367,6 +4305,12 @@ var PluginMcpClient = class {
|
|
|
4367
4305
|
};
|
|
4368
4306
|
|
|
4369
4307
|
// src/chat/mcp/tool-manager.ts
|
|
4308
|
+
var McpToolError = class extends Error {
|
|
4309
|
+
constructor(message) {
|
|
4310
|
+
super(message);
|
|
4311
|
+
this.name = "McpToolError";
|
|
4312
|
+
}
|
|
4313
|
+
};
|
|
4370
4314
|
function normalizeMcpToolName(provider, toolName) {
|
|
4371
4315
|
return `mcp__${provider}__${toolName}`;
|
|
4372
4316
|
}
|
|
@@ -4550,13 +4494,6 @@ var McpToolManager = class {
|
|
|
4550
4494
|
return left.tool.name.localeCompare(right.tool.name);
|
|
4551
4495
|
}).slice(0, Math.max(1, options.limit ?? 8)).map((entry) => this.toToolDescriptor(entry.tool));
|
|
4552
4496
|
}
|
|
4553
|
-
async executeTool(skills, canonicalToolName, args) {
|
|
4554
|
-
const tool2 = this.resolveActiveTool(skills, canonicalToolName);
|
|
4555
|
-
if (!tool2) {
|
|
4556
|
-
throw new Error(`Unknown active MCP tool: ${canonicalToolName}`);
|
|
4557
|
-
}
|
|
4558
|
-
return await tool2.execute(this.validateExecutionArgs(tool2, args));
|
|
4559
|
-
}
|
|
4560
4497
|
filterListedTools(plugin, tools) {
|
|
4561
4498
|
const allowedTools = plugin.manifest.mcp?.allowedTools;
|
|
4562
4499
|
if (!allowedTools || allowedTools.length === 0) {
|
|
@@ -4600,7 +4537,7 @@ var McpToolManager = class {
|
|
|
4600
4537
|
try {
|
|
4601
4538
|
const result = await client2.callTool(tool2.name, resolvedArgs);
|
|
4602
4539
|
if ("isError" in result && result.isError) {
|
|
4603
|
-
throw new
|
|
4540
|
+
throw new McpToolError(extractMcpErrorMessage(result));
|
|
4604
4541
|
}
|
|
4605
4542
|
return {
|
|
4606
4543
|
content: toAgentToolContent(result),
|
|
@@ -4651,6 +4588,7 @@ var McpToolManager = class {
|
|
|
4651
4588
|
this.activeProviders.delete(provider);
|
|
4652
4589
|
return true;
|
|
4653
4590
|
}
|
|
4591
|
+
/** Return all active ManagedMcpTool objects for the given skill scope. */
|
|
4654
4592
|
getResolvedActiveTools(skills, options = {}) {
|
|
4655
4593
|
const resolved = [];
|
|
4656
4594
|
for (const provider of this.getActiveProviders()) {
|
|
@@ -4674,11 +4612,6 @@ var McpToolManager = class {
|
|
|
4674
4612
|
}
|
|
4675
4613
|
return providerTools;
|
|
4676
4614
|
}
|
|
4677
|
-
resolveActiveTool(skills, canonicalToolName) {
|
|
4678
|
-
return this.getResolvedActiveTools(skills).find(
|
|
4679
|
-
(tool2) => tool2.name === canonicalToolName
|
|
4680
|
-
);
|
|
4681
|
-
}
|
|
4682
4615
|
toToolDescriptor(tool2) {
|
|
4683
4616
|
return {
|
|
4684
4617
|
name: tool2.name,
|
|
@@ -4687,15 +4620,6 @@ var McpToolManager = class {
|
|
|
4687
4620
|
provider: tool2.provider
|
|
4688
4621
|
};
|
|
4689
4622
|
}
|
|
4690
|
-
validateExecutionArgs(tool2, args) {
|
|
4691
|
-
return validateToolArguments(
|
|
4692
|
-
tool2,
|
|
4693
|
-
{
|
|
4694
|
-
name: tool2.name,
|
|
4695
|
-
arguments: args
|
|
4696
|
-
}
|
|
4697
|
-
);
|
|
4698
|
-
}
|
|
4699
4623
|
scoreToolMatch(tool2, normalizedQuery, queryTokens) {
|
|
4700
4624
|
const exactCandidates = [tool2.name, tool2.rawName, tool2.title].filter((value) => Boolean(value)).map((value) => value.toLowerCase());
|
|
4701
4625
|
if (exactCandidates.includes(normalizedQuery)) {
|
|
@@ -5161,7 +5085,7 @@ var DEFAULT_LIMIT = 5;
|
|
|
5161
5085
|
var MAX_LIMIT = 20;
|
|
5162
5086
|
function createSearchToolsTool(mcpToolManager, getActiveSkills) {
|
|
5163
5087
|
return tool({
|
|
5164
|
-
description: "Search active MCP tools exposed by the currently loaded skills. Use when you need to rediscover or filter tools
|
|
5088
|
+
description: "Search active MCP tools exposed by the currently loaded skills. Use when you need to rediscover or filter active tools.",
|
|
5165
5089
|
inputSchema: Type6.Object(
|
|
5166
5090
|
{
|
|
5167
5091
|
query: Type6.String({
|
|
@@ -6237,45 +6161,8 @@ function createSystemTimeTool() {
|
|
|
6237
6161
|
});
|
|
6238
6162
|
}
|
|
6239
6163
|
|
|
6240
|
-
// src/chat/tools/skill/use-tool.ts
|
|
6241
|
-
import { Type as Type13 } from "@sinclair/typebox";
|
|
6242
|
-
function normalizeToolArguments(value) {
|
|
6243
|
-
return value ?? {};
|
|
6244
|
-
}
|
|
6245
|
-
function createUseToolTool(mcpToolManager, getActiveSkills) {
|
|
6246
|
-
return tool({
|
|
6247
|
-
description: "Execute an active MCP tool by canonical tool_name. Use tool_name values disclosed by `loadSkill`, `<loaded_tools>`, or `searchTools`.",
|
|
6248
|
-
inputSchema: Type13.Object(
|
|
6249
|
-
{
|
|
6250
|
-
tool_name: Type13.String({
|
|
6251
|
-
minLength: 1,
|
|
6252
|
-
description: "Canonical MCP tool name in the form mcp__<provider>__<tool>."
|
|
6253
|
-
}),
|
|
6254
|
-
arguments: Type13.Optional(
|
|
6255
|
-
Type13.Object(
|
|
6256
|
-
{},
|
|
6257
|
-
{
|
|
6258
|
-
additionalProperties: true,
|
|
6259
|
-
description: "Arguments for the selected MCP tool."
|
|
6260
|
-
}
|
|
6261
|
-
)
|
|
6262
|
-
)
|
|
6263
|
-
},
|
|
6264
|
-
{ additionalProperties: false }
|
|
6265
|
-
),
|
|
6266
|
-
execute: async ({ tool_name, arguments: rawArguments }) => {
|
|
6267
|
-
const activeSkills = getActiveSkills();
|
|
6268
|
-
return await mcpToolManager.executeTool(
|
|
6269
|
-
activeSkills,
|
|
6270
|
-
tool_name,
|
|
6271
|
-
normalizeToolArguments(rawArguments)
|
|
6272
|
-
);
|
|
6273
|
-
}
|
|
6274
|
-
});
|
|
6275
|
-
}
|
|
6276
|
-
|
|
6277
6164
|
// src/chat/tools/web/fetch-tool.ts
|
|
6278
|
-
import { Type as
|
|
6165
|
+
import { Type as Type13 } from "@sinclair/typebox";
|
|
6279
6166
|
|
|
6280
6167
|
// src/chat/tools/web/constants.ts
|
|
6281
6168
|
var USER_AGENT = "junior-bot/0.1";
|
|
@@ -6623,13 +6510,13 @@ function extractHttpStatusFromMessage(message) {
|
|
|
6623
6510
|
function createWebFetchTool(hooks) {
|
|
6624
6511
|
return tool({
|
|
6625
6512
|
description: "Fetch and extract readable content from a specific URL. Use when you need details from a known page or document. Do not use for discovery when search is the first step.",
|
|
6626
|
-
inputSchema:
|
|
6627
|
-
url:
|
|
6513
|
+
inputSchema: Type13.Object({
|
|
6514
|
+
url: Type13.String({
|
|
6628
6515
|
minLength: 1,
|
|
6629
6516
|
description: "HTTP(S) URL to fetch."
|
|
6630
6517
|
}),
|
|
6631
|
-
max_chars:
|
|
6632
|
-
|
|
6518
|
+
max_chars: Type13.Optional(
|
|
6519
|
+
Type13.Integer({
|
|
6633
6520
|
minimum: 500,
|
|
6634
6521
|
maximum: MAX_FETCH_CHARS,
|
|
6635
6522
|
description: "Optional maximum number of extracted characters to return."
|
|
@@ -6689,7 +6576,7 @@ function createWebFetchTool(hooks) {
|
|
|
6689
6576
|
// src/chat/tools/web/search.ts
|
|
6690
6577
|
import { generateText } from "ai";
|
|
6691
6578
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
6692
|
-
import { Type as
|
|
6579
|
+
import { Type as Type14 } from "@sinclair/typebox";
|
|
6693
6580
|
var SEARCH_TIMEOUT_MS = 1e4;
|
|
6694
6581
|
var MAX_RESULTS = 5;
|
|
6695
6582
|
var DEFAULT_SEARCH_MODEL = "xai/grok-4-fast-reasoning";
|
|
@@ -6740,14 +6627,14 @@ function isTimeoutSearchFailure(message) {
|
|
|
6740
6627
|
function createWebSearchTool() {
|
|
6741
6628
|
return tool({
|
|
6742
6629
|
description: "Search public web sources and return top snippets/URLs. Use when you need discovery or source candidates. Do not use when the user already provided a specific URL to inspect.",
|
|
6743
|
-
inputSchema:
|
|
6744
|
-
query:
|
|
6630
|
+
inputSchema: Type14.Object({
|
|
6631
|
+
query: Type14.String({
|
|
6745
6632
|
minLength: 1,
|
|
6746
6633
|
maxLength: 500,
|
|
6747
6634
|
description: "Search query."
|
|
6748
6635
|
}),
|
|
6749
|
-
max_results:
|
|
6750
|
-
|
|
6636
|
+
max_results: Type14.Optional(
|
|
6637
|
+
Type14.Integer({
|
|
6751
6638
|
minimum: 1,
|
|
6752
6639
|
maximum: MAX_RESULTS,
|
|
6753
6640
|
description: "Max results to return."
|
|
@@ -6808,17 +6695,17 @@ function createWebSearchTool() {
|
|
|
6808
6695
|
}
|
|
6809
6696
|
|
|
6810
6697
|
// src/chat/tools/sandbox/write-file.ts
|
|
6811
|
-
import { Type as
|
|
6698
|
+
import { Type as Type15 } from "@sinclair/typebox";
|
|
6812
6699
|
function createWriteFileTool() {
|
|
6813
6700
|
return tool({
|
|
6814
6701
|
description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or replacement after validation. Do not use for exploratory analysis-only turns.",
|
|
6815
|
-
inputSchema:
|
|
6702
|
+
inputSchema: Type15.Object(
|
|
6816
6703
|
{
|
|
6817
|
-
path:
|
|
6704
|
+
path: Type15.String({
|
|
6818
6705
|
minLength: 1,
|
|
6819
6706
|
description: "Path to write in the sandbox workspace."
|
|
6820
6707
|
}),
|
|
6821
|
-
content:
|
|
6708
|
+
content: Type15.String({
|
|
6822
6709
|
description: "UTF-8 file content to write."
|
|
6823
6710
|
})
|
|
6824
6711
|
},
|
|
@@ -6938,20 +6825,16 @@ function createTools(availableSkills, hooks = {}, context) {
|
|
|
6938
6825
|
createSearchToolsTool(context.mcpToolManager, context.getActiveSkills),
|
|
6939
6826
|
hooks
|
|
6940
6827
|
);
|
|
6941
|
-
tools.useTool = wrapToolExecution(
|
|
6942
|
-
"useTool",
|
|
6943
|
-
createUseToolTool(context.mcpToolManager, context.getActiveSkills),
|
|
6944
|
-
hooks
|
|
6945
|
-
);
|
|
6946
6828
|
}
|
|
6947
|
-
|
|
6829
|
+
const { channelCapabilities } = context;
|
|
6830
|
+
if (channelCapabilities.canCreateCanvas) {
|
|
6948
6831
|
tools.slackCanvasCreate = wrapToolExecution(
|
|
6949
6832
|
"slackCanvasCreate",
|
|
6950
6833
|
createSlackCanvasCreateTool(context, state),
|
|
6951
6834
|
hooks
|
|
6952
6835
|
);
|
|
6953
6836
|
}
|
|
6954
|
-
if (
|
|
6837
|
+
if (channelCapabilities.canPostToChannel) {
|
|
6955
6838
|
tools.slackChannelPostMessage = wrapToolExecution(
|
|
6956
6839
|
"slackChannelPostMessage",
|
|
6957
6840
|
createSlackChannelPostMessageTool(context, state),
|
|
@@ -6963,7 +6846,7 @@ function createTools(availableSkills, hooks = {}, context) {
|
|
|
6963
6846
|
hooks
|
|
6964
6847
|
);
|
|
6965
6848
|
}
|
|
6966
|
-
if (
|
|
6849
|
+
if (channelCapabilities.canAddReactions) {
|
|
6967
6850
|
tools.slackMessageAddReaction = wrapToolExecution(
|
|
6968
6851
|
"slackMessageAddReaction",
|
|
6969
6852
|
createSlackMessageAddReactionTool(context, state),
|
|
@@ -6973,6 +6856,15 @@ function createTools(availableSkills, hooks = {}, context) {
|
|
|
6973
6856
|
return tools;
|
|
6974
6857
|
}
|
|
6975
6858
|
|
|
6859
|
+
// src/chat/tools/channel-capabilities.ts
|
|
6860
|
+
function resolveChannelCapabilities(channelId) {
|
|
6861
|
+
return {
|
|
6862
|
+
canCreateCanvas: isConversationScopedChannel(channelId),
|
|
6863
|
+
canPostToChannel: isConversationChannel(channelId),
|
|
6864
|
+
canAddReactions: isConversationScopedChannel(channelId)
|
|
6865
|
+
};
|
|
6866
|
+
}
|
|
6867
|
+
|
|
6976
6868
|
// src/chat/sandbox/sandbox.ts
|
|
6977
6869
|
import fs3 from "fs/promises";
|
|
6978
6870
|
import path4 from "path";
|
|
@@ -7914,6 +7806,8 @@ function createSandboxExecutor(options) {
|
|
|
7914
7806
|
pathPrefix: `${SANDBOX_RUNTIME_BIN_DIR}:$PATH`
|
|
7915
7807
|
});
|
|
7916
7808
|
let commandError;
|
|
7809
|
+
let result;
|
|
7810
|
+
let restoreError;
|
|
7917
7811
|
try {
|
|
7918
7812
|
const commandResult2 = await activeSandbox.runCommand({
|
|
7919
7813
|
cmd: "bash",
|
|
@@ -7929,7 +7823,7 @@ function createSandboxExecutor(options) {
|
|
|
7929
7823
|
const stderrRaw = await commandResult2.stderr();
|
|
7930
7824
|
const stdout = truncateOutput(stdoutRaw, boundedOutputLength);
|
|
7931
7825
|
const stderr = truncateOutput(stderrRaw, boundedOutputLength);
|
|
7932
|
-
|
|
7826
|
+
result = {
|
|
7933
7827
|
stdout: stdout.value,
|
|
7934
7828
|
stderr: stderr.value,
|
|
7935
7829
|
exitCode: commandResult2.exitCode,
|
|
@@ -7943,14 +7837,16 @@ function createSandboxExecutor(options) {
|
|
|
7943
7837
|
if (headerTransforms && headerTransforms.length > 0) {
|
|
7944
7838
|
try {
|
|
7945
7839
|
await activeSandbox.updateNetworkPolicy(restoreNetworkPolicy);
|
|
7946
|
-
} catch (
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
throw restoreError;
|
|
7950
|
-
}
|
|
7840
|
+
} catch (error) {
|
|
7841
|
+
restoreError = error;
|
|
7842
|
+
await invalidateSandboxInstance(activeSandbox, error);
|
|
7951
7843
|
}
|
|
7952
7844
|
}
|
|
7953
7845
|
}
|
|
7846
|
+
if (restoreError && !commandError) {
|
|
7847
|
+
throw restoreError;
|
|
7848
|
+
}
|
|
7849
|
+
return result;
|
|
7954
7850
|
},
|
|
7955
7851
|
readFile: async (input) => await executeReadFile(input, {
|
|
7956
7852
|
toolCallId: "sandbox-read-file",
|
|
@@ -8173,95 +8069,6 @@ function shouldEmitDevAgentTrace() {
|
|
|
8173
8069
|
return process.env.NODE_ENV === "development";
|
|
8174
8070
|
}
|
|
8175
8071
|
|
|
8176
|
-
// src/chat/state/turn-session-store.ts
|
|
8177
|
-
var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
|
|
8178
|
-
var AGENT_TURN_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8179
|
-
function agentTurnSessionKey(conversationId, sessionId) {
|
|
8180
|
-
return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
|
|
8181
|
-
}
|
|
8182
|
-
function parseAgentTurnSessionCheckpoint(value) {
|
|
8183
|
-
if (typeof value !== "string") {
|
|
8184
|
-
return void 0;
|
|
8185
|
-
}
|
|
8186
|
-
try {
|
|
8187
|
-
const parsed = JSON.parse(value);
|
|
8188
|
-
if (!isRecord(parsed)) {
|
|
8189
|
-
return void 0;
|
|
8190
|
-
}
|
|
8191
|
-
const status = parsed.state;
|
|
8192
|
-
if (status !== "running" && status !== "awaiting_resume" && status !== "completed" && status !== "failed") {
|
|
8193
|
-
return void 0;
|
|
8194
|
-
}
|
|
8195
|
-
const conversationId = parsed.conversationId;
|
|
8196
|
-
const sessionId = parsed.sessionId;
|
|
8197
|
-
const sliceId = parsed.sliceId;
|
|
8198
|
-
const checkpointVersion = parsed.checkpointVersion;
|
|
8199
|
-
const updatedAtMs = parsed.updatedAtMs;
|
|
8200
|
-
if (typeof conversationId !== "string" || typeof sessionId !== "string" || typeof sliceId !== "number" || typeof checkpointVersion !== "number" || typeof updatedAtMs !== "number") {
|
|
8201
|
-
return void 0;
|
|
8202
|
-
}
|
|
8203
|
-
return {
|
|
8204
|
-
checkpointVersion,
|
|
8205
|
-
conversationId,
|
|
8206
|
-
sessionId,
|
|
8207
|
-
sliceId,
|
|
8208
|
-
state: status,
|
|
8209
|
-
updatedAtMs,
|
|
8210
|
-
piMessages: Array.isArray(parsed.piMessages) ? parsed.piMessages : [],
|
|
8211
|
-
...Array.isArray(parsed.loadedSkillNames) ? {
|
|
8212
|
-
loadedSkillNames: parsed.loadedSkillNames.filter(
|
|
8213
|
-
(value2) => typeof value2 === "string"
|
|
8214
|
-
)
|
|
8215
|
-
} : {},
|
|
8216
|
-
...parsed.resumeReason === "timeout" || parsed.resumeReason === "auth" ? { resumeReason: parsed.resumeReason } : {},
|
|
8217
|
-
...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
|
|
8218
|
-
...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {}
|
|
8219
|
-
};
|
|
8220
|
-
} catch {
|
|
8221
|
-
return void 0;
|
|
8222
|
-
}
|
|
8223
|
-
}
|
|
8224
|
-
async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
|
|
8225
|
-
const stateAdapter = getStateAdapter();
|
|
8226
|
-
await stateAdapter.connect();
|
|
8227
|
-
const value = await stateAdapter.get(
|
|
8228
|
-
agentTurnSessionKey(conversationId, sessionId)
|
|
8229
|
-
);
|
|
8230
|
-
return parseAgentTurnSessionCheckpoint(value);
|
|
8231
|
-
}
|
|
8232
|
-
async function upsertAgentTurnSessionCheckpoint(args) {
|
|
8233
|
-
const stateAdapter = getStateAdapter();
|
|
8234
|
-
await stateAdapter.connect();
|
|
8235
|
-
const existing = await getAgentTurnSessionCheckpoint(
|
|
8236
|
-
args.conversationId,
|
|
8237
|
-
args.sessionId
|
|
8238
|
-
);
|
|
8239
|
-
const checkpoint = {
|
|
8240
|
-
checkpointVersion: (existing?.checkpointVersion ?? 0) + 1,
|
|
8241
|
-
conversationId: args.conversationId,
|
|
8242
|
-
sessionId: args.sessionId,
|
|
8243
|
-
sliceId: args.sliceId,
|
|
8244
|
-
state: args.state,
|
|
8245
|
-
updatedAtMs: Date.now(),
|
|
8246
|
-
piMessages: Array.isArray(args.piMessages) ? args.piMessages : [],
|
|
8247
|
-
...Array.isArray(args.loadedSkillNames) ? {
|
|
8248
|
-
loadedSkillNames: args.loadedSkillNames.filter(
|
|
8249
|
-
(value) => typeof value === "string"
|
|
8250
|
-
)
|
|
8251
|
-
} : {},
|
|
8252
|
-
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
8253
|
-
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
8254
|
-
...typeof args.resumedFromSliceId === "number" ? { resumedFromSliceId: args.resumedFromSliceId } : {}
|
|
8255
|
-
};
|
|
8256
|
-
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
8257
|
-
await stateAdapter.set(
|
|
8258
|
-
agentTurnSessionKey(args.conversationId, args.sessionId),
|
|
8259
|
-
JSON.stringify(checkpoint),
|
|
8260
|
-
ttlMs
|
|
8261
|
-
);
|
|
8262
|
-
return checkpoint;
|
|
8263
|
-
}
|
|
8264
|
-
|
|
8265
8072
|
// src/chat/runtime/status-format.ts
|
|
8266
8073
|
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
8267
8074
|
function truncateWithEllipsis(text, maxLength) {
|
|
@@ -8408,20 +8215,6 @@ function extractStatusUrlDomain(value) {
|
|
|
8408
8215
|
}
|
|
8409
8216
|
|
|
8410
8217
|
// src/chat/runtime/tool-status.ts
|
|
8411
|
-
function formatCanonicalToolStatusName(value) {
|
|
8412
|
-
if (typeof value !== "string") {
|
|
8413
|
-
return void 0;
|
|
8414
|
-
}
|
|
8415
|
-
const trimmed = value.trim();
|
|
8416
|
-
if (!trimmed) {
|
|
8417
|
-
return void 0;
|
|
8418
|
-
}
|
|
8419
|
-
const mcpMatch = /^mcp__([^_]+)__(.+)$/.exec(trimmed);
|
|
8420
|
-
if (mcpMatch) {
|
|
8421
|
-
return compactStatusText(`${mcpMatch[1]}/${mcpMatch[2]}`, 40);
|
|
8422
|
-
}
|
|
8423
|
-
return compactStatusText(trimmed, 40);
|
|
8424
|
-
}
|
|
8425
8218
|
function formatToolStatus(toolName) {
|
|
8426
8219
|
const known = {
|
|
8427
8220
|
loadSkill: "Loading skill instructions",
|
|
@@ -8440,12 +8233,15 @@ function formatToolStatus(toolName) {
|
|
|
8440
8233
|
slackListAddItems: "Updating tracking list",
|
|
8441
8234
|
slackListUpdateItem: "Updating tracking list",
|
|
8442
8235
|
imageGenerate: "Generating image",
|
|
8443
|
-
searchTools: "Searching active tools"
|
|
8444
|
-
useTool: "Running active tool"
|
|
8236
|
+
searchTools: "Searching active tools"
|
|
8445
8237
|
};
|
|
8446
8238
|
if (known[toolName]) {
|
|
8447
8239
|
return known[toolName];
|
|
8448
8240
|
}
|
|
8241
|
+
const mcpMatch = /^mcp__([^_]+)__(.+)$/.exec(toolName);
|
|
8242
|
+
if (mcpMatch) {
|
|
8243
|
+
return `Running ${mcpMatch[1]}/${mcpMatch[2]}`;
|
|
8244
|
+
}
|
|
8449
8245
|
const readable = toolName.replaceAll("_", " ").trim();
|
|
8450
8246
|
return readable.length > 0 ? `Running ${readable}` : "Running tool";
|
|
8451
8247
|
}
|
|
@@ -8458,7 +8254,6 @@ function formatToolStatusWithInput(toolName, input) {
|
|
|
8458
8254
|
const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
|
|
8459
8255
|
const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
|
|
8460
8256
|
const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
|
|
8461
|
-
const activeToolName = obj ? formatCanonicalToolStatusName(obj.tool_name ?? obj.toolName) : void 0;
|
|
8462
8257
|
if (command && toolName === "bash") {
|
|
8463
8258
|
return `Running ${command}`;
|
|
8464
8259
|
}
|
|
@@ -8483,41 +8278,93 @@ function formatToolStatusWithInput(toolName, input) {
|
|
|
8483
8278
|
if (query && toolName === "searchTools") {
|
|
8484
8279
|
return `Searching tools for "${query}"`;
|
|
8485
8280
|
}
|
|
8486
|
-
if (activeToolName && toolName === "useTool") {
|
|
8487
|
-
return `Running ${activeToolName}`;
|
|
8488
|
-
}
|
|
8489
8281
|
if (domain && toolName === "webFetch") {
|
|
8490
8282
|
return `Fetching page from ${domain}`;
|
|
8491
8283
|
}
|
|
8492
8284
|
return formatToolStatus(toolName);
|
|
8493
8285
|
}
|
|
8494
8286
|
|
|
8495
|
-
// src/chat/tools/
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8287
|
+
// src/chat/tools/execution/build-sandbox-input.ts
|
|
8288
|
+
function buildSandboxInput(toolName, params) {
|
|
8289
|
+
if (toolName === "bash") {
|
|
8290
|
+
return { command: String(params.command ?? "") };
|
|
8291
|
+
}
|
|
8292
|
+
if (toolName === "readFile") {
|
|
8293
|
+
return { path: String(params.path ?? "") };
|
|
8294
|
+
}
|
|
8295
|
+
if (toolName === "writeFile") {
|
|
8296
|
+
return {
|
|
8297
|
+
path: String(params.path ?? ""),
|
|
8298
|
+
content: String(params.content ?? "")
|
|
8299
|
+
};
|
|
8503
8300
|
}
|
|
8301
|
+
return params;
|
|
8504
8302
|
}
|
|
8505
|
-
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
|
|
8303
|
+
|
|
8304
|
+
// src/chat/tools/execution/inject-credentials.ts
|
|
8305
|
+
function resolveCredentialInjection(toolName, command, capabilityRuntime, sandbox) {
|
|
8306
|
+
if (toolName !== "bash" || !capabilityRuntime) {
|
|
8307
|
+
return {};
|
|
8308
|
+
}
|
|
8309
|
+
const headerTransforms = capabilityRuntime.getTurnHeaderTransforms();
|
|
8310
|
+
const env = capabilityRuntime.getTurnEnv();
|
|
8311
|
+
const isCustomCommand = /^jr-rpc(?:\s|$)/.test(command.trim());
|
|
8312
|
+
const shouldLog = !isCustomCommand && Boolean(headerTransforms && headerTransforms.length > 0);
|
|
8313
|
+
if (shouldLog) {
|
|
8314
|
+
const headerDomains = (headerTransforms ?? []).map(
|
|
8315
|
+
(transform) => transform.domain
|
|
8316
|
+
);
|
|
8317
|
+
logInfo(
|
|
8318
|
+
"credential_inject_start",
|
|
8319
|
+
{},
|
|
8320
|
+
{
|
|
8321
|
+
"app.skill.name": sandbox.getActiveSkill()?.name,
|
|
8322
|
+
"app.credential.delivery": "header_transform",
|
|
8323
|
+
"app.credential.header_domains": headerDomains
|
|
8324
|
+
},
|
|
8325
|
+
"Injecting scoped credential headers for sandbox command"
|
|
8326
|
+
);
|
|
8327
|
+
}
|
|
8328
|
+
return { headerTransforms, env };
|
|
8329
|
+
}
|
|
8330
|
+
|
|
8331
|
+
// src/chat/tools/execution/normalize-result.ts
|
|
8332
|
+
function isStructuredToolExecutionResult(value) {
|
|
8333
|
+
const content = value?.content;
|
|
8334
|
+
return typeof value === "object" && value !== null && Array.isArray(content) && content.every((part) => {
|
|
8335
|
+
if (!part || typeof part !== "object") {
|
|
8336
|
+
return false;
|
|
8337
|
+
}
|
|
8338
|
+
const record = part;
|
|
8339
|
+
if (record.type === "text") {
|
|
8340
|
+
return typeof record.text === "string";
|
|
8341
|
+
}
|
|
8515
8342
|
if (record.type === "image") {
|
|
8516
8343
|
return typeof record.data === "string" && typeof record.mimeType === "string";
|
|
8517
8344
|
}
|
|
8518
8345
|
return false;
|
|
8519
8346
|
}) && "details" in value;
|
|
8520
8347
|
}
|
|
8348
|
+
function toToolContentText(value) {
|
|
8349
|
+
if (typeof value === "string") return value;
|
|
8350
|
+
try {
|
|
8351
|
+
return JSON.stringify(value);
|
|
8352
|
+
} catch {
|
|
8353
|
+
return String(value);
|
|
8354
|
+
}
|
|
8355
|
+
}
|
|
8356
|
+
function normalizeToolResult(result, isSandboxResult) {
|
|
8357
|
+
const unwrapped = isSandboxResult && result && typeof result === "object" && "result" in result ? result.result : result;
|
|
8358
|
+
if (isStructuredToolExecutionResult(unwrapped)) {
|
|
8359
|
+
return unwrapped;
|
|
8360
|
+
}
|
|
8361
|
+
return {
|
|
8362
|
+
content: [{ type: "text", text: toToolContentText(unwrapped) }],
|
|
8363
|
+
details: unwrapped
|
|
8364
|
+
};
|
|
8365
|
+
}
|
|
8366
|
+
|
|
8367
|
+
// src/chat/tools/execution/tool-error-handler.ts
|
|
8521
8368
|
function getToolErrorAttributes(error) {
|
|
8522
8369
|
if (!(error instanceof SlackActionError)) {
|
|
8523
8370
|
return {};
|
|
@@ -8530,6 +8377,44 @@ function getToolErrorAttributes(error) {
|
|
|
8530
8377
|
...error.detailRule ? { "app.slack.detail_rule": error.detailRule } : {}
|
|
8531
8378
|
};
|
|
8532
8379
|
}
|
|
8380
|
+
function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, traceContext) {
|
|
8381
|
+
setSpanAttributes({
|
|
8382
|
+
"error.type": error instanceof Error ? error.name : "tool_execution_error"
|
|
8383
|
+
});
|
|
8384
|
+
if (shouldTrace) {
|
|
8385
|
+
logWarn(
|
|
8386
|
+
"agent_tool_call_failed",
|
|
8387
|
+
traceContext,
|
|
8388
|
+
{
|
|
8389
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8390
|
+
"gen_ai.operation.name": "execute_tool",
|
|
8391
|
+
"gen_ai.tool.name": toolName,
|
|
8392
|
+
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
8393
|
+
"error.type": error instanceof Error ? error.name : "tool_execution_error",
|
|
8394
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
8395
|
+
},
|
|
8396
|
+
"Agent tool call failed"
|
|
8397
|
+
);
|
|
8398
|
+
}
|
|
8399
|
+
if (!(error instanceof McpToolError)) {
|
|
8400
|
+
logException(
|
|
8401
|
+
error,
|
|
8402
|
+
"agent_tool_call_failed",
|
|
8403
|
+
{},
|
|
8404
|
+
{
|
|
8405
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8406
|
+
"gen_ai.operation.name": "execute_tool",
|
|
8407
|
+
"gen_ai.tool.name": toolName,
|
|
8408
|
+
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
8409
|
+
...getToolErrorAttributes(error)
|
|
8410
|
+
},
|
|
8411
|
+
"Agent tool call failed"
|
|
8412
|
+
);
|
|
8413
|
+
}
|
|
8414
|
+
throw error;
|
|
8415
|
+
}
|
|
8416
|
+
|
|
8417
|
+
// src/chat/tools/agent-tools.ts
|
|
8533
8418
|
function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, capabilityRuntime, hooks) {
|
|
8534
8419
|
const shouldTrace = shouldEmitDevAgentTrace();
|
|
8535
8420
|
return Object.entries(tools).map(([toolName, toolDef]) => ({
|
|
@@ -8541,7 +8426,6 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
8541
8426
|
const normalizedToolCallId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
|
|
8542
8427
|
const toolArgumentsAttribute = serializeGenAiAttribute(params);
|
|
8543
8428
|
hooks?.onToolCall?.(toolName);
|
|
8544
|
-
const toolStartedAt = Date.now();
|
|
8545
8429
|
const traceToolContext = {
|
|
8546
8430
|
...spanContext,
|
|
8547
8431
|
conversationId: spanContext.conversationId,
|
|
@@ -8554,163 +8438,65 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
8554
8438
|
"gen_ai.execute_tool",
|
|
8555
8439
|
spanContext,
|
|
8556
8440
|
async () => {
|
|
8557
|
-
if (!Value.Check(toolDef.inputSchema, params)) {
|
|
8558
|
-
const details = [...Value.Errors(toolDef.inputSchema, params)].slice(0, 3).map((entry) => `${entry.path || "/"}: ${entry.message}`).join("; ");
|
|
8559
|
-
const validationMessage = details.length > 0 ? details : "Invalid tool input";
|
|
8560
|
-
const durationMs = Date.now() - toolStartedAt;
|
|
8561
|
-
setSpanAttributes({
|
|
8562
|
-
"app.ai.tool_duration_ms": durationMs,
|
|
8563
|
-
"error.type": "tool_input_validation_error"
|
|
8564
|
-
});
|
|
8565
|
-
setSpanStatus("error");
|
|
8566
|
-
logWarn(
|
|
8567
|
-
"agent_tool_call_invalid_input",
|
|
8568
|
-
{},
|
|
8569
|
-
{
|
|
8570
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8571
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8572
|
-
"gen_ai.tool.name": toolName,
|
|
8573
|
-
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8574
|
-
"app.ai.tool_duration_ms": durationMs
|
|
8575
|
-
},
|
|
8576
|
-
"Agent tool call input validation failed"
|
|
8577
|
-
);
|
|
8578
|
-
logException(
|
|
8579
|
-
new Error(validationMessage),
|
|
8580
|
-
"agent_tool_call_invalid_input_exception",
|
|
8581
|
-
{},
|
|
8582
|
-
{
|
|
8583
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8584
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8585
|
-
"gen_ai.tool.name": toolName,
|
|
8586
|
-
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8587
|
-
"app.ai.tool_duration_ms": durationMs
|
|
8588
|
-
},
|
|
8589
|
-
"Agent tool call input validation failed with exception"
|
|
8590
|
-
);
|
|
8591
|
-
throw new Error(validationMessage);
|
|
8592
|
-
}
|
|
8593
8441
|
const parsed = params;
|
|
8594
8442
|
try {
|
|
8595
8443
|
if (typeof toolDef.execute !== "function") {
|
|
8596
|
-
const
|
|
8597
|
-
const
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
|
|
8601
|
-
|
|
8602
|
-
|
|
8603
|
-
});
|
|
8604
|
-
setSpanStatus("ok");
|
|
8444
|
+
const resultDetails = { ok: true };
|
|
8445
|
+
const toolResultAttribute2 = serializeGenAiAttribute(resultDetails);
|
|
8446
|
+
if (toolResultAttribute2) {
|
|
8447
|
+
setSpanAttributes({
|
|
8448
|
+
"gen_ai.tool.call.result": toolResultAttribute2
|
|
8449
|
+
});
|
|
8450
|
+
}
|
|
8605
8451
|
return {
|
|
8606
8452
|
content: [{ type: "text", text: "ok" }],
|
|
8607
|
-
details:
|
|
8453
|
+
details: resultDetails
|
|
8608
8454
|
};
|
|
8609
8455
|
}
|
|
8610
|
-
const injectedHeaders = toolName === "bash" ? capabilityRuntime?.getTurnHeaderTransforms() : void 0;
|
|
8611
|
-
const injectedEnv = toolName === "bash" ? capabilityRuntime?.getTurnEnv() : void 0;
|
|
8612
8456
|
const bashCommand = toolName === "bash" && typeof parsed.command === "string" ? parsed.command.trim() : "";
|
|
8613
|
-
const
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
|
|
8619
|
-
|
|
8620
|
-
|
|
8621
|
-
|
|
8622
|
-
{
|
|
8623
|
-
"app.skill.name": sandbox.getActiveSkill()?.name,
|
|
8624
|
-
"app.credential.delivery": "header_transform",
|
|
8625
|
-
"app.credential.header_domains": headerDomains
|
|
8626
|
-
},
|
|
8627
|
-
"Injecting scoped credential headers for sandbox command"
|
|
8628
|
-
);
|
|
8629
|
-
}
|
|
8630
|
-
const hasBashCredentials = injectedHeaders || injectedEnv;
|
|
8631
|
-
const sandboxInput = toolName === "bash" ? { command: String(parsed.command ?? "") } : toolName === "readFile" ? { path: String(parsed.path ?? "") } : toolName === "writeFile" ? {
|
|
8632
|
-
path: String(parsed.path ?? ""),
|
|
8633
|
-
content: String(parsed.content ?? "")
|
|
8634
|
-
} : parsed;
|
|
8635
|
-
const result = sandboxExecutor?.canExecute(toolName) ? await sandboxExecutor.execute({
|
|
8457
|
+
const injection = resolveCredentialInjection(
|
|
8458
|
+
toolName,
|
|
8459
|
+
bashCommand,
|
|
8460
|
+
capabilityRuntime,
|
|
8461
|
+
sandbox
|
|
8462
|
+
);
|
|
8463
|
+
const sandboxInput = buildSandboxInput(toolName, parsed);
|
|
8464
|
+
const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
|
|
8465
|
+
const result = isSandbox ? await sandboxExecutor.execute({
|
|
8636
8466
|
toolName,
|
|
8637
|
-
input: toolName === "bash" &&
|
|
8467
|
+
input: toolName === "bash" && (injection.headerTransforms || injection.env) ? {
|
|
8638
8468
|
...sandboxInput,
|
|
8639
|
-
...
|
|
8640
|
-
...
|
|
8469
|
+
...injection.headerTransforms ? { headerTransforms: injection.headerTransforms } : {},
|
|
8470
|
+
...injection.env ? { env: injection.env } : {}
|
|
8641
8471
|
} : sandboxInput
|
|
8642
8472
|
}) : await toolDef.execute(parsed, {
|
|
8643
8473
|
experimental_context: sandbox
|
|
8644
8474
|
});
|
|
8645
|
-
const
|
|
8646
|
-
const durationMs = Date.now() - toolStartedAt;
|
|
8647
|
-
const structuredToolResult = isStructuredToolExecutionResult(
|
|
8648
|
-
resultDetails
|
|
8649
|
-
) ? resultDetails : void 0;
|
|
8475
|
+
const normalized = normalizeToolResult(result, isSandbox);
|
|
8650
8476
|
const toolResultAttribute = serializeGenAiAttribute(
|
|
8651
|
-
|
|
8477
|
+
normalized.details
|
|
8652
8478
|
);
|
|
8653
|
-
|
|
8654
|
-
|
|
8655
|
-
|
|
8656
|
-
|
|
8657
|
-
});
|
|
8658
|
-
setSpanStatus("ok");
|
|
8659
|
-
if (structuredToolResult) {
|
|
8660
|
-
return structuredToolResult;
|
|
8479
|
+
if (toolResultAttribute) {
|
|
8480
|
+
setSpanAttributes({
|
|
8481
|
+
"gen_ai.tool.call.result": toolResultAttribute
|
|
8482
|
+
});
|
|
8661
8483
|
}
|
|
8662
|
-
return
|
|
8663
|
-
content: [
|
|
8664
|
-
{ type: "text", text: toToolContentText(resultDetails) }
|
|
8665
|
-
],
|
|
8666
|
-
details: resultDetails
|
|
8667
|
-
};
|
|
8484
|
+
return normalized;
|
|
8668
8485
|
} catch (error) {
|
|
8669
|
-
|
|
8670
|
-
setSpanAttributes({
|
|
8671
|
-
"app.ai.tool_duration_ms": durationMs,
|
|
8672
|
-
"app.ai.tool_outcome": "error",
|
|
8673
|
-
"error.type": error instanceof Error ? error.name : "tool_execution_error"
|
|
8674
|
-
});
|
|
8675
|
-
setSpanStatus("error");
|
|
8676
|
-
if (shouldTrace) {
|
|
8677
|
-
logWarn(
|
|
8678
|
-
"agent_tool_call_failed",
|
|
8679
|
-
traceToolContext,
|
|
8680
|
-
{
|
|
8681
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8682
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8683
|
-
"gen_ai.tool.name": toolName,
|
|
8684
|
-
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8685
|
-
"app.ai.tool_duration_ms": durationMs,
|
|
8686
|
-
"app.ai.tool_outcome": "error",
|
|
8687
|
-
"error.type": error instanceof Error ? error.name : "tool_execution_error",
|
|
8688
|
-
"error.message": error instanceof Error ? error.message : String(error)
|
|
8689
|
-
},
|
|
8690
|
-
"Agent tool call failed"
|
|
8691
|
-
);
|
|
8692
|
-
}
|
|
8693
|
-
logException(
|
|
8486
|
+
handleToolExecutionError(
|
|
8694
8487
|
error,
|
|
8695
|
-
|
|
8696
|
-
|
|
8697
|
-
|
|
8698
|
-
|
|
8699
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8700
|
-
"gen_ai.tool.name": toolName,
|
|
8701
|
-
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8702
|
-
"app.ai.tool_duration_ms": durationMs,
|
|
8703
|
-
...getToolErrorAttributes(error)
|
|
8704
|
-
},
|
|
8705
|
-
"Agent tool call failed"
|
|
8488
|
+
toolName,
|
|
8489
|
+
normalizedToolCallId,
|
|
8490
|
+
shouldTrace,
|
|
8491
|
+
traceToolContext
|
|
8706
8492
|
);
|
|
8707
|
-
throw error;
|
|
8708
8493
|
}
|
|
8709
8494
|
},
|
|
8710
8495
|
{
|
|
8711
8496
|
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8712
8497
|
"gen_ai.operation.name": "execute_tool",
|
|
8713
8498
|
"gen_ai.tool.name": toolName,
|
|
8499
|
+
"gen_ai.tool.description": toolDef.description,
|
|
8714
8500
|
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8715
8501
|
...toolArgumentsAttribute ? { "gen_ai.tool.call.arguments": toolArgumentsAttribute } : {}
|
|
8716
8502
|
}
|
|
@@ -8779,6 +8565,65 @@ function resolveReplyDelivery(args) {
|
|
|
8779
8565
|
};
|
|
8780
8566
|
}
|
|
8781
8567
|
|
|
8568
|
+
// src/chat/services/reply-delivery-plan.ts
|
|
8569
|
+
var REACTION_ONLY_ACK_RE = /^(?::[a-z0-9_+-]+:|[\p{Extended_Pictographic}\uFE0F\u200D]+)$/u;
|
|
8570
|
+
var REDUNDANT_REACTION_ACK_TEXT = ["done", "got it", "ok", "okay"];
|
|
8571
|
+
var REACTION_ALIAS_PREFIX_RE = /^:[a-z0-9_+-]*$/i;
|
|
8572
|
+
function normalizeReactionAckText(text) {
|
|
8573
|
+
return text.trim().toLowerCase().replace(/[!.]+$/g, "");
|
|
8574
|
+
}
|
|
8575
|
+
function isRedundantReactionAckText(text) {
|
|
8576
|
+
const trimmed = text.trim();
|
|
8577
|
+
if (!trimmed) {
|
|
8578
|
+
return false;
|
|
8579
|
+
}
|
|
8580
|
+
if (REACTION_ONLY_ACK_RE.test(trimmed)) {
|
|
8581
|
+
return true;
|
|
8582
|
+
}
|
|
8583
|
+
const normalized = normalizeReactionAckText(text);
|
|
8584
|
+
return REDUNDANT_REACTION_ACK_TEXT.includes(
|
|
8585
|
+
normalized
|
|
8586
|
+
);
|
|
8587
|
+
}
|
|
8588
|
+
function isPotentialRedundantReactionAckText(text) {
|
|
8589
|
+
const trimmed = text.trim();
|
|
8590
|
+
if (!trimmed) {
|
|
8591
|
+
return true;
|
|
8592
|
+
}
|
|
8593
|
+
if (REACTION_ONLY_ACK_RE.test(trimmed) || REACTION_ALIAS_PREFIX_RE.test(trimmed)) {
|
|
8594
|
+
return true;
|
|
8595
|
+
}
|
|
8596
|
+
const normalized = normalizeReactionAckText(text);
|
|
8597
|
+
return REDUNDANT_REACTION_ACK_TEXT.some(
|
|
8598
|
+
(candidate) => candidate.startsWith(normalized)
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8601
|
+
function buildReplyDeliveryPlan(args) {
|
|
8602
|
+
const mode = args.explicitChannelPostIntent && args.channelPostPerformed ? "channel_only" : "thread";
|
|
8603
|
+
let attachFiles = "none";
|
|
8604
|
+
if (args.hasFiles && mode === "thread") {
|
|
8605
|
+
attachFiles = args.streamingThreadReply ? "followup" : "inline";
|
|
8606
|
+
}
|
|
8607
|
+
return {
|
|
8608
|
+
mode,
|
|
8609
|
+
postThreadText: mode === "thread",
|
|
8610
|
+
attachFiles
|
|
8611
|
+
};
|
|
8612
|
+
}
|
|
8613
|
+
|
|
8614
|
+
// src/chat/services/channel-intent.ts
|
|
8615
|
+
function isExplicitChannelPostIntent(text) {
|
|
8616
|
+
if (!/\bchannel\b/i.test(text)) {
|
|
8617
|
+
return false;
|
|
8618
|
+
}
|
|
8619
|
+
const directChannelVerb = /\b(show|post|send|share|say|announce|broadcast)\b[\s\S]{0,80}\b(?:the\s+)?channel\b/i;
|
|
8620
|
+
if (directChannelVerb.test(text)) {
|
|
8621
|
+
return true;
|
|
8622
|
+
}
|
|
8623
|
+
const scopedChannelVerb = /\b(post|send|share|say|announce|broadcast)\b[\s\S]{0,80}\b(?:in|to)\b[\s\S]{0,40}\b(?:the\s+)?channel\b/i;
|
|
8624
|
+
return scopedChannelVerb.test(text);
|
|
8625
|
+
}
|
|
8626
|
+
|
|
8782
8627
|
// src/chat/services/attachment-claims.ts
|
|
8783
8628
|
function splitSentences(text) {
|
|
8784
8629
|
return text.split(/\n+/).flatMap((line) => line.split(/(?<=[.!?])\s+/)).map((part) => part.trim()).filter((part) => part.length > 0);
|
|
@@ -8808,8 +8653,268 @@ function enforceAttachmentClaimTruth(text, hasAttachedFiles) {
|
|
|
8808
8653
|
Note: No file was attached in this turn. I need to attach the file before claiming it is shared.`;
|
|
8809
8654
|
}
|
|
8810
8655
|
|
|
8811
|
-
// src/chat/
|
|
8812
|
-
|
|
8656
|
+
// src/chat/services/turn-result.ts
|
|
8657
|
+
function buildTurnResult(input) {
|
|
8658
|
+
const {
|
|
8659
|
+
newMessages,
|
|
8660
|
+
userInput,
|
|
8661
|
+
replyFiles,
|
|
8662
|
+
artifactStatePatch,
|
|
8663
|
+
toolCalls,
|
|
8664
|
+
sandboxId,
|
|
8665
|
+
sandboxDependencyProfileHash,
|
|
8666
|
+
hasTextDeltaCallback,
|
|
8667
|
+
shouldTrace,
|
|
8668
|
+
spanContext,
|
|
8669
|
+
correlation,
|
|
8670
|
+
assistantUserName
|
|
8671
|
+
} = input;
|
|
8672
|
+
const toolResults = newMessages.filter(isToolResultMessage);
|
|
8673
|
+
const assistantMessages = newMessages.filter(isAssistantMessage);
|
|
8674
|
+
const primaryText = assistantMessages.map((message) => extractAssistantText(message)).join("\n\n").trim();
|
|
8675
|
+
const oauthStartedMessage = extractOAuthStartedMessageFromToolResults(toolResults);
|
|
8676
|
+
const toolErrorCount = toolResults.filter((result) => result.isError).length;
|
|
8677
|
+
const explicitChannelPostIntent = isExplicitChannelPostIntent(userInput);
|
|
8678
|
+
const successfulToolNames = new Set(
|
|
8679
|
+
toolResults.filter((result) => !isToolResultError(result)).map((result) => normalizeToolNameFromResult(result)).filter((value) => Boolean(value))
|
|
8680
|
+
);
|
|
8681
|
+
const channelPostPerformed = successfulToolNames.has(
|
|
8682
|
+
"slackChannelPostMessage"
|
|
8683
|
+
);
|
|
8684
|
+
const deliveryPlan = buildReplyDeliveryPlan({
|
|
8685
|
+
explicitChannelPostIntent,
|
|
8686
|
+
channelPostPerformed,
|
|
8687
|
+
hasFiles: replyFiles.length > 0,
|
|
8688
|
+
streamingThreadReply: hasTextDeltaCallback
|
|
8689
|
+
});
|
|
8690
|
+
const deliveryMode = deliveryPlan.mode;
|
|
8691
|
+
if (!primaryText && !oauthStartedMessage) {
|
|
8692
|
+
logWarn(
|
|
8693
|
+
"ai_model_response_empty",
|
|
8694
|
+
{
|
|
8695
|
+
slackThreadId: correlation?.threadId,
|
|
8696
|
+
slackUserId: correlation?.requesterId,
|
|
8697
|
+
slackChannelId: correlation?.channelId,
|
|
8698
|
+
runId: correlation?.runId,
|
|
8699
|
+
assistantUserName,
|
|
8700
|
+
modelId: botConfig.modelId
|
|
8701
|
+
},
|
|
8702
|
+
{
|
|
8703
|
+
"app.ai.tool_results": toolResults.length,
|
|
8704
|
+
"app.ai.tool_error_results": toolErrorCount,
|
|
8705
|
+
"app.ai.generated_files": input.generatedFileCount
|
|
8706
|
+
},
|
|
8707
|
+
"Model returned empty text response"
|
|
8708
|
+
);
|
|
8709
|
+
}
|
|
8710
|
+
const lastAssistant = assistantMessages.at(-1);
|
|
8711
|
+
const stopReason = typeof lastAssistant?.stopReason === "string" ? lastAssistant.stopReason : void 0;
|
|
8712
|
+
const errorMessage = typeof lastAssistant?.errorMessage === "string" ? lastAssistant.errorMessage : void 0;
|
|
8713
|
+
const usedPrimaryText = Boolean(primaryText);
|
|
8714
|
+
const outcome = primaryText || oauthStartedMessage ? stopReason === "error" ? "provider_error" : "success" : "execution_failure";
|
|
8715
|
+
const fallbackText = oauthStartedMessage ?? buildExecutionFailureMessage(toolErrorCount);
|
|
8716
|
+
const responseText = primaryText || fallbackText;
|
|
8717
|
+
const escapedOrRawPayload = Boolean(primaryText) && (isExecutionEscapeResponse(primaryText) || isRawToolPayloadResponse(primaryText));
|
|
8718
|
+
const resolvedText = escapedOrRawPayload ? fallbackText : enforceAttachmentClaimTruth(responseText, replyFiles.length > 0);
|
|
8719
|
+
const resolvedOutcome = escapedOrRawPayload ? oauthStartedMessage ? outcome : "execution_failure" : outcome;
|
|
8720
|
+
if (shouldTrace) {
|
|
8721
|
+
logInfo(
|
|
8722
|
+
"agent_message_out",
|
|
8723
|
+
spanContext,
|
|
8724
|
+
{
|
|
8725
|
+
"app.message.kind": "assistant_outbound",
|
|
8726
|
+
"app.message.length": resolvedText.length,
|
|
8727
|
+
"app.message.output": summarizeMessageText(resolvedText),
|
|
8728
|
+
"app.ai.outcome": resolvedOutcome,
|
|
8729
|
+
"app.ai.assistant_messages": assistantMessages.length,
|
|
8730
|
+
...stopReason ? { "gen_ai.response.finish_reasons": [stopReason] } : {}
|
|
8731
|
+
},
|
|
8732
|
+
"Agent message sent"
|
|
8733
|
+
);
|
|
8734
|
+
}
|
|
8735
|
+
const resolvedDiagnostics = {
|
|
8736
|
+
outcome: resolvedOutcome,
|
|
8737
|
+
modelId: botConfig.modelId,
|
|
8738
|
+
assistantMessageCount: assistantMessages.length,
|
|
8739
|
+
toolCalls,
|
|
8740
|
+
toolResultCount: toolResults.length,
|
|
8741
|
+
toolErrorCount,
|
|
8742
|
+
usedPrimaryText,
|
|
8743
|
+
stopReason,
|
|
8744
|
+
errorMessage,
|
|
8745
|
+
providerError: void 0
|
|
8746
|
+
};
|
|
8747
|
+
return {
|
|
8748
|
+
text: resolvedText,
|
|
8749
|
+
files: replyFiles.length > 0 ? replyFiles : void 0,
|
|
8750
|
+
artifactStatePatch: Object.keys(artifactStatePatch).length > 0 ? artifactStatePatch : void 0,
|
|
8751
|
+
deliveryPlan,
|
|
8752
|
+
deliveryMode,
|
|
8753
|
+
sandboxId,
|
|
8754
|
+
sandboxDependencyProfileHash,
|
|
8755
|
+
diagnostics: resolvedDiagnostics
|
|
8756
|
+
};
|
|
8757
|
+
}
|
|
8758
|
+
|
|
8759
|
+
// src/chat/state/turn-session-store.ts
|
|
8760
|
+
var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
|
|
8761
|
+
var AGENT_TURN_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8762
|
+
function agentTurnSessionKey(conversationId, sessionId) {
|
|
8763
|
+
return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
|
|
8764
|
+
}
|
|
8765
|
+
function parseAgentTurnSessionCheckpoint(value) {
|
|
8766
|
+
if (typeof value !== "string") {
|
|
8767
|
+
return void 0;
|
|
8768
|
+
}
|
|
8769
|
+
try {
|
|
8770
|
+
const parsed = JSON.parse(value);
|
|
8771
|
+
if (!isRecord(parsed)) {
|
|
8772
|
+
return void 0;
|
|
8773
|
+
}
|
|
8774
|
+
const status = parsed.state;
|
|
8775
|
+
if (status !== "running" && status !== "awaiting_resume" && status !== "completed" && status !== "failed") {
|
|
8776
|
+
return void 0;
|
|
8777
|
+
}
|
|
8778
|
+
const conversationId = parsed.conversationId;
|
|
8779
|
+
const sessionId = parsed.sessionId;
|
|
8780
|
+
const sliceId = parsed.sliceId;
|
|
8781
|
+
const checkpointVersion = parsed.checkpointVersion;
|
|
8782
|
+
const updatedAtMs = parsed.updatedAtMs;
|
|
8783
|
+
if (typeof conversationId !== "string" || typeof sessionId !== "string" || typeof sliceId !== "number" || typeof checkpointVersion !== "number" || typeof updatedAtMs !== "number") {
|
|
8784
|
+
return void 0;
|
|
8785
|
+
}
|
|
8786
|
+
return {
|
|
8787
|
+
checkpointVersion,
|
|
8788
|
+
conversationId,
|
|
8789
|
+
sessionId,
|
|
8790
|
+
sliceId,
|
|
8791
|
+
state: status,
|
|
8792
|
+
updatedAtMs,
|
|
8793
|
+
piMessages: Array.isArray(parsed.piMessages) ? parsed.piMessages : [],
|
|
8794
|
+
...Array.isArray(parsed.loadedSkillNames) ? {
|
|
8795
|
+
loadedSkillNames: parsed.loadedSkillNames.filter(
|
|
8796
|
+
(value2) => typeof value2 === "string"
|
|
8797
|
+
)
|
|
8798
|
+
} : {},
|
|
8799
|
+
...parsed.resumeReason === "timeout" || parsed.resumeReason === "auth" ? { resumeReason: parsed.resumeReason } : {},
|
|
8800
|
+
...typeof parsed.errorMessage === "string" ? { errorMessage: parsed.errorMessage } : {},
|
|
8801
|
+
...typeof parsed.resumedFromSliceId === "number" ? { resumedFromSliceId: parsed.resumedFromSliceId } : {}
|
|
8802
|
+
};
|
|
8803
|
+
} catch {
|
|
8804
|
+
return void 0;
|
|
8805
|
+
}
|
|
8806
|
+
}
|
|
8807
|
+
async function getAgentTurnSessionCheckpoint(conversationId, sessionId) {
|
|
8808
|
+
const stateAdapter = getStateAdapter();
|
|
8809
|
+
await stateAdapter.connect();
|
|
8810
|
+
const value = await stateAdapter.get(
|
|
8811
|
+
agentTurnSessionKey(conversationId, sessionId)
|
|
8812
|
+
);
|
|
8813
|
+
return parseAgentTurnSessionCheckpoint(value);
|
|
8814
|
+
}
|
|
8815
|
+
async function upsertAgentTurnSessionCheckpoint(args) {
|
|
8816
|
+
const stateAdapter = getStateAdapter();
|
|
8817
|
+
await stateAdapter.connect();
|
|
8818
|
+
const existing = await getAgentTurnSessionCheckpoint(
|
|
8819
|
+
args.conversationId,
|
|
8820
|
+
args.sessionId
|
|
8821
|
+
);
|
|
8822
|
+
const checkpoint = {
|
|
8823
|
+
checkpointVersion: (existing?.checkpointVersion ?? 0) + 1,
|
|
8824
|
+
conversationId: args.conversationId,
|
|
8825
|
+
sessionId: args.sessionId,
|
|
8826
|
+
sliceId: args.sliceId,
|
|
8827
|
+
state: args.state,
|
|
8828
|
+
updatedAtMs: Date.now(),
|
|
8829
|
+
piMessages: Array.isArray(args.piMessages) ? args.piMessages : [],
|
|
8830
|
+
...Array.isArray(args.loadedSkillNames) ? {
|
|
8831
|
+
loadedSkillNames: args.loadedSkillNames.filter(
|
|
8832
|
+
(value) => typeof value === "string"
|
|
8833
|
+
)
|
|
8834
|
+
} : {},
|
|
8835
|
+
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
8836
|
+
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
8837
|
+
...typeof args.resumedFromSliceId === "number" ? { resumedFromSliceId: args.resumedFromSliceId } : {}
|
|
8838
|
+
};
|
|
8839
|
+
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
8840
|
+
await stateAdapter.set(
|
|
8841
|
+
agentTurnSessionKey(args.conversationId, args.sessionId),
|
|
8842
|
+
JSON.stringify(checkpoint),
|
|
8843
|
+
ttlMs
|
|
8844
|
+
);
|
|
8845
|
+
return checkpoint;
|
|
8846
|
+
}
|
|
8847
|
+
|
|
8848
|
+
// src/chat/services/turn-checkpoint.ts
|
|
8849
|
+
async function loadTurnCheckpoint(ctx) {
|
|
8850
|
+
const canUseTurnSession = Boolean(ctx.conversationId && ctx.sessionId);
|
|
8851
|
+
const existingCheckpoint = canUseTurnSession && ctx.conversationId && ctx.sessionId ? await getAgentTurnSessionCheckpoint(ctx.conversationId, ctx.sessionId) : void 0;
|
|
8852
|
+
const hasAwaitingResumeCheckpoint = Boolean(
|
|
8853
|
+
existingCheckpoint && existingCheckpoint.state === "awaiting_resume" && existingCheckpoint.piMessages.length > 0
|
|
8854
|
+
);
|
|
8855
|
+
return {
|
|
8856
|
+
canUseTurnSession,
|
|
8857
|
+
resumedFromCheckpoint: hasAwaitingResumeCheckpoint,
|
|
8858
|
+
currentSliceId: hasAwaitingResumeCheckpoint ? existingCheckpoint.sliceId : 1,
|
|
8859
|
+
existingCheckpoint
|
|
8860
|
+
};
|
|
8861
|
+
}
|
|
8862
|
+
async function persistCompletedCheckpoint(args) {
|
|
8863
|
+
await upsertAgentTurnSessionCheckpoint({
|
|
8864
|
+
conversationId: args.conversationId,
|
|
8865
|
+
sessionId: args.sessionId,
|
|
8866
|
+
sliceId: args.sliceId,
|
|
8867
|
+
state: "completed",
|
|
8868
|
+
piMessages: args.allMessages,
|
|
8869
|
+
loadedSkillNames: args.loadedSkillNames
|
|
8870
|
+
});
|
|
8871
|
+
}
|
|
8872
|
+
async function persistAuthPauseCheckpoint(args) {
|
|
8873
|
+
const nextSliceId = args.currentSliceId + 1;
|
|
8874
|
+
try {
|
|
8875
|
+
const latestCheckpoint = await getAgentTurnSessionCheckpoint(
|
|
8876
|
+
args.conversationId,
|
|
8877
|
+
args.sessionId
|
|
8878
|
+
);
|
|
8879
|
+
const piMessages = trimTrailingAssistantMessages(
|
|
8880
|
+
args.messages.length > 0 ? args.messages : latestCheckpoint?.piMessages ?? []
|
|
8881
|
+
);
|
|
8882
|
+
await upsertAgentTurnSessionCheckpoint({
|
|
8883
|
+
conversationId: args.conversationId,
|
|
8884
|
+
sessionId: args.sessionId,
|
|
8885
|
+
sliceId: nextSliceId,
|
|
8886
|
+
state: "awaiting_resume",
|
|
8887
|
+
piMessages,
|
|
8888
|
+
loadedSkillNames: args.loadedSkillNames,
|
|
8889
|
+
resumeReason: "auth",
|
|
8890
|
+
resumedFromSliceId: args.currentSliceId,
|
|
8891
|
+
errorMessage: args.errorMessage
|
|
8892
|
+
});
|
|
8893
|
+
} catch (checkpointError) {
|
|
8894
|
+
logException(
|
|
8895
|
+
checkpointError,
|
|
8896
|
+
"agent_turn_auth_resume_checkpoint_failed",
|
|
8897
|
+
{
|
|
8898
|
+
slackThreadId: args.logContext.threadId,
|
|
8899
|
+
slackUserId: args.logContext.requesterId,
|
|
8900
|
+
slackChannelId: args.logContext.channelId,
|
|
8901
|
+
runId: args.logContext.runId,
|
|
8902
|
+
assistantUserName: args.logContext.assistantUserName,
|
|
8903
|
+
modelId: args.logContext.modelId
|
|
8904
|
+
},
|
|
8905
|
+
{
|
|
8906
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
8907
|
+
"app.ai.resume_session_id": args.sessionId,
|
|
8908
|
+
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
8909
|
+
"app.ai.resume_next_slice_id": nextSliceId
|
|
8910
|
+
},
|
|
8911
|
+
"Failed to persist auth checkpoint before retry"
|
|
8912
|
+
);
|
|
8913
|
+
}
|
|
8914
|
+
return nextSliceId;
|
|
8915
|
+
}
|
|
8916
|
+
|
|
8917
|
+
// src/chat/services/mcp-auth-orchestration.ts
|
|
8813
8918
|
var McpAuthorizationPauseError = class extends Error {
|
|
8814
8919
|
provider;
|
|
8815
8920
|
constructor(provider) {
|
|
@@ -8818,6 +8923,86 @@ var McpAuthorizationPauseError = class extends Error {
|
|
|
8818
8923
|
this.provider = provider;
|
|
8819
8924
|
}
|
|
8820
8925
|
};
|
|
8926
|
+
function createMcpAuthOrchestration(deps, abortAgent) {
|
|
8927
|
+
let pendingPause;
|
|
8928
|
+
const authSessionIdsByProvider = /* @__PURE__ */ new Map();
|
|
8929
|
+
const authProviderFactory = async (plugin) => {
|
|
8930
|
+
if (!deps.conversationId || !deps.sessionId || !deps.requesterId) {
|
|
8931
|
+
return void 0;
|
|
8932
|
+
}
|
|
8933
|
+
const provider = await createMcpOAuthClientProvider({
|
|
8934
|
+
provider: plugin.manifest.name,
|
|
8935
|
+
conversationId: deps.conversationId,
|
|
8936
|
+
sessionId: deps.sessionId,
|
|
8937
|
+
userId: deps.requesterId,
|
|
8938
|
+
userMessage: deps.userMessage,
|
|
8939
|
+
...deps.channelId ? { channelId: deps.channelId } : {},
|
|
8940
|
+
...deps.threadTs ? { threadTs: deps.threadTs } : {},
|
|
8941
|
+
...deps.toolChannelId ? { toolChannelId: deps.toolChannelId } : {},
|
|
8942
|
+
configuration: deps.getConfiguration(),
|
|
8943
|
+
artifactState: deps.getArtifactState()
|
|
8944
|
+
});
|
|
8945
|
+
authSessionIdsByProvider.set(plugin.manifest.name, provider.authSessionId);
|
|
8946
|
+
return provider;
|
|
8947
|
+
};
|
|
8948
|
+
const onAuthorizationRequired = async (provider) => {
|
|
8949
|
+
if (pendingPause) {
|
|
8950
|
+
return true;
|
|
8951
|
+
}
|
|
8952
|
+
const authSessionId = authSessionIdsByProvider.get(provider);
|
|
8953
|
+
if (!authSessionId || !deps.requesterId) {
|
|
8954
|
+
throw new Error(
|
|
8955
|
+
`Missing MCP auth session context for plugin "${provider}"`
|
|
8956
|
+
);
|
|
8957
|
+
}
|
|
8958
|
+
const latestArtifactState = deps.getMergedArtifactState();
|
|
8959
|
+
await patchMcpAuthSession(authSessionId, {
|
|
8960
|
+
configuration: { ...deps.getConfiguration() },
|
|
8961
|
+
artifactState: latestArtifactState,
|
|
8962
|
+
toolChannelId: deps.toolChannelId ?? latestArtifactState.assistantContextChannelId ?? deps.channelId
|
|
8963
|
+
});
|
|
8964
|
+
const authSession = await getMcpAuthSession(authSessionId);
|
|
8965
|
+
if (!authSession?.authorizationUrl) {
|
|
8966
|
+
throw new Error(`Missing MCP authorization URL for plugin "${provider}"`);
|
|
8967
|
+
}
|
|
8968
|
+
const delivery = await deliverPrivateMessage({
|
|
8969
|
+
channelId: authSession.channelId,
|
|
8970
|
+
threadTs: authSession.threadTs,
|
|
8971
|
+
userId: authSession.userId,
|
|
8972
|
+
text: `<${authSession.authorizationUrl}|Click here to link your ${formatProviderLabel(provider)} MCP access>. Once you've authorized, this thread will continue automatically.`
|
|
8973
|
+
});
|
|
8974
|
+
if (!delivery) {
|
|
8975
|
+
throw new Error(
|
|
8976
|
+
`Unable to deliver MCP authorization link for plugin "${provider}"`
|
|
8977
|
+
);
|
|
8978
|
+
}
|
|
8979
|
+
pendingPause = new McpAuthorizationPauseError(provider);
|
|
8980
|
+
abortAgent();
|
|
8981
|
+
return true;
|
|
8982
|
+
};
|
|
8983
|
+
return {
|
|
8984
|
+
authProviderFactory,
|
|
8985
|
+
onAuthorizationRequired,
|
|
8986
|
+
getPendingPause: () => pendingPause
|
|
8987
|
+
};
|
|
8988
|
+
}
|
|
8989
|
+
|
|
8990
|
+
// src/chat/respond.ts
|
|
8991
|
+
var startupDiscoveryLogged = false;
|
|
8992
|
+
function mcpToolsToDefinitions(mcpTools) {
|
|
8993
|
+
const defs = {};
|
|
8994
|
+
for (const tool2 of mcpTools) {
|
|
8995
|
+
defs[tool2.name] = {
|
|
8996
|
+
description: tool2.description,
|
|
8997
|
+
// Raw JSON Schema from MCP servers — not a TypeBox TSchema, but
|
|
8998
|
+
// pi-agent-core validates with AJV and the Anthropic provider reads
|
|
8999
|
+
// .properties/.required, so raw JSON Schema works at runtime.
|
|
9000
|
+
inputSchema: tool2.parameters,
|
|
9001
|
+
execute: async (args) => tool2.execute(args)
|
|
9002
|
+
};
|
|
9003
|
+
}
|
|
9004
|
+
return defs;
|
|
9005
|
+
}
|
|
8821
9006
|
async function maybeReplaceAgentMessages(agent, messages) {
|
|
8822
9007
|
const resumable = agent;
|
|
8823
9008
|
if (typeof resumable.replaceMessages !== "function") {
|
|
@@ -8842,7 +9027,6 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
8842
9027
|
let lastKnownSandboxDependencyProfileHash = context.sandbox?.sandboxDependencyProfileHash;
|
|
8843
9028
|
let loadedSkillNamesForResume = [];
|
|
8844
9029
|
let mcpToolManager;
|
|
8845
|
-
let pendingMcpAuthorizationPause;
|
|
8846
9030
|
try {
|
|
8847
9031
|
const shouldTrace = shouldEmitDevAgentTrace();
|
|
8848
9032
|
const spanContext = {
|
|
@@ -8901,15 +9085,13 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
8901
9085
|
const activeSkills = [];
|
|
8902
9086
|
const skillSandbox = new SkillSandbox(availableSkills, activeSkills);
|
|
8903
9087
|
const { conversationId: sessionConversationId, sessionId } = getSessionIdentifiers(context);
|
|
8904
|
-
const
|
|
9088
|
+
const checkpointState = await loadTurnCheckpoint({
|
|
9089
|
+
conversationId: sessionConversationId,
|
|
9090
|
+
sessionId
|
|
9091
|
+
});
|
|
9092
|
+
const { resumedFromCheckpoint, currentSliceId, existingCheckpoint } = checkpointState;
|
|
8905
9093
|
timeoutResumeConversationId = sessionConversationId;
|
|
8906
9094
|
timeoutResumeSessionId = sessionId;
|
|
8907
|
-
const existingTurnCheckpoint = canUseTurnSession && sessionConversationId && sessionId ? await getAgentTurnSessionCheckpoint(sessionConversationId, sessionId) : void 0;
|
|
8908
|
-
const hasAwaitingResumeCheckpoint = Boolean(
|
|
8909
|
-
existingTurnCheckpoint && existingTurnCheckpoint.state === "awaiting_resume" && existingTurnCheckpoint.piMessages.length > 0
|
|
8910
|
-
);
|
|
8911
|
-
const resumedFromCheckpoint = hasAwaitingResumeCheckpoint;
|
|
8912
|
-
const currentSliceId = hasAwaitingResumeCheckpoint ? existingTurnCheckpoint.sliceId : 1;
|
|
8913
9095
|
timeoutResumeSliceId = currentSliceId;
|
|
8914
9096
|
const capabilityRuntime = createSkillCapabilityRuntime({
|
|
8915
9097
|
invocationArgs: skillInvocation?.args,
|
|
@@ -8946,7 +9128,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
8946
9128
|
lastKnownSandboxDependencyProfileHash = sandboxExecutor.getDependencyProfileHash();
|
|
8947
9129
|
sandboxExecutor.configureSkills(availableSkills);
|
|
8948
9130
|
const sandbox = await sandboxExecutor.createSandbox();
|
|
8949
|
-
for (const skillName of
|
|
9131
|
+
for (const skillName of existingCheckpoint?.loadedSkillNames ?? []) {
|
|
8950
9132
|
const preloaded = await skillSandbox.loadSkill(skillName);
|
|
8951
9133
|
if (preloaded) {
|
|
8952
9134
|
upsertActiveSkill(activeSkills, preloaded);
|
|
@@ -8960,79 +9142,36 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
8960
9142
|
}
|
|
8961
9143
|
const userTurnText = buildUserTurnText(
|
|
8962
9144
|
userInput,
|
|
8963
|
-
context.conversationContext
|
|
9145
|
+
context.conversationContext,
|
|
9146
|
+
{
|
|
9147
|
+
sessionContext: { conversationId: sessionConversationId },
|
|
9148
|
+
turnContext: { traceId: getActiveTraceId() }
|
|
9149
|
+
}
|
|
8964
9150
|
);
|
|
8965
9151
|
timeoutResumeMessages = [];
|
|
8966
|
-
pendingMcpAuthorizationPause = void 0;
|
|
8967
9152
|
const generatedFiles = [];
|
|
8968
9153
|
const replyFiles = [];
|
|
8969
9154
|
const artifactStatePatch = {};
|
|
8970
9155
|
const toolCalls = [];
|
|
8971
|
-
const mcpAuthSessionIdsByProvider = /* @__PURE__ */ new Map();
|
|
8972
9156
|
let agent;
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
8978
|
-
|
|
8979
|
-
|
|
8980
|
-
|
|
8981
|
-
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
...context.correlation?.threadTs ? { threadTs: context.correlation.threadTs } : {},
|
|
8986
|
-
...context.toolChannelId ? { toolChannelId: context.toolChannelId } : {},
|
|
8987
|
-
configuration: configurationValues,
|
|
8988
|
-
artifactState: context.artifactState
|
|
8989
|
-
});
|
|
8990
|
-
mcpAuthSessionIdsByProvider.set(
|
|
8991
|
-
plugin.manifest.name,
|
|
8992
|
-
provider.authSessionId
|
|
8993
|
-
);
|
|
8994
|
-
return provider;
|
|
9157
|
+
const mcpAuth = createMcpAuthOrchestration(
|
|
9158
|
+
{
|
|
9159
|
+
conversationId: sessionConversationId,
|
|
9160
|
+
sessionId,
|
|
9161
|
+
requesterId: context.requester?.userId,
|
|
9162
|
+
channelId: context.correlation?.channelId,
|
|
9163
|
+
threadTs: context.correlation?.threadTs,
|
|
9164
|
+
toolChannelId: context.toolChannelId,
|
|
9165
|
+
userMessage: userInput,
|
|
9166
|
+
getConfiguration: () => configurationValues,
|
|
9167
|
+
getArtifactState: () => context.artifactState,
|
|
9168
|
+
getMergedArtifactState: () => mergeArtifactsState(context.artifactState ?? {}, artifactStatePatch)
|
|
8995
9169
|
},
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
if (!authSessionId || !context.requester?.userId) {
|
|
9002
|
-
throw new Error(
|
|
9003
|
-
`Missing MCP auth session context for plugin "${provider}"`
|
|
9004
|
-
);
|
|
9005
|
-
}
|
|
9006
|
-
const latestArtifactState = mergeArtifactsState(
|
|
9007
|
-
context.artifactState ?? {},
|
|
9008
|
-
artifactStatePatch
|
|
9009
|
-
);
|
|
9010
|
-
await patchMcpAuthSession(authSessionId, {
|
|
9011
|
-
configuration: { ...configurationValues },
|
|
9012
|
-
artifactState: latestArtifactState,
|
|
9013
|
-
toolChannelId: context.toolChannelId ?? latestArtifactState.assistantContextChannelId ?? context.correlation?.channelId
|
|
9014
|
-
});
|
|
9015
|
-
const authSession = await getMcpAuthSession(authSessionId);
|
|
9016
|
-
if (!authSession?.authorizationUrl) {
|
|
9017
|
-
throw new Error(
|
|
9018
|
-
`Missing MCP authorization URL for plugin "${provider}"`
|
|
9019
|
-
);
|
|
9020
|
-
}
|
|
9021
|
-
const delivery = await deliverPrivateMessage({
|
|
9022
|
-
channelId: authSession.channelId,
|
|
9023
|
-
threadTs: authSession.threadTs,
|
|
9024
|
-
userId: authSession.userId,
|
|
9025
|
-
text: `<${authSession.authorizationUrl}|Click here to link your ${formatProviderLabel(provider)} MCP access>. Once you've authorized, this thread will continue automatically.`
|
|
9026
|
-
});
|
|
9027
|
-
if (!delivery) {
|
|
9028
|
-
throw new Error(
|
|
9029
|
-
`Unable to deliver MCP authorization link for plugin "${provider}"`
|
|
9030
|
-
);
|
|
9031
|
-
}
|
|
9032
|
-
pendingMcpAuthorizationPause = new McpAuthorizationPauseError(provider);
|
|
9033
|
-
agent?.abort();
|
|
9034
|
-
return true;
|
|
9035
|
-
}
|
|
9170
|
+
() => agent?.abort()
|
|
9171
|
+
);
|
|
9172
|
+
mcpToolManager = new McpToolManager(getPluginMcpProviders(), {
|
|
9173
|
+
authProviderFactory: mcpAuth.authProviderFactory,
|
|
9174
|
+
onAuthorizationRequired: mcpAuth.onAuthorizationRequired
|
|
9036
9175
|
});
|
|
9037
9176
|
const turnMcpToolManager = mcpToolManager;
|
|
9038
9177
|
const syncResumeState = () => {
|
|
@@ -9075,22 +9214,25 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9075
9214
|
syncResumeState();
|
|
9076
9215
|
await turnMcpToolManager.activateForSkill(effective);
|
|
9077
9216
|
syncResumeState();
|
|
9078
|
-
if (
|
|
9217
|
+
if (mcpAuth.getPendingPause()) {
|
|
9079
9218
|
return void 0;
|
|
9080
9219
|
}
|
|
9081
9220
|
if (!effective.pluginProvider) {
|
|
9082
9221
|
return void 0;
|
|
9083
9222
|
}
|
|
9223
|
+
syncMcpAgentTools();
|
|
9084
9224
|
return {
|
|
9085
9225
|
available_tools: turnMcpToolManager.getActiveToolCatalog(activeSkills, {
|
|
9086
9226
|
provider: effective.pluginProvider
|
|
9087
|
-
}).map(toExposedToolSummary)
|
|
9088
|
-
tool_search_available: true
|
|
9227
|
+
}).map(toExposedToolSummary)
|
|
9089
9228
|
};
|
|
9090
9229
|
}
|
|
9091
9230
|
},
|
|
9092
9231
|
{
|
|
9093
9232
|
channelId: context.toolChannelId ?? context.correlation?.channelId,
|
|
9233
|
+
channelCapabilities: resolveChannelCapabilities(
|
|
9234
|
+
context.toolChannelId ?? context.correlation?.channelId
|
|
9235
|
+
),
|
|
9094
9236
|
messageTs: context.correlation?.messageTs,
|
|
9095
9237
|
threadTs: context.correlation?.threadTs,
|
|
9096
9238
|
userText: userInput,
|
|
@@ -9105,9 +9247,9 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9105
9247
|
for (const skill of activeSkills) {
|
|
9106
9248
|
await turnMcpToolManager.activateForSkill(skill);
|
|
9107
9249
|
syncResumeState();
|
|
9108
|
-
if (
|
|
9109
|
-
timeoutResumeMessages =
|
|
9110
|
-
throw
|
|
9250
|
+
if (mcpAuth.getPendingPause()) {
|
|
9251
|
+
timeoutResumeMessages = existingCheckpoint?.piMessages ?? [];
|
|
9252
|
+
throw mcpAuth.getPendingPause();
|
|
9111
9253
|
}
|
|
9112
9254
|
}
|
|
9113
9255
|
syncResumeState();
|
|
@@ -9152,6 +9294,11 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9152
9294
|
content: userContentParts.map((part) => toObservablePromptPart(part))
|
|
9153
9295
|
}
|
|
9154
9296
|
]);
|
|
9297
|
+
const agentToolHooks = {
|
|
9298
|
+
onToolCall: (toolName) => {
|
|
9299
|
+
toolCalls.push(toolName);
|
|
9300
|
+
}
|
|
9301
|
+
};
|
|
9155
9302
|
const baseAgentTools = createAgentTools(
|
|
9156
9303
|
tools,
|
|
9157
9304
|
skillSandbox,
|
|
@@ -9159,18 +9306,31 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9159
9306
|
context.onStatus,
|
|
9160
9307
|
sandboxExecutor,
|
|
9161
9308
|
capabilityRuntime,
|
|
9162
|
-
|
|
9163
|
-
onToolCall: (toolName) => {
|
|
9164
|
-
toolCalls.push(toolName);
|
|
9165
|
-
}
|
|
9166
|
-
}
|
|
9309
|
+
agentToolHooks
|
|
9167
9310
|
);
|
|
9311
|
+
const agentTools = [...baseAgentTools];
|
|
9312
|
+
const syncMcpAgentTools = () => {
|
|
9313
|
+
const mcpTools = turnMcpToolManager.getResolvedActiveTools(activeSkills);
|
|
9314
|
+
const mcpDefs = mcpToolsToDefinitions(mcpTools);
|
|
9315
|
+
const mcpAgentTools = createAgentTools(
|
|
9316
|
+
mcpDefs,
|
|
9317
|
+
skillSandbox,
|
|
9318
|
+
spanContext,
|
|
9319
|
+
context.onStatus,
|
|
9320
|
+
sandboxExecutor,
|
|
9321
|
+
capabilityRuntime,
|
|
9322
|
+
agentToolHooks
|
|
9323
|
+
);
|
|
9324
|
+
agentTools.length = 0;
|
|
9325
|
+
agentTools.push(...baseAgentTools, ...mcpAgentTools);
|
|
9326
|
+
};
|
|
9327
|
+
syncMcpAgentTools();
|
|
9168
9328
|
agent = new Agent({
|
|
9169
9329
|
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
9170
9330
|
initialState: {
|
|
9171
9331
|
systemPrompt: baseInstructions,
|
|
9172
9332
|
model: resolveGatewayModel(botConfig.modelId),
|
|
9173
|
-
tools:
|
|
9333
|
+
tools: agentTools
|
|
9174
9334
|
}
|
|
9175
9335
|
});
|
|
9176
9336
|
let hasEmittedText = false;
|
|
@@ -9182,16 +9342,10 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9182
9342
|
}
|
|
9183
9343
|
return;
|
|
9184
9344
|
}
|
|
9185
|
-
if (event.type !== "message_update")
|
|
9186
|
-
|
|
9187
|
-
}
|
|
9188
|
-
if (event.assistantMessageEvent.type !== "text_delta") {
|
|
9189
|
-
return;
|
|
9190
|
-
}
|
|
9345
|
+
if (event.type !== "message_update") return;
|
|
9346
|
+
if (event.assistantMessageEvent.type !== "text_delta") return;
|
|
9191
9347
|
const deltaText = event.assistantMessageEvent.delta;
|
|
9192
|
-
if (!deltaText)
|
|
9193
|
-
return;
|
|
9194
|
-
}
|
|
9348
|
+
if (!deltaText) return;
|
|
9195
9349
|
const text = needsSeparator ? "\n\n" + deltaText : deltaText;
|
|
9196
9350
|
needsSeparator = false;
|
|
9197
9351
|
hasEmittedText = true;
|
|
@@ -9213,7 +9367,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9213
9367
|
if (resumedFromCheckpoint) {
|
|
9214
9368
|
const didReplace = await maybeReplaceAgentMessages(
|
|
9215
9369
|
agent,
|
|
9216
|
-
|
|
9370
|
+
existingCheckpoint.piMessages
|
|
9217
9371
|
);
|
|
9218
9372
|
if (!didReplace) {
|
|
9219
9373
|
throw new Error(
|
|
@@ -9265,9 +9419,9 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9265
9419
|
});
|
|
9266
9420
|
timeoutResumeMessages = [...agent.state.messages];
|
|
9267
9421
|
}
|
|
9268
|
-
if (
|
|
9422
|
+
if (mcpAuth.getPendingPause()) {
|
|
9269
9423
|
timeoutResumeMessages = [...agent.state.messages];
|
|
9270
|
-
throw
|
|
9424
|
+
throw mcpAuth.getPendingPause();
|
|
9271
9425
|
}
|
|
9272
9426
|
throw error;
|
|
9273
9427
|
} finally {
|
|
@@ -9279,12 +9433,9 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9279
9433
|
beforeMessageCount
|
|
9280
9434
|
);
|
|
9281
9435
|
completedAssistantTurn = hasCompletedAssistantTurn(newMessages);
|
|
9282
|
-
if (
|
|
9436
|
+
if (mcpAuth.getPendingPause() && !completedAssistantTurn) {
|
|
9283
9437
|
timeoutResumeMessages = [...agent.state.messages];
|
|
9284
|
-
throw
|
|
9285
|
-
}
|
|
9286
|
-
if (pendingMcpAuthorizationPause && completedAssistantTurn) {
|
|
9287
|
-
pendingMcpAuthorizationPause = void 0;
|
|
9438
|
+
throw mcpAuth.getPendingPause();
|
|
9288
9439
|
}
|
|
9289
9440
|
const outputMessages = newMessages.filter(isAssistantMessage);
|
|
9290
9441
|
const outputMessagesAttribute = serializeGenAiAttribute(outputMessages);
|
|
@@ -9308,171 +9459,51 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
9308
9459
|
} finally {
|
|
9309
9460
|
unsubscribe();
|
|
9310
9461
|
}
|
|
9311
|
-
if (
|
|
9312
|
-
throw
|
|
9462
|
+
if (mcpAuth.getPendingPause() && !completedAssistantTurn) {
|
|
9463
|
+
throw mcpAuth.getPendingPause();
|
|
9313
9464
|
}
|
|
9314
|
-
if (canUseTurnSession && sessionConversationId && sessionId) {
|
|
9315
|
-
await
|
|
9465
|
+
if (checkpointState.canUseTurnSession && sessionConversationId && sessionId) {
|
|
9466
|
+
await persistCompletedCheckpoint({
|
|
9316
9467
|
conversationId: sessionConversationId,
|
|
9317
9468
|
sessionId,
|
|
9318
9469
|
sliceId: currentSliceId,
|
|
9319
|
-
|
|
9320
|
-
piMessages: agent.state.messages,
|
|
9470
|
+
allMessages: agent.state.messages,
|
|
9321
9471
|
loadedSkillNames: activeSkills.map((skill) => skill.name)
|
|
9322
9472
|
});
|
|
9323
9473
|
}
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9328
|
-
|
|
9329
|
-
|
|
9330
|
-
|
|
9331
|
-
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9335
|
-
|
|
9336
|
-
|
|
9337
|
-
|
|
9338
|
-
const deliveryPlan = buildReplyDeliveryPlan({
|
|
9339
|
-
explicitChannelPostIntent,
|
|
9340
|
-
channelPostPerformed,
|
|
9341
|
-
hasFiles: replyFiles.length > 0,
|
|
9342
|
-
streamingThreadReply: Boolean(context.onTextDelta)
|
|
9474
|
+
return buildTurnResult({
|
|
9475
|
+
newMessages,
|
|
9476
|
+
userInput,
|
|
9477
|
+
replyFiles,
|
|
9478
|
+
artifactStatePatch,
|
|
9479
|
+
toolCalls,
|
|
9480
|
+
sandboxId: sandboxExecutor.getSandboxId(),
|
|
9481
|
+
sandboxDependencyProfileHash: sandboxExecutor.getDependencyProfileHash(),
|
|
9482
|
+
generatedFileCount: generatedFiles.length,
|
|
9483
|
+
hasTextDeltaCallback: Boolean(context.onTextDelta),
|
|
9484
|
+
shouldTrace,
|
|
9485
|
+
spanContext,
|
|
9486
|
+
correlation: context.correlation,
|
|
9487
|
+
assistantUserName: context.assistant?.userName
|
|
9343
9488
|
});
|
|
9344
|
-
|
|
9345
|
-
if (
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
|
|
9489
|
+
} catch (error) {
|
|
9490
|
+
if (error instanceof McpAuthorizationPauseError && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
9491
|
+
const nextSliceId = await persistAuthPauseCheckpoint({
|
|
9492
|
+
conversationId: timeoutResumeConversationId,
|
|
9493
|
+
sessionId: timeoutResumeSessionId,
|
|
9494
|
+
currentSliceId: timeoutResumeSliceId,
|
|
9495
|
+
messages: timeoutResumeMessages,
|
|
9496
|
+
loadedSkillNames: loadedSkillNamesForResume,
|
|
9497
|
+
errorMessage: error.message,
|
|
9498
|
+
logContext: {
|
|
9499
|
+
threadId: context.correlation?.threadId,
|
|
9500
|
+
requesterId: context.correlation?.requesterId,
|
|
9501
|
+
channelId: context.correlation?.channelId,
|
|
9352
9502
|
runId: context.correlation?.runId,
|
|
9353
9503
|
assistantUserName: context.assistant?.userName,
|
|
9354
9504
|
modelId: botConfig.modelId
|
|
9355
|
-
},
|
|
9356
|
-
{
|
|
9357
|
-
"app.ai.tool_results": toolResults.length,
|
|
9358
|
-
"app.ai.tool_error_results": toolErrorCount,
|
|
9359
|
-
"app.ai.generated_files": generatedFiles.length
|
|
9360
|
-
},
|
|
9361
|
-
"Model returned empty text response"
|
|
9362
|
-
);
|
|
9363
|
-
}
|
|
9364
|
-
const lastAssistant = assistantMessages.at(-1);
|
|
9365
|
-
const stopReason = typeof lastAssistant?.stopReason === "string" ? lastAssistant.stopReason : void 0;
|
|
9366
|
-
const errorMessage = typeof lastAssistant?.errorMessage === "string" ? lastAssistant.errorMessage : void 0;
|
|
9367
|
-
const usedPrimaryText = Boolean(primaryText);
|
|
9368
|
-
const outcome = primaryText || oauthStartedMessage ? stopReason === "error" ? "provider_error" : "success" : "execution_failure";
|
|
9369
|
-
const fallbackText = oauthStartedMessage ?? buildExecutionFailureMessage(toolErrorCount);
|
|
9370
|
-
const responseText = primaryText || fallbackText;
|
|
9371
|
-
const escapedOrRawPayload = Boolean(primaryText) && (isExecutionEscapeResponse(primaryText) || isRawToolPayloadResponse(primaryText));
|
|
9372
|
-
const resolvedText = escapedOrRawPayload ? fallbackText : enforceAttachmentClaimTruth(responseText, replyFiles.length > 0);
|
|
9373
|
-
const resolvedOutcome = escapedOrRawPayload ? oauthStartedMessage ? outcome : "execution_failure" : outcome;
|
|
9374
|
-
if (shouldTrace) {
|
|
9375
|
-
logInfo(
|
|
9376
|
-
"agent_message_out",
|
|
9377
|
-
spanContext,
|
|
9378
|
-
{
|
|
9379
|
-
"app.message.kind": "assistant_outbound",
|
|
9380
|
-
"app.message.length": resolvedText.length,
|
|
9381
|
-
"app.message.output": summarizeMessageText(resolvedText),
|
|
9382
|
-
"app.ai.outcome": resolvedOutcome,
|
|
9383
|
-
"app.ai.assistant_messages": assistantMessages.length,
|
|
9384
|
-
...stopReason ? { "app.ai.stop_reason": stopReason } : {}
|
|
9385
|
-
},
|
|
9386
|
-
"Agent message sent"
|
|
9387
|
-
);
|
|
9388
|
-
}
|
|
9389
|
-
if (escapedOrRawPayload) {
|
|
9390
|
-
return {
|
|
9391
|
-
text: resolvedText,
|
|
9392
|
-
files: replyFiles.length > 0 ? replyFiles : void 0,
|
|
9393
|
-
artifactStatePatch: Object.keys(artifactStatePatch).length > 0 ? artifactStatePatch : void 0,
|
|
9394
|
-
deliveryPlan,
|
|
9395
|
-
deliveryMode,
|
|
9396
|
-
sandboxId: sandboxExecutor.getSandboxId(),
|
|
9397
|
-
sandboxDependencyProfileHash: sandboxExecutor.getDependencyProfileHash(),
|
|
9398
|
-
diagnostics: {
|
|
9399
|
-
outcome: "execution_failure",
|
|
9400
|
-
modelId: botConfig.modelId,
|
|
9401
|
-
assistantMessageCount: assistantMessages.length,
|
|
9402
|
-
toolCalls,
|
|
9403
|
-
toolResultCount: toolResults.length,
|
|
9404
|
-
toolErrorCount,
|
|
9405
|
-
usedPrimaryText,
|
|
9406
|
-
stopReason,
|
|
9407
|
-
errorMessage,
|
|
9408
|
-
providerError: void 0
|
|
9409
9505
|
}
|
|
9410
|
-
};
|
|
9411
|
-
}
|
|
9412
|
-
return {
|
|
9413
|
-
text: resolvedText,
|
|
9414
|
-
files: replyFiles.length > 0 ? replyFiles : void 0,
|
|
9415
|
-
artifactStatePatch: Object.keys(artifactStatePatch).length > 0 ? artifactStatePatch : void 0,
|
|
9416
|
-
deliveryPlan,
|
|
9417
|
-
deliveryMode,
|
|
9418
|
-
sandboxId: sandboxExecutor.getSandboxId(),
|
|
9419
|
-
sandboxDependencyProfileHash: sandboxExecutor.getDependencyProfileHash(),
|
|
9420
|
-
diagnostics: {
|
|
9421
|
-
outcome,
|
|
9422
|
-
modelId: botConfig.modelId,
|
|
9423
|
-
assistantMessageCount: assistantMessages.length,
|
|
9424
|
-
toolCalls,
|
|
9425
|
-
toolResultCount: toolResults.length,
|
|
9426
|
-
toolErrorCount,
|
|
9427
|
-
usedPrimaryText,
|
|
9428
|
-
stopReason,
|
|
9429
|
-
errorMessage,
|
|
9430
|
-
providerError: void 0
|
|
9431
|
-
}
|
|
9432
|
-
};
|
|
9433
|
-
} catch (error) {
|
|
9434
|
-
if (error instanceof McpAuthorizationPauseError && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
9435
|
-
const nextSliceId = timeoutResumeSliceId + 1;
|
|
9436
|
-
try {
|
|
9437
|
-
const latestCheckpoint = await getAgentTurnSessionCheckpoint(
|
|
9438
|
-
timeoutResumeConversationId,
|
|
9439
|
-
timeoutResumeSessionId
|
|
9440
|
-
);
|
|
9441
|
-
const piMessages = trimTrailingAssistantMessages(
|
|
9442
|
-
timeoutResumeMessages.length > 0 ? timeoutResumeMessages : latestCheckpoint?.piMessages ?? []
|
|
9443
|
-
);
|
|
9444
|
-
await upsertAgentTurnSessionCheckpoint({
|
|
9445
|
-
conversationId: timeoutResumeConversationId,
|
|
9446
|
-
sessionId: timeoutResumeSessionId,
|
|
9447
|
-
sliceId: nextSliceId,
|
|
9448
|
-
state: "awaiting_resume",
|
|
9449
|
-
piMessages,
|
|
9450
|
-
loadedSkillNames: loadedSkillNamesForResume,
|
|
9451
|
-
resumeReason: "auth",
|
|
9452
|
-
resumedFromSliceId: timeoutResumeSliceId,
|
|
9453
|
-
errorMessage: error.message
|
|
9454
|
-
});
|
|
9455
|
-
} catch (checkpointError) {
|
|
9456
|
-
logException(
|
|
9457
|
-
checkpointError,
|
|
9458
|
-
"agent_turn_auth_resume_checkpoint_failed",
|
|
9459
|
-
{
|
|
9460
|
-
slackThreadId: context.correlation?.threadId,
|
|
9461
|
-
slackUserId: context.correlation?.requesterId,
|
|
9462
|
-
slackChannelId: context.correlation?.channelId,
|
|
9463
|
-
runId: context.correlation?.runId,
|
|
9464
|
-
assistantUserName: context.assistant?.userName,
|
|
9465
|
-
modelId: botConfig.modelId
|
|
9466
|
-
},
|
|
9467
|
-
{
|
|
9468
|
-
"app.ai.resume_conversation_id": timeoutResumeConversationId,
|
|
9469
|
-
"app.ai.resume_session_id": timeoutResumeSessionId,
|
|
9470
|
-
"app.ai.resume_from_slice_id": timeoutResumeSliceId,
|
|
9471
|
-
"app.ai.resume_next_slice_id": nextSliceId
|
|
9472
|
-
},
|
|
9473
|
-
"Failed to persist auth checkpoint before retry"
|
|
9474
|
-
);
|
|
9475
|
-
}
|
|
9506
|
+
});
|
|
9476
9507
|
throw new RetryableTurnError(
|
|
9477
9508
|
"mcp_auth_resume",
|
|
9478
9509
|
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${nextSliceId}`
|
|
@@ -11975,7 +12006,11 @@ function createReplyToThread(deps) {
|
|
|
11975
12006
|
"app.ai.tool_error_results": reply.diagnostics.toolErrorCount,
|
|
11976
12007
|
"app.ai.tool_call_count": reply.diagnostics.toolCalls.length,
|
|
11977
12008
|
"app.ai.used_primary_text": reply.diagnostics.usedPrimaryText,
|
|
11978
|
-
...reply.diagnostics.stopReason ? {
|
|
12009
|
+
...reply.diagnostics.stopReason ? {
|
|
12010
|
+
"gen_ai.response.finish_reasons": [
|
|
12011
|
+
reply.diagnostics.stopReason
|
|
12012
|
+
]
|
|
12013
|
+
} : {},
|
|
11979
12014
|
...reply.diagnostics.errorMessage ? { "error.message": reply.diagnostics.errorMessage } : {}
|
|
11980
12015
|
};
|
|
11981
12016
|
setSpanAttributes(diagnosticsAttributes);
|
|
@@ -12973,21 +13008,21 @@ async function createApp(options) {
|
|
|
12973
13008
|
options?.pluginPackages ?? await resolveBuildPluginPackages()
|
|
12974
13009
|
);
|
|
12975
13010
|
const waitUntil = options?.waitUntil ?? await defaultWaitUntil();
|
|
12976
|
-
const app = new Hono()
|
|
13011
|
+
const app = new Hono();
|
|
12977
13012
|
app.onError((err, c) => {
|
|
12978
13013
|
logException(err, "unhandled_route_error");
|
|
12979
13014
|
return c.text("Internal Server Error", 500);
|
|
12980
13015
|
});
|
|
13016
|
+
app.get("/", () => GET3());
|
|
12981
13017
|
app.get("/health", () => GET2());
|
|
12982
|
-
app.get("/
|
|
12983
|
-
app.get("/
|
|
12984
|
-
app.get("/oauth/callback/mcp/:provider", (c) => {
|
|
13018
|
+
app.get("/api/info", () => GET());
|
|
13019
|
+
app.get("/api/oauth/callback/mcp/:provider", (c) => {
|
|
12985
13020
|
return GET4(c.req.raw, c.req.param("provider"), waitUntil);
|
|
12986
13021
|
});
|
|
12987
|
-
app.get("/oauth/callback/:provider", (c) => {
|
|
13022
|
+
app.get("/api/oauth/callback/:provider", (c) => {
|
|
12988
13023
|
return GET5(c.req.raw, c.req.param("provider"), waitUntil);
|
|
12989
13024
|
});
|
|
12990
|
-
app.post("/webhooks/:platform", (c) => {
|
|
13025
|
+
app.post("/api/webhooks/:platform", (c) => {
|
|
12991
13026
|
return POST(c.req.raw, c.req.param("platform"), waitUntil);
|
|
12992
13027
|
});
|
|
12993
13028
|
return app;
|