@sentry/junior 0.34.0 → 0.36.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +852 -676
- package/dist/{chunk-HZIJ4BSE.js → chunk-ERH4OYNB.js} +32 -1
- package/dist/cli/snapshot-warmup.js +1 -1
- package/package.json +22 -22
package/dist/app.js
CHANGED
|
@@ -30,13 +30,14 @@ import {
|
|
|
30
30
|
runNonInteractiveCommand,
|
|
31
31
|
sandboxSkillDir,
|
|
32
32
|
sandboxSkillFile
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ERH4OYNB.js";
|
|
34
34
|
import {
|
|
35
35
|
CredentialUnavailableError,
|
|
36
36
|
buildOAuthTokenRequest,
|
|
37
37
|
createChatSdkLogger,
|
|
38
38
|
createPluginBroker,
|
|
39
39
|
createRequestContext,
|
|
40
|
+
extractGenAiUsageAttributes,
|
|
40
41
|
extractGenAiUsageSummary,
|
|
41
42
|
getActiveTraceId,
|
|
42
43
|
getPluginCapabilityProviders,
|
|
@@ -401,6 +402,7 @@ function defaultConversationState() {
|
|
|
401
402
|
return {
|
|
402
403
|
schemaVersion: 1,
|
|
403
404
|
messages: [],
|
|
405
|
+
piMessages: [],
|
|
404
406
|
compactions: [],
|
|
405
407
|
backfill: {},
|
|
406
408
|
processing: {},
|
|
@@ -512,6 +514,7 @@ function coerceThreadConversationState(value) {
|
|
|
512
514
|
return {
|
|
513
515
|
schemaVersion: 1,
|
|
514
516
|
messages,
|
|
517
|
+
piMessages: Array.isArray(rawConversation.piMessages) ? rawConversation.piMessages : [],
|
|
515
518
|
compactions,
|
|
516
519
|
backfill,
|
|
517
520
|
processing,
|
|
@@ -2030,20 +2033,6 @@ function getChannelConfigurationServiceById(channelId) {
|
|
|
2030
2033
|
});
|
|
2031
2034
|
}
|
|
2032
2035
|
|
|
2033
|
-
// src/chat/runtime/thread-participants.ts
|
|
2034
|
-
function buildThreadParticipants(messages) {
|
|
2035
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2036
|
-
const participants = [];
|
|
2037
|
-
for (const message of messages) {
|
|
2038
|
-
const { userId, userName, fullName } = message.author ?? {};
|
|
2039
|
-
if (!userId || message.author?.isBot) continue;
|
|
2040
|
-
if (seen.has(userId)) continue;
|
|
2041
|
-
seen.add(userId);
|
|
2042
|
-
participants.push({ userId, userName, fullName });
|
|
2043
|
-
}
|
|
2044
|
-
return participants;
|
|
2045
|
-
}
|
|
2046
|
-
|
|
2047
2036
|
// src/chat/state/turn-id.ts
|
|
2048
2037
|
function buildDeterministicTurnId(messageId) {
|
|
2049
2038
|
const sanitized = messageId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
@@ -2512,7 +2501,7 @@ function getConversationMessageSlackTs(message) {
|
|
|
2512
2501
|
}
|
|
2513
2502
|
|
|
2514
2503
|
// src/chat/respond.ts
|
|
2515
|
-
import { Agent } from "@mariozechner/pi-agent-core";
|
|
2504
|
+
import { Agent as Agent2 } from "@mariozechner/pi-agent-core";
|
|
2516
2505
|
|
|
2517
2506
|
// src/chat/prompt.ts
|
|
2518
2507
|
import fs from "fs";
|
|
@@ -2902,12 +2891,12 @@ function formatConfigurationValue(value) {
|
|
|
2902
2891
|
return escapeXml(String(value));
|
|
2903
2892
|
}
|
|
2904
2893
|
}
|
|
2905
|
-
function
|
|
2894
|
+
function renderRequesterBlock(fields) {
|
|
2906
2895
|
const lines = Object.entries(fields).filter(([, value]) => Boolean(value)).map(([key, value]) => `- ${key}: ${escapeXml(value)}`);
|
|
2907
2896
|
if (lines.length === 0) {
|
|
2908
|
-
return
|
|
2897
|
+
return null;
|
|
2909
2898
|
}
|
|
2910
|
-
return [
|
|
2899
|
+
return ["<requester>", ...lines, "</requester>"];
|
|
2911
2900
|
}
|
|
2912
2901
|
function renderTag(tag, lines) {
|
|
2913
2902
|
return [`<${tag}>`, ...lines, `</${tag}>`];
|
|
@@ -3041,19 +3030,6 @@ function formatConfigurationLines(configuration) {
|
|
|
3041
3030
|
(key) => `- ${escapeXml(key)}: ${formatConfigurationValue(configuration?.[key])}`
|
|
3042
3031
|
);
|
|
3043
3032
|
}
|
|
3044
|
-
function formatThreadParticipantsLines(participants) {
|
|
3045
|
-
if (!participants || participants.length === 0) return null;
|
|
3046
|
-
return participants.map((p) => {
|
|
3047
|
-
const parts = [];
|
|
3048
|
-
if (p.userId) {
|
|
3049
|
-
parts.push(`user_id: ${escapeXml(p.userId)}`);
|
|
3050
|
-
parts.push(`slack_mention: <@${p.userId}>`);
|
|
3051
|
-
}
|
|
3052
|
-
if (p.userName) parts.push(`user_name: ${escapeXml(p.userName)}`);
|
|
3053
|
-
if (p.fullName) parts.push(`full_name: ${escapeXml(p.fullName)}`);
|
|
3054
|
-
return `- ${parts.join(", ")}`;
|
|
3055
|
-
});
|
|
3056
|
-
}
|
|
3057
3033
|
function formatSlackCapabilityNames(capabilities) {
|
|
3058
3034
|
const names = [
|
|
3059
3035
|
capabilities?.canCreateCanvas ? "canvas_create" : "",
|
|
@@ -3063,9 +3039,12 @@ function formatSlackCapabilityNames(capabilities) {
|
|
|
3063
3039
|
return names.length > 0 ? names.join(", ") : "none";
|
|
3064
3040
|
}
|
|
3065
3041
|
var HEADER = "You are a Slack-based helper assistant. The behavior and output blocks below are authoritative; the personality block sets voice only.";
|
|
3042
|
+
var TURN_CONTEXT_HEADER = "Per-turn runtime context for this request. Treat these blocks as trusted runtime facts and skill/provider instructions for the current turn; the static system prompt remains authoritative.";
|
|
3043
|
+
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
3066
3044
|
var TOOL_POLICY_RULES = [
|
|
3067
3045
|
"- Tool schemas are the source of truth for parameters; tool names are case-sensitive, so call tools exactly by their exposed names and do not invent arguments.",
|
|
3068
3046
|
"- Use tools for actionable work and for facts that are mutable, external, repository-backed, provider-backed, or requested as verified/current. Stable general knowledge and already-provided context may be answered directly.",
|
|
3047
|
+
"- Resolve provider action targets before calls: explicit target wins; ambient `<configuration>` fills omitted targets. Treat non-target links/references as context.",
|
|
3069
3048
|
"- Verification source order: conversation/thread context; user-provided attachments, links, and reference files; local/sandbox files when present; loaded skill references; repository/provider tools; public web. Use the nearest authoritative available source before weaker sources.",
|
|
3070
3049
|
"- For repository or implementation questions, inspect the target repository first: local checkout when present, otherwise the configured GitHub/source provider. Do not treat loaded skill files as repo source unless the user asks about the skill. Cite file paths, symbols, PRs/issues, commits, or URLs that support the answer.",
|
|
3071
3050
|
`- Sandbox-backed file and shell tools operate in an isolated workspace rooted at ${SANDBOX_WORKSPACE_ROOT}; readFile/writeFile paths are sandbox-workspace paths, bash runs inside that workspace, and attachFile accepts absolute or workspace-relative sandbox paths.`,
|
|
@@ -3145,6 +3124,12 @@ function buildOutputSection() {
|
|
|
3145
3124
|
"</output>"
|
|
3146
3125
|
].join("\n");
|
|
3147
3126
|
}
|
|
3127
|
+
function buildIdentitySection() {
|
|
3128
|
+
return renderTagBlock(
|
|
3129
|
+
"identity",
|
|
3130
|
+
`Your Slack username is \`${escapeXml(botConfig.userName)}\`.`
|
|
3131
|
+
);
|
|
3132
|
+
}
|
|
3148
3133
|
function buildRuntimeSection(params) {
|
|
3149
3134
|
const lines = [
|
|
3150
3135
|
`- version: ${escapeXml(getRuntimeMetadata().version ?? "unknown")}`,
|
|
@@ -3173,29 +3158,13 @@ function buildContextSection(params) {
|
|
|
3173
3158
|
])
|
|
3174
3159
|
);
|
|
3175
3160
|
}
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
)
|
|
3182
|
-
|
|
3183
|
-
renderIdentityBlock("requester", {
|
|
3184
|
-
full_name: params.requester?.fullName,
|
|
3185
|
-
user_name: params.requester?.userName,
|
|
3186
|
-
user_id: params.requester?.userId
|
|
3187
|
-
})
|
|
3188
|
-
);
|
|
3189
|
-
const participantLines = formatThreadParticipantsLines(
|
|
3190
|
-
params.threadParticipants
|
|
3191
|
-
);
|
|
3192
|
-
if (participantLines) {
|
|
3193
|
-
blocks.push(
|
|
3194
|
-
renderTag("thread-participants", [
|
|
3195
|
-
"Known participants. When you mention one of these people, use the provided `<@USERID>` token exactly; do not write a bare `@name`.",
|
|
3196
|
-
...participantLines
|
|
3197
|
-
])
|
|
3198
|
-
);
|
|
3161
|
+
const requesterLines = renderRequesterBlock({
|
|
3162
|
+
full_name: params.requester?.fullName,
|
|
3163
|
+
user_name: params.requester?.userName,
|
|
3164
|
+
user_id: params.requester?.userId
|
|
3165
|
+
});
|
|
3166
|
+
if (requesterLines) {
|
|
3167
|
+
blocks.push(requesterLines);
|
|
3199
3168
|
}
|
|
3200
3169
|
const artifactLines = formatArtifactsLines(params.artifactState);
|
|
3201
3170
|
if (artifactLines) {
|
|
@@ -3205,7 +3174,7 @@ function buildContextSection(params) {
|
|
|
3205
3174
|
if (configLines) {
|
|
3206
3175
|
blocks.push(
|
|
3207
3176
|
renderTag("configuration", [
|
|
3208
|
-
"
|
|
3177
|
+
"Ambient provider defaults; explicit targets win.",
|
|
3209
3178
|
...configLines
|
|
3210
3179
|
])
|
|
3211
3180
|
);
|
|
@@ -3240,27 +3209,35 @@ function buildCapabilitiesSection(params) {
|
|
|
3240
3209
|
}
|
|
3241
3210
|
return renderTagBlock("capabilities", blocks.join("\n\n"));
|
|
3242
3211
|
}
|
|
3243
|
-
|
|
3212
|
+
var STATIC_SYSTEM_PROMPT = [
|
|
3213
|
+
HEADER,
|
|
3214
|
+
buildIdentitySection(),
|
|
3215
|
+
renderTagBlock("personality", JUNIOR_PERSONALITY.trim()),
|
|
3216
|
+
renderTagBlock("behavior", buildBehaviorSection()),
|
|
3217
|
+
buildOutputSection()
|
|
3218
|
+
].join("\n\n");
|
|
3219
|
+
function buildSystemPrompt() {
|
|
3220
|
+
return STATIC_SYSTEM_PROMPT;
|
|
3221
|
+
}
|
|
3222
|
+
function buildTurnContextPrompt(params) {
|
|
3244
3223
|
const sections = [
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
buildOutputSection(),
|
|
3224
|
+
`<${TURN_CONTEXT_TAG}>`,
|
|
3225
|
+
TURN_CONTEXT_HEADER,
|
|
3226
|
+
params.turnState === "resumed" ? "Continue the pending turn from prior conversation history; this block is not a new user request." : "The current user instruction appears after this block in the same message.",
|
|
3249
3227
|
buildCapabilitiesSection({
|
|
3250
3228
|
availableSkills: params.availableSkills,
|
|
3251
3229
|
activeSkills: params.activeSkills,
|
|
3252
3230
|
activeMcpCatalogs: params.activeMcpCatalogs ?? []
|
|
3253
3231
|
}),
|
|
3254
3232
|
buildContextSection({
|
|
3255
|
-
assistant: params.assistant,
|
|
3256
3233
|
requester: params.requester,
|
|
3257
3234
|
artifactState: params.artifactState,
|
|
3258
3235
|
configuration: params.configuration,
|
|
3259
|
-
threadParticipants: params.threadParticipants,
|
|
3260
3236
|
invocation: params.invocation,
|
|
3261
3237
|
turnState: params.turnState
|
|
3262
3238
|
}),
|
|
3263
|
-
buildRuntimeSection(params.runtime ?? {})
|
|
3239
|
+
buildRuntimeSection(params.runtime ?? {}),
|
|
3240
|
+
`</${TURN_CONTEXT_TAG}>`
|
|
3264
3241
|
];
|
|
3265
3242
|
return sections.join("\n\n");
|
|
3266
3243
|
}
|
|
@@ -6414,110 +6391,527 @@ function createSystemTimeTool() {
|
|
|
6414
6391
|
});
|
|
6415
6392
|
}
|
|
6416
6393
|
|
|
6417
|
-
// src/chat/tools/
|
|
6394
|
+
// src/chat/tools/advisor/tool.ts
|
|
6395
|
+
import {
|
|
6396
|
+
Agent
|
|
6397
|
+
} from "@mariozechner/pi-agent-core";
|
|
6418
6398
|
import { Type as Type15 } from "@sinclair/typebox";
|
|
6419
6399
|
|
|
6420
|
-
// src/chat/
|
|
6421
|
-
var
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
function
|
|
6434
|
-
|
|
6435
|
-
|
|
6436
|
-
|
|
6400
|
+
// src/chat/respond-helpers.ts
|
|
6401
|
+
var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
|
|
6402
|
+
function getSessionIdentifiers(context) {
|
|
6403
|
+
return {
|
|
6404
|
+
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId,
|
|
6405
|
+
sessionId: context.correlation?.turnId
|
|
6406
|
+
};
|
|
6407
|
+
}
|
|
6408
|
+
function isExecutionDeferralResponse(text) {
|
|
6409
|
+
return /\b(want me to proceed|do you want me to proceed|shall i proceed|can i proceed|should i proceed|let me do that now|give me a moment|tag me again|fresh invocation)\b/i.test(
|
|
6410
|
+
text
|
|
6411
|
+
);
|
|
6412
|
+
}
|
|
6413
|
+
function isToolAccessDisclaimerResponse(text) {
|
|
6414
|
+
return /\b(i (don't|do not) have access to (active )?tool|tool results came back empty|prior results .* empty|cannot access .*tool|need to (run|load) .*tool .* first)\b/i.test(
|
|
6415
|
+
text
|
|
6416
|
+
);
|
|
6417
|
+
}
|
|
6418
|
+
function isExecutionEscapeResponse(text) {
|
|
6419
|
+
const trimmed = text.trim();
|
|
6420
|
+
if (!trimmed) return false;
|
|
6421
|
+
return isExecutionDeferralResponse(trimmed) || isToolAccessDisclaimerResponse(trimmed);
|
|
6422
|
+
}
|
|
6423
|
+
function parseJsonCandidate(text) {
|
|
6424
|
+
const trimmed = text.trim();
|
|
6425
|
+
if (!trimmed) return void 0;
|
|
6426
|
+
try {
|
|
6427
|
+
return JSON.parse(trimmed);
|
|
6428
|
+
} catch {
|
|
6429
|
+
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
6430
|
+
if (!fenced) return void 0;
|
|
6431
|
+
try {
|
|
6432
|
+
return JSON.parse(fenced[1]);
|
|
6433
|
+
} catch {
|
|
6434
|
+
return void 0;
|
|
6435
|
+
}
|
|
6437
6436
|
}
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
if (
|
|
6441
|
-
|
|
6442
|
-
|
|
6437
|
+
}
|
|
6438
|
+
function isToolPayloadShape(payload) {
|
|
6439
|
+
if (!payload || typeof payload !== "object") return false;
|
|
6440
|
+
const record = payload;
|
|
6441
|
+
const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
|
|
6442
|
+
if (type.startsWith("tool-")) return true;
|
|
6443
|
+
if (type === "tool_use" || type === "tool_call" || type === "tool_result" || type === "tool_error")
|
|
6444
|
+
return true;
|
|
6445
|
+
const hasToolName = typeof record.toolName === "string" || typeof record.name === "string";
|
|
6446
|
+
const hasToolInput = Object.prototype.hasOwnProperty.call(record, "input") || Object.prototype.hasOwnProperty.call(record, "args");
|
|
6447
|
+
if (hasToolName && hasToolInput) return true;
|
|
6443
6448
|
return false;
|
|
6444
6449
|
}
|
|
6445
|
-
function
|
|
6446
|
-
|
|
6447
|
-
|
|
6450
|
+
function isRawToolPayloadResponse(text) {
|
|
6451
|
+
const parsed = parseJsonCandidate(text);
|
|
6452
|
+
if (Array.isArray(parsed)) {
|
|
6453
|
+
return parsed.some((entry) => isToolPayloadShape(entry));
|
|
6448
6454
|
}
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
return void 0;
|
|
6455
|
+
if (isToolPayloadShape(parsed)) {
|
|
6456
|
+
return true;
|
|
6452
6457
|
}
|
|
6453
|
-
const
|
|
6454
|
-
|
|
6455
|
-
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
6458
|
+
const compact = text.replace(/\s+/g, " ");
|
|
6459
|
+
return /"type"\s*:\s*"tool[-_](use|call|result|error)"/i.test(compact);
|
|
6456
6460
|
}
|
|
6457
|
-
function
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
+
function toObservablePromptPart(part) {
|
|
6462
|
+
if (part.type === "text") {
|
|
6463
|
+
return {
|
|
6464
|
+
type: "text",
|
|
6465
|
+
text: part.text
|
|
6466
|
+
};
|
|
6461
6467
|
}
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6468
|
+
return {
|
|
6469
|
+
type: "image",
|
|
6470
|
+
mimeType: part.mimeType,
|
|
6471
|
+
data: `[omitted:${part.data.length}]`
|
|
6472
|
+
};
|
|
6473
|
+
}
|
|
6474
|
+
function summarizeMessageText(text) {
|
|
6475
|
+
const normalized = text.trim().replace(/\s+/g, " ");
|
|
6476
|
+
if (!normalized) {
|
|
6477
|
+
return "[empty]";
|
|
6467
6478
|
}
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6479
|
+
return normalized.length > 1200 ? `${normalized.slice(0, 1200)}...` : normalized;
|
|
6480
|
+
}
|
|
6481
|
+
function buildUserTurnText(userInput, conversationContext, metadata) {
|
|
6482
|
+
const trimmedContext = conversationContext?.trim();
|
|
6483
|
+
const conversationId = metadata?.sessionContext?.conversationId;
|
|
6484
|
+
const traceId = metadata?.turnContext?.traceId;
|
|
6485
|
+
if (!trimmedContext && !conversationId && !traceId) {
|
|
6486
|
+
return userInput;
|
|
6474
6487
|
}
|
|
6475
|
-
|
|
6488
|
+
const sections = [];
|
|
6489
|
+
if (trimmedContext) {
|
|
6490
|
+
sections.push(
|
|
6491
|
+
"<thread-background>",
|
|
6492
|
+
trimmedContext,
|
|
6493
|
+
"</thread-background>",
|
|
6494
|
+
""
|
|
6495
|
+
);
|
|
6496
|
+
}
|
|
6497
|
+
if (conversationId) {
|
|
6498
|
+
sections.push(
|
|
6499
|
+
"<session-context>",
|
|
6500
|
+
`- gen_ai.conversation.id: ${conversationId}`,
|
|
6501
|
+
"</session-context>",
|
|
6502
|
+
""
|
|
6503
|
+
);
|
|
6504
|
+
}
|
|
6505
|
+
if (traceId) {
|
|
6506
|
+
sections.push(
|
|
6507
|
+
"<turn-context>",
|
|
6508
|
+
`- trace_id: ${traceId}`,
|
|
6509
|
+
"</turn-context>",
|
|
6510
|
+
""
|
|
6511
|
+
);
|
|
6512
|
+
}
|
|
6513
|
+
sections.push(
|
|
6514
|
+
'<current-instruction priority="highest">',
|
|
6515
|
+
userInput,
|
|
6516
|
+
"</current-instruction>"
|
|
6517
|
+
);
|
|
6518
|
+
return sections.join("\n");
|
|
6476
6519
|
}
|
|
6477
|
-
function
|
|
6478
|
-
const
|
|
6479
|
-
|
|
6480
|
-
|
|
6520
|
+
function encodeNonImageAttachmentForPrompt(attachment) {
|
|
6521
|
+
const base64 = attachment.data.toString("base64");
|
|
6522
|
+
const wasTruncated = base64.length > MAX_INLINE_ATTACHMENT_BASE64_CHARS;
|
|
6523
|
+
const encodedPayload = wasTruncated ? `${base64.slice(0, MAX_INLINE_ATTACHMENT_BASE64_CHARS)}...` : base64;
|
|
6524
|
+
return [
|
|
6525
|
+
"<attachment>",
|
|
6526
|
+
`filename: ${attachment.filename ?? "unnamed"}`,
|
|
6527
|
+
`media_type: ${attachment.mediaType}`,
|
|
6528
|
+
"encoding: base64",
|
|
6529
|
+
`truncated: ${wasTruncated ? "true" : "false"}`,
|
|
6530
|
+
"<data_base64>",
|
|
6531
|
+
encodedPayload,
|
|
6532
|
+
"</data_base64>",
|
|
6533
|
+
"</attachment>"
|
|
6534
|
+
].join("\n");
|
|
6535
|
+
}
|
|
6536
|
+
function buildExecutionFailureMessage(toolErrorCount) {
|
|
6537
|
+
if (toolErrorCount > 0) {
|
|
6538
|
+
return "I couldn't complete this because one or more required tools failed in this turn. I've logged the failure details.";
|
|
6481
6539
|
}
|
|
6482
|
-
return
|
|
6540
|
+
return "I couldn't complete this request in this turn due to an execution failure. I've logged the details for debugging.";
|
|
6483
6541
|
}
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6542
|
+
function isToolResultMessage(value) {
|
|
6543
|
+
return typeof value === "object" && value !== null && value.role === "toolResult";
|
|
6544
|
+
}
|
|
6545
|
+
function normalizeToolNameFromResult(result) {
|
|
6546
|
+
if (!result || typeof result !== "object") return void 0;
|
|
6547
|
+
const record = result;
|
|
6548
|
+
if (typeof record.toolName === "string" && record.toolName.length > 0) {
|
|
6549
|
+
return record.toolName;
|
|
6488
6550
|
}
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
const family = record.family === 6 ? 6 : 4;
|
|
6492
|
-
if (family === 4 && isPrivateIpv4(record.address)) {
|
|
6493
|
-
throw new Error("Resolved to a private IPv4 address");
|
|
6494
|
-
}
|
|
6495
|
-
if (family === 6 && isPrivateIpv6(record.address)) {
|
|
6496
|
-
throw new Error("Resolved to a private IPv6 address");
|
|
6497
|
-
}
|
|
6498
|
-
deduped.set(`${family}:${record.address}`, {
|
|
6499
|
-
address: record.address,
|
|
6500
|
-
family
|
|
6501
|
-
});
|
|
6551
|
+
if (typeof record.name === "string" && record.name.length > 0) {
|
|
6552
|
+
return record.name;
|
|
6502
6553
|
}
|
|
6503
|
-
return
|
|
6554
|
+
return void 0;
|
|
6504
6555
|
}
|
|
6505
|
-
|
|
6506
|
-
|
|
6507
|
-
|
|
6556
|
+
function isToolResultError(result) {
|
|
6557
|
+
if (!result || typeof result !== "object") return false;
|
|
6558
|
+
return Boolean(result.isError);
|
|
6559
|
+
}
|
|
6560
|
+
function isAssistantMessage(value) {
|
|
6561
|
+
return typeof value === "object" && value !== null && value.role === "assistant";
|
|
6562
|
+
}
|
|
6563
|
+
function getPiMessageRole(value) {
|
|
6564
|
+
if (!value || typeof value !== "object") {
|
|
6508
6565
|
return void 0;
|
|
6509
6566
|
}
|
|
6510
|
-
|
|
6567
|
+
const role = value.role;
|
|
6568
|
+
return typeof role === "string" ? role : void 0;
|
|
6511
6569
|
}
|
|
6512
|
-
function
|
|
6513
|
-
const
|
|
6514
|
-
return (
|
|
6515
|
-
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6570
|
+
function extractAssistantText(message) {
|
|
6571
|
+
const content = message.content ?? [];
|
|
6572
|
+
return content.filter(
|
|
6573
|
+
(part) => part.type === "text" && typeof part.text === "string"
|
|
6574
|
+
).map((part) => part.text).join("\n");
|
|
6575
|
+
}
|
|
6576
|
+
function getTerminalAssistantMessages(messages) {
|
|
6577
|
+
let lastToolResultIndex = -1;
|
|
6578
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
6579
|
+
if (isToolResultMessage(messages[index])) {
|
|
6580
|
+
lastToolResultIndex = index;
|
|
6581
|
+
break;
|
|
6582
|
+
}
|
|
6583
|
+
}
|
|
6584
|
+
return messages.slice(lastToolResultIndex + 1).filter(isAssistantMessage);
|
|
6585
|
+
}
|
|
6586
|
+
function upsertActiveSkill(activeSkills, next) {
|
|
6587
|
+
const existing = activeSkills.find((skill) => skill.name === next.name);
|
|
6588
|
+
if (existing) {
|
|
6589
|
+
existing.body = next.body;
|
|
6590
|
+
existing.description = next.description;
|
|
6591
|
+
existing.skillPath = next.skillPath;
|
|
6592
|
+
existing.allowedTools = next.allowedTools;
|
|
6593
|
+
existing.pluginProvider = next.pluginProvider;
|
|
6594
|
+
return;
|
|
6595
|
+
}
|
|
6596
|
+
activeSkills.push(next);
|
|
6597
|
+
}
|
|
6598
|
+
function trimTrailingAssistantMessages(messages) {
|
|
6599
|
+
let end = messages.length;
|
|
6600
|
+
while (end > 0 && getPiMessageRole(messages[end - 1]) === "assistant") {
|
|
6601
|
+
end -= 1;
|
|
6602
|
+
}
|
|
6603
|
+
return end === messages.length ? [...messages] : messages.slice(0, end);
|
|
6604
|
+
}
|
|
6605
|
+
|
|
6606
|
+
// src/chat/tools/advisor/session-store.ts
|
|
6607
|
+
import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS2 } from "chat";
|
|
6608
|
+
var ADVISOR_SESSION_TTL_MS = THREAD_STATE_TTL_MS2;
|
|
6609
|
+
function cloneMessages(messages) {
|
|
6610
|
+
return structuredClone(messages);
|
|
6611
|
+
}
|
|
6612
|
+
function getAdvisorSessionKey(conversationId) {
|
|
6613
|
+
return `junior:${conversationId}:advisor_session`;
|
|
6614
|
+
}
|
|
6615
|
+
function createStateAdvisorSessionStore() {
|
|
6616
|
+
return {
|
|
6617
|
+
load: async (conversationId) => {
|
|
6618
|
+
const stateAdapter = getStateAdapter();
|
|
6619
|
+
await stateAdapter.connect();
|
|
6620
|
+
const messages = await stateAdapter.get(
|
|
6621
|
+
getAdvisorSessionKey(conversationId)
|
|
6622
|
+
) ?? [];
|
|
6623
|
+
return cloneMessages(messages);
|
|
6624
|
+
},
|
|
6625
|
+
save: async (conversationId, messages) => {
|
|
6626
|
+
const stateAdapter = getStateAdapter();
|
|
6627
|
+
await stateAdapter.connect();
|
|
6628
|
+
await stateAdapter.set(
|
|
6629
|
+
getAdvisorSessionKey(conversationId),
|
|
6630
|
+
cloneMessages(messages),
|
|
6631
|
+
ADVISOR_SESSION_TTL_MS
|
|
6632
|
+
);
|
|
6633
|
+
}
|
|
6634
|
+
};
|
|
6635
|
+
}
|
|
6636
|
+
|
|
6637
|
+
// src/chat/tools/advisor/tool.ts
|
|
6638
|
+
var ADVISOR_ALLOWED_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
6639
|
+
"bash",
|
|
6640
|
+
"readFile",
|
|
6641
|
+
"searchMcpTools",
|
|
6642
|
+
"slackCanvasRead",
|
|
6643
|
+
"slackChannelListMessages",
|
|
6644
|
+
"slackListGetItems",
|
|
6645
|
+
"systemTime",
|
|
6646
|
+
"webFetch",
|
|
6647
|
+
"webSearch"
|
|
6648
|
+
]);
|
|
6649
|
+
var ADVISOR_TOOL_DESCRIPTION = "Ask a stronger advisor for deep technical guidance. Call this when the task has a hard reasoning core: algorithm design, architecture, concurrency, security-sensitive logic, data modeling, unclear requirements, repeated failures, difficult debugging, broad refactors, or final review of nontrivial work. Pass a focused question plus curated context containing the exact evidence, constraints, current plan, alternatives, command output, code snippets, or diffs the advisor should start from. The advisor does not automatically receive the parent transcript, keeps its own advisor history for this parent conversation, can use inspection tools to verify evidence, can reason deeply, and returns guidance for you to apply and verify. Follow-up calls can build on prior advisor guidance but must include any new evidence or changed constraints. Use it after initial orientation reads when repository context matters, before committing to a non-obvious implementation plan, when changing approach, when stuck, and before declaring complex work complete. Do not use it for greetings, simple deterministic edits, routine formatting, or tasks where the next action is already obvious from fresh tool output.";
|
|
6650
|
+
var ADVISOR_SYSTEM_PROMPT = [
|
|
6651
|
+
"You are a senior technical advisor for the executor.",
|
|
6652
|
+
"Analyze the executor-supplied context deeply. Use inspection tools when direct inspection or verification would materially improve the advice.",
|
|
6653
|
+
"Distinguish evidence from inference. Treat the advisor task as the focus for this call and the executor context as the starting evidence packet.",
|
|
6654
|
+
"Do not assume access to parent transcript or tool output that was not included or gathered in this advisor call.",
|
|
6655
|
+
"Do not make user-visible side effects, post Slack messages, or mutate files. If a mutating action is needed, recommend it to the executor instead.",
|
|
6656
|
+
"Identify the hard part, recommend a concrete plan or correction, call out blocking risks, and propose focused verification.",
|
|
6657
|
+
"If the supplied context is insufficient, say exactly what additional evidence the executor needs to gather before acting.",
|
|
6658
|
+
"Do not write user-facing prose.",
|
|
6659
|
+
"Use concise technical memo sections when helpful: Assessment, Recommended Plan, Risks, Verification, Stop Conditions."
|
|
6660
|
+
].join("\n");
|
|
6661
|
+
function lastAssistantMessage(messages) {
|
|
6662
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
6663
|
+
const message = messages[index];
|
|
6664
|
+
if (isAssistantMessage(message)) {
|
|
6665
|
+
return message;
|
|
6666
|
+
}
|
|
6667
|
+
}
|
|
6668
|
+
return void 0;
|
|
6669
|
+
}
|
|
6670
|
+
function failure(errorCode, text = `Advisor guidance is unavailable (${errorCode}). Continue only if the next step is clear from verified evidence.`) {
|
|
6671
|
+
return {
|
|
6672
|
+
content: [{ type: "text", text }],
|
|
6673
|
+
details: {
|
|
6674
|
+
ok: false,
|
|
6675
|
+
error_code: errorCode
|
|
6676
|
+
}
|
|
6677
|
+
};
|
|
6678
|
+
}
|
|
6679
|
+
function success(memo) {
|
|
6680
|
+
return {
|
|
6681
|
+
content: [{ type: "text", text: memo }],
|
|
6682
|
+
details: {
|
|
6683
|
+
ok: true
|
|
6684
|
+
}
|
|
6685
|
+
};
|
|
6686
|
+
}
|
|
6687
|
+
function isAdvisorToolAllowed(toolName) {
|
|
6688
|
+
return ADVISOR_ALLOWED_TOOL_NAMES.has(toolName);
|
|
6689
|
+
}
|
|
6690
|
+
function createAdvisorTool(context) {
|
|
6691
|
+
const store = context.store ?? createStateAdvisorSessionStore();
|
|
6692
|
+
const spanContext = context.logContext ?? {};
|
|
6693
|
+
return tool({
|
|
6694
|
+
description: ADVISOR_TOOL_DESCRIPTION,
|
|
6695
|
+
inputSchema: Type15.Object({
|
|
6696
|
+
question: Type15.String({
|
|
6697
|
+
minLength: 1,
|
|
6698
|
+
description: "Focused advisor question or decision point."
|
|
6699
|
+
}),
|
|
6700
|
+
context: Type15.String({
|
|
6701
|
+
minLength: 1,
|
|
6702
|
+
description: "Curated evidence packet: relevant requirements, constraints, current plan, alternatives, code snippets, diffs, command output, and open questions."
|
|
6703
|
+
})
|
|
6704
|
+
}),
|
|
6705
|
+
execute: async ({ question, context: suppliedContext }) => {
|
|
6706
|
+
if (typeof question !== "string" || !question.trim()) {
|
|
6707
|
+
return failure(
|
|
6708
|
+
"invalid_question",
|
|
6709
|
+
"Advisor guidance is unavailable because the question was empty or invalid. Ask a focused advisor question before retrying."
|
|
6710
|
+
);
|
|
6711
|
+
}
|
|
6712
|
+
const advisorQuestion = question.trim();
|
|
6713
|
+
if (typeof suppliedContext !== "string" || !suppliedContext.trim()) {
|
|
6714
|
+
return failure(
|
|
6715
|
+
"invalid_context",
|
|
6716
|
+
"Advisor guidance is unavailable because the curated context was empty or invalid. Include the relevant evidence and constraints before retrying."
|
|
6717
|
+
);
|
|
6718
|
+
}
|
|
6719
|
+
const advisorContext = suppliedContext.trim();
|
|
6720
|
+
if (!context.conversationId) {
|
|
6721
|
+
return failure(
|
|
6722
|
+
"missing_conversation_id",
|
|
6723
|
+
"Advisor guidance is unavailable because this turn has no parent conversation id. Continue without assuming advisor history."
|
|
6724
|
+
);
|
|
6725
|
+
}
|
|
6726
|
+
const conversationId = context.conversationId;
|
|
6727
|
+
return await withSpan(
|
|
6728
|
+
"ai.invoke_advisor",
|
|
6729
|
+
"gen_ai.invoke_agent",
|
|
6730
|
+
spanContext,
|
|
6731
|
+
async () => {
|
|
6732
|
+
const requestText = [
|
|
6733
|
+
"<advisor-task>",
|
|
6734
|
+
escapeXml(advisorQuestion),
|
|
6735
|
+
"</advisor-task>",
|
|
6736
|
+
"",
|
|
6737
|
+
"<executor-context>",
|
|
6738
|
+
escapeXml(advisorContext),
|
|
6739
|
+
"</executor-context>"
|
|
6740
|
+
].join("\n");
|
|
6741
|
+
const requestMessage = {
|
|
6742
|
+
role: "user",
|
|
6743
|
+
content: [{ type: "text", text: requestText }],
|
|
6744
|
+
timestamp: Date.now()
|
|
6745
|
+
};
|
|
6746
|
+
let advisorMessages;
|
|
6747
|
+
try {
|
|
6748
|
+
advisorMessages = await store.load(conversationId);
|
|
6749
|
+
} catch {
|
|
6750
|
+
setSpanStatus("error");
|
|
6751
|
+
return failure(
|
|
6752
|
+
"session_unavailable",
|
|
6753
|
+
"Advisor guidance is unavailable because advisor history could not be loaded. Continue without assuming advisor history."
|
|
6754
|
+
);
|
|
6755
|
+
}
|
|
6756
|
+
const advisorAgent = new Agent({
|
|
6757
|
+
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
6758
|
+
initialState: {
|
|
6759
|
+
systemPrompt: ADVISOR_SYSTEM_PROMPT,
|
|
6760
|
+
model: resolveGatewayModel(context.config.modelId),
|
|
6761
|
+
thinkingLevel: context.config.thinkingLevel,
|
|
6762
|
+
tools: context.getTools()
|
|
6763
|
+
},
|
|
6764
|
+
sessionId: getAdvisorSessionKey(conversationId),
|
|
6765
|
+
streamFn: context.streamFn
|
|
6766
|
+
});
|
|
6767
|
+
advisorAgent.state.messages = advisorMessages;
|
|
6768
|
+
const beforeMessageCount = advisorAgent.state.messages.length;
|
|
6769
|
+
try {
|
|
6770
|
+
await advisorAgent.prompt(requestMessage);
|
|
6771
|
+
} catch {
|
|
6772
|
+
setSpanStatus("error");
|
|
6773
|
+
return failure(
|
|
6774
|
+
"unavailable",
|
|
6775
|
+
"Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
|
|
6776
|
+
);
|
|
6777
|
+
}
|
|
6778
|
+
const assistant = lastAssistantMessage(advisorAgent.state.messages);
|
|
6779
|
+
const newAdvisorMessages = advisorAgent.state.messages.slice(beforeMessageCount);
|
|
6780
|
+
setSpanAttributes(extractGenAiUsageAttributes(...newAdvisorMessages));
|
|
6781
|
+
if (!assistant || assistant.stopReason === "error" || assistant.stopReason === "aborted") {
|
|
6782
|
+
setSpanStatus("error");
|
|
6783
|
+
return failure(
|
|
6784
|
+
"unavailable",
|
|
6785
|
+
"Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
|
|
6786
|
+
);
|
|
6787
|
+
}
|
|
6788
|
+
const memo = extractAssistantText(assistant);
|
|
6789
|
+
try {
|
|
6790
|
+
await store.save(conversationId, advisorAgent.state.messages);
|
|
6791
|
+
} catch {
|
|
6792
|
+
setSpanStatus("error");
|
|
6793
|
+
return failure(
|
|
6794
|
+
"session_unavailable",
|
|
6795
|
+
"Advisor guidance is unavailable because advisor history could not be saved. Retry the advisor call or continue without assuming advisor history."
|
|
6796
|
+
);
|
|
6797
|
+
}
|
|
6798
|
+
setSpanStatus("ok");
|
|
6799
|
+
return success(memo);
|
|
6800
|
+
},
|
|
6801
|
+
{
|
|
6802
|
+
"gen_ai.provider.name": "vercel-ai-gateway",
|
|
6803
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
6804
|
+
"gen_ai.request.model": context.config.modelId
|
|
6805
|
+
}
|
|
6806
|
+
);
|
|
6807
|
+
}
|
|
6808
|
+
});
|
|
6809
|
+
}
|
|
6810
|
+
|
|
6811
|
+
// src/chat/tools/web/fetch-tool.ts
|
|
6812
|
+
import { Type as Type16 } from "@sinclair/typebox";
|
|
6813
|
+
|
|
6814
|
+
// src/chat/tools/web/constants.ts
|
|
6815
|
+
var USER_AGENT = "junior-bot/0.1";
|
|
6816
|
+
var FETCH_TIMEOUT_MS = 8e3;
|
|
6817
|
+
var MAX_REDIRECTS = 3;
|
|
6818
|
+
var DEFAULT_MAX_CHARS = 6e3;
|
|
6819
|
+
var MAX_FETCH_CHARS = 12e3;
|
|
6820
|
+
var MAX_FETCH_BYTES = 256e3;
|
|
6821
|
+
|
|
6822
|
+
// src/chat/tools/web/network.ts
|
|
6823
|
+
import dns from "dns/promises";
|
|
6824
|
+
import http from "http";
|
|
6825
|
+
import https from "https";
|
|
6826
|
+
import net from "net";
|
|
6827
|
+
function isPrivateIpv4(ip) {
|
|
6828
|
+
const parts = ip.split(".").map((chunk) => Number.parseInt(chunk, 10));
|
|
6829
|
+
if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
|
|
6830
|
+
return true;
|
|
6831
|
+
}
|
|
6832
|
+
if (parts[0] === 10 || parts[0] === 127) return true;
|
|
6833
|
+
if (parts[0] === 169 && parts[1] === 254) return true;
|
|
6834
|
+
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) return true;
|
|
6835
|
+
if (parts[0] === 192 && parts[1] === 168) return true;
|
|
6836
|
+
if (parts[0] === 0) return true;
|
|
6837
|
+
return false;
|
|
6838
|
+
}
|
|
6839
|
+
function parseMappedIpv4FromIpv6(mapped) {
|
|
6840
|
+
if (net.isIP(mapped) === 4) {
|
|
6841
|
+
return mapped;
|
|
6842
|
+
}
|
|
6843
|
+
const hexMatch = mapped.match(/^([0-9a-f]{1,4}):([0-9a-f]{1,4})$/i);
|
|
6844
|
+
if (!hexMatch) {
|
|
6845
|
+
return void 0;
|
|
6846
|
+
}
|
|
6847
|
+
const high = Number.parseInt(hexMatch[1], 16);
|
|
6848
|
+
const low = Number.parseInt(hexMatch[2], 16);
|
|
6849
|
+
return `${high >> 8 & 255}.${high & 255}.${low >> 8 & 255}.${low & 255}`;
|
|
6850
|
+
}
|
|
6851
|
+
function isPrivateIpv6(ip) {
|
|
6852
|
+
const normalized = ip.toLowerCase();
|
|
6853
|
+
if (normalized === "::1" || normalized.startsWith("fc") || normalized.startsWith("fd")) {
|
|
6854
|
+
return true;
|
|
6855
|
+
}
|
|
6856
|
+
if (normalized.startsWith("fe")) {
|
|
6857
|
+
const third = normalized[2];
|
|
6858
|
+
if (third === "8" || third === "9" || third === "a" || third === "b") {
|
|
6859
|
+
return true;
|
|
6860
|
+
}
|
|
6861
|
+
}
|
|
6862
|
+
if (normalized.startsWith("::ffff:")) {
|
|
6863
|
+
const mapped = normalized.slice("::ffff:".length);
|
|
6864
|
+
const mappedIpv4 = parseMappedIpv4FromIpv6(mapped);
|
|
6865
|
+
if (mappedIpv4 && isPrivateIpv4(mappedIpv4)) {
|
|
6866
|
+
return true;
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
return false;
|
|
6870
|
+
}
|
|
6871
|
+
function normalizeHostname(hostname) {
|
|
6872
|
+
const lowered = hostname.toLowerCase();
|
|
6873
|
+
if (lowered.startsWith("[") && lowered.endsWith("]")) {
|
|
6874
|
+
return lowered.slice(1, -1);
|
|
6875
|
+
}
|
|
6876
|
+
return lowered;
|
|
6877
|
+
}
|
|
6878
|
+
async function resolvePublicHostname(hostname) {
|
|
6879
|
+
const records = await dns.lookup(hostname, { all: true, verbatim: true });
|
|
6880
|
+
if (records.length === 0) {
|
|
6881
|
+
throw new Error("Could not resolve hostname");
|
|
6882
|
+
}
|
|
6883
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
6884
|
+
for (const record of records) {
|
|
6885
|
+
const family = record.family === 6 ? 6 : 4;
|
|
6886
|
+
if (family === 4 && isPrivateIpv4(record.address)) {
|
|
6887
|
+
throw new Error("Resolved to a private IPv4 address");
|
|
6888
|
+
}
|
|
6889
|
+
if (family === 6 && isPrivateIpv6(record.address)) {
|
|
6890
|
+
throw new Error("Resolved to a private IPv6 address");
|
|
6891
|
+
}
|
|
6892
|
+
deduped.set(`${family}:${record.address}`, {
|
|
6893
|
+
address: record.address,
|
|
6894
|
+
family
|
|
6895
|
+
});
|
|
6896
|
+
}
|
|
6897
|
+
return [...deduped.values()];
|
|
6898
|
+
}
|
|
6899
|
+
async function resolvePinnedAddresses(url) {
|
|
6900
|
+
const hostname = normalizeHostname(url.hostname);
|
|
6901
|
+
if (net.isIP(hostname) !== 0) {
|
|
6902
|
+
return void 0;
|
|
6903
|
+
}
|
|
6904
|
+
return resolvePublicHostname(hostname);
|
|
6905
|
+
}
|
|
6906
|
+
function createPinnedLookup(resolved) {
|
|
6907
|
+
const fallback = resolved[0];
|
|
6908
|
+
return (_hostname, options, callback) => {
|
|
6909
|
+
if (options?.all) {
|
|
6910
|
+
callback(
|
|
6911
|
+
null,
|
|
6912
|
+
resolved.map((entry) => ({
|
|
6913
|
+
address: entry.address,
|
|
6914
|
+
family: entry.family
|
|
6521
6915
|
}))
|
|
6522
6916
|
);
|
|
6523
6917
|
return;
|
|
@@ -6760,13 +7154,13 @@ function extractHttpStatusFromMessage(message) {
|
|
|
6760
7154
|
function createWebFetchTool(hooks) {
|
|
6761
7155
|
return tool({
|
|
6762
7156
|
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.",
|
|
6763
|
-
inputSchema:
|
|
6764
|
-
url:
|
|
7157
|
+
inputSchema: Type16.Object({
|
|
7158
|
+
url: Type16.String({
|
|
6765
7159
|
minLength: 1,
|
|
6766
7160
|
description: "HTTP(S) URL to fetch."
|
|
6767
7161
|
}),
|
|
6768
|
-
max_chars:
|
|
6769
|
-
|
|
7162
|
+
max_chars: Type16.Optional(
|
|
7163
|
+
Type16.Integer({
|
|
6770
7164
|
minimum: 500,
|
|
6771
7165
|
maximum: MAX_FETCH_CHARS,
|
|
6772
7166
|
description: "Optional maximum number of extracted characters to return."
|
|
@@ -6826,7 +7220,7 @@ function createWebFetchTool(hooks) {
|
|
|
6826
7220
|
// src/chat/tools/web/search.ts
|
|
6827
7221
|
import { generateText } from "ai";
|
|
6828
7222
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
6829
|
-
import { Type as
|
|
7223
|
+
import { Type as Type17 } from "@sinclair/typebox";
|
|
6830
7224
|
var SEARCH_TIMEOUT_MS = 6e4;
|
|
6831
7225
|
var MAX_RESULTS2 = 5;
|
|
6832
7226
|
var DEFAULT_SEARCH_MODEL = "xai/grok-4-fast-reasoning";
|
|
@@ -6869,14 +7263,14 @@ function isAuthFailure(message) {
|
|
|
6869
7263
|
function createWebSearchTool() {
|
|
6870
7264
|
return tool({
|
|
6871
7265
|
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.",
|
|
6872
|
-
inputSchema:
|
|
6873
|
-
query:
|
|
7266
|
+
inputSchema: Type17.Object({
|
|
7267
|
+
query: Type17.String({
|
|
6874
7268
|
minLength: 1,
|
|
6875
7269
|
maxLength: 500,
|
|
6876
7270
|
description: "Search query."
|
|
6877
7271
|
}),
|
|
6878
|
-
max_results:
|
|
6879
|
-
|
|
7272
|
+
max_results: Type17.Optional(
|
|
7273
|
+
Type17.Integer({
|
|
6880
7274
|
minimum: 1,
|
|
6881
7275
|
maximum: MAX_RESULTS2,
|
|
6882
7276
|
description: "Max results to return."
|
|
@@ -6942,17 +7336,17 @@ function createWebSearchTool() {
|
|
|
6942
7336
|
}
|
|
6943
7337
|
|
|
6944
7338
|
// src/chat/tools/sandbox/write-file.ts
|
|
6945
|
-
import { Type as
|
|
7339
|
+
import { Type as Type18 } from "@sinclair/typebox";
|
|
6946
7340
|
function createWriteFileTool() {
|
|
6947
7341
|
return tool({
|
|
6948
7342
|
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.",
|
|
6949
|
-
inputSchema:
|
|
7343
|
+
inputSchema: Type18.Object(
|
|
6950
7344
|
{
|
|
6951
|
-
path:
|
|
7345
|
+
path: Type18.String({
|
|
6952
7346
|
minLength: 1,
|
|
6953
7347
|
description: "Path to write in the sandbox workspace."
|
|
6954
7348
|
}),
|
|
6955
|
-
content:
|
|
7349
|
+
content: Type18.String({
|
|
6956
7350
|
description: "UTF-8 file content to write."
|
|
6957
7351
|
})
|
|
6958
7352
|
},
|
|
@@ -7026,6 +7420,9 @@ function createTools(availableSkills, hooks = {}, context) {
|
|
|
7026
7420
|
slackListGetItems: createSlackListGetItemsTool(state),
|
|
7027
7421
|
slackListUpdateItem: createSlackListUpdateItemTool(state)
|
|
7028
7422
|
};
|
|
7423
|
+
if (context.advisor) {
|
|
7424
|
+
tools.advisor = createAdvisorTool(context.advisor);
|
|
7425
|
+
}
|
|
7029
7426
|
if (context.mcpToolManager && context.getActiveSkills) {
|
|
7030
7427
|
tools.searchMcpTools = createSearchMcpToolsTool(
|
|
7031
7428
|
context.mcpToolManager,
|
|
@@ -8715,363 +9112,157 @@ function normalizeToolResult(result, isSandboxResult) {
|
|
|
8715
9112
|
content: [{ type: "text", text: toToolContentText(unwrapped) }],
|
|
8716
9113
|
details: unwrapped
|
|
8717
9114
|
};
|
|
8718
|
-
}
|
|
8719
|
-
|
|
8720
|
-
// src/chat/tools/execution/tool-error-handler.ts
|
|
8721
|
-
function getToolErrorAttributes(error) {
|
|
8722
|
-
if (!(error instanceof SlackActionError)) {
|
|
8723
|
-
return {};
|
|
8724
|
-
}
|
|
8725
|
-
return {
|
|
8726
|
-
"app.slack.error_code": error.code,
|
|
8727
|
-
...error.apiError ? { "app.slack.api_error": error.apiError } : {},
|
|
8728
|
-
...error.detail ? { "app.slack.detail": error.detail } : {},
|
|
8729
|
-
...error.detailLine !== void 0 ? { "app.slack.detail_line": error.detailLine } : {},
|
|
8730
|
-
...error.detailRule ? { "app.slack.detail_rule": error.detailRule } : {}
|
|
8731
|
-
};
|
|
8732
|
-
}
|
|
8733
|
-
function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, traceContext) {
|
|
8734
|
-
const errorType = getMcpAwareErrorType(error, "tool_execution_error");
|
|
8735
|
-
const errorMessage = getMcpAwareErrorMessage(error);
|
|
8736
|
-
setSpanAttributes({
|
|
8737
|
-
"error.type": errorType
|
|
8738
|
-
});
|
|
8739
|
-
if (shouldTrace) {
|
|
8740
|
-
logWarn(
|
|
8741
|
-
"agent_tool_call_failed",
|
|
8742
|
-
traceContext,
|
|
8743
|
-
{
|
|
8744
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8745
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8746
|
-
"gen_ai.tool.name": toolName,
|
|
8747
|
-
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
8748
|
-
"error.type": errorType,
|
|
8749
|
-
"error.message": errorMessage
|
|
8750
|
-
},
|
|
8751
|
-
"Agent tool call failed"
|
|
8752
|
-
);
|
|
8753
|
-
}
|
|
8754
|
-
if (!(error instanceof McpToolError)) {
|
|
8755
|
-
logException(
|
|
8756
|
-
error,
|
|
8757
|
-
"agent_tool_call_failed",
|
|
8758
|
-
{},
|
|
8759
|
-
{
|
|
8760
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8761
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8762
|
-
"gen_ai.tool.name": toolName,
|
|
8763
|
-
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
8764
|
-
...getToolErrorAttributes(error)
|
|
8765
|
-
},
|
|
8766
|
-
"Agent tool call failed"
|
|
8767
|
-
);
|
|
8768
|
-
}
|
|
8769
|
-
throw error;
|
|
8770
|
-
}
|
|
8771
|
-
|
|
8772
|
-
// src/chat/tools/agent-tools.ts
|
|
8773
|
-
function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, capabilityRuntime, pluginAuthOrchestration, onToolCall) {
|
|
8774
|
-
const shouldTrace = shouldEmitDevAgentTrace();
|
|
8775
|
-
return Object.entries(tools).map(([toolName, toolDef]) => ({
|
|
8776
|
-
name: toolName,
|
|
8777
|
-
label: toolName,
|
|
8778
|
-
description: toolDef.description,
|
|
8779
|
-
parameters: toolDef.inputSchema,
|
|
8780
|
-
execute: async (toolCallId, params) => {
|
|
8781
|
-
const normalizedToolCallId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
|
|
8782
|
-
const toolArgumentsAttribute = serializeGenAiAttribute(params);
|
|
8783
|
-
if (toolName === "reportProgress") {
|
|
8784
|
-
const status = buildReportedProgressStatus(params);
|
|
8785
|
-
if (status) {
|
|
8786
|
-
await onStatus?.(status);
|
|
8787
|
-
}
|
|
8788
|
-
}
|
|
8789
|
-
return withSpan(
|
|
8790
|
-
`execute_tool ${toolName}`,
|
|
8791
|
-
"gen_ai.execute_tool",
|
|
8792
|
-
spanContext,
|
|
8793
|
-
async () => {
|
|
8794
|
-
const parsed = params;
|
|
8795
|
-
onToolCall?.(toolName, parsed);
|
|
8796
|
-
try {
|
|
8797
|
-
if (typeof toolDef.execute !== "function") {
|
|
8798
|
-
const resultDetails = { ok: true };
|
|
8799
|
-
const toolResultAttribute2 = serializeGenAiAttribute(resultDetails);
|
|
8800
|
-
if (toolResultAttribute2) {
|
|
8801
|
-
setSpanAttributes({
|
|
8802
|
-
"gen_ai.tool.call.result": toolResultAttribute2
|
|
8803
|
-
});
|
|
8804
|
-
}
|
|
8805
|
-
return {
|
|
8806
|
-
content: [{ type: "text", text: "ok" }],
|
|
8807
|
-
details: resultDetails
|
|
8808
|
-
};
|
|
8809
|
-
}
|
|
8810
|
-
const bashCommand = toolName === "bash" && typeof parsed.command === "string" ? parsed.command.trim() : "";
|
|
8811
|
-
const injection = resolveCredentialInjection(
|
|
8812
|
-
toolName,
|
|
8813
|
-
bashCommand,
|
|
8814
|
-
capabilityRuntime,
|
|
8815
|
-
sandbox
|
|
8816
|
-
);
|
|
8817
|
-
const sandboxInput = buildSandboxInput(toolName, parsed);
|
|
8818
|
-
const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
|
|
8819
|
-
const result = isSandbox ? await sandboxExecutor.execute({
|
|
8820
|
-
toolName,
|
|
8821
|
-
input: toolName === "bash" && (injection.headerTransforms || injection.env) ? {
|
|
8822
|
-
...sandboxInput,
|
|
8823
|
-
...injection.headerTransforms ? { headerTransforms: injection.headerTransforms } : {},
|
|
8824
|
-
...injection.env ? { env: injection.env } : {}
|
|
8825
|
-
} : sandboxInput
|
|
8826
|
-
}) : await toolDef.execute(parsed, {
|
|
8827
|
-
experimental_context: sandbox
|
|
8828
|
-
});
|
|
8829
|
-
const normalized = normalizeToolResult(result, isSandbox);
|
|
8830
|
-
if (bashCommand && pluginAuthOrchestration) {
|
|
8831
|
-
await pluginAuthOrchestration.handleCommandFailure({
|
|
8832
|
-
activeSkill: sandbox.getActiveSkill(),
|
|
8833
|
-
command: bashCommand,
|
|
8834
|
-
details: normalized.details
|
|
8835
|
-
});
|
|
8836
|
-
}
|
|
8837
|
-
const resultAttributeValue = normalized.details && typeof normalized.details === "object" && "rawResult" in normalized.details && normalized.details.rawResult !== void 0 ? normalized.details.rawResult : normalized.details;
|
|
8838
|
-
const toolResultAttribute = serializeGenAiAttribute(resultAttributeValue);
|
|
8839
|
-
if (toolResultAttribute) {
|
|
8840
|
-
setSpanAttributes({
|
|
8841
|
-
"gen_ai.tool.call.result": toolResultAttribute
|
|
8842
|
-
});
|
|
8843
|
-
}
|
|
8844
|
-
return normalized;
|
|
8845
|
-
} catch (error) {
|
|
8846
|
-
if (error instanceof AuthorizationPauseError) {
|
|
8847
|
-
throw error;
|
|
8848
|
-
}
|
|
8849
|
-
handleToolExecutionError(
|
|
8850
|
-
error,
|
|
8851
|
-
toolName,
|
|
8852
|
-
normalizedToolCallId,
|
|
8853
|
-
shouldTrace,
|
|
8854
|
-
spanContext
|
|
8855
|
-
);
|
|
8856
|
-
}
|
|
8857
|
-
},
|
|
8858
|
-
{
|
|
8859
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8860
|
-
"gen_ai.operation.name": "execute_tool",
|
|
8861
|
-
"gen_ai.tool.name": toolName,
|
|
8862
|
-
"gen_ai.tool.description": toolDef.description,
|
|
8863
|
-
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
8864
|
-
...toolArgumentsAttribute ? { "gen_ai.tool.call.arguments": toolArgumentsAttribute } : {}
|
|
8865
|
-
}
|
|
8866
|
-
);
|
|
8867
|
-
}
|
|
8868
|
-
}));
|
|
8869
|
-
}
|
|
8870
|
-
|
|
8871
|
-
// src/chat/respond-helpers.ts
|
|
8872
|
-
var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
|
|
8873
|
-
function getSessionIdentifiers(context) {
|
|
8874
|
-
return {
|
|
8875
|
-
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId,
|
|
8876
|
-
sessionId: context.correlation?.turnId
|
|
8877
|
-
};
|
|
8878
|
-
}
|
|
8879
|
-
function isExecutionDeferralResponse(text) {
|
|
8880
|
-
return /\b(want me to proceed|do you want me to proceed|shall i proceed|can i proceed|should i proceed|let me do that now|give me a moment|tag me again|fresh invocation)\b/i.test(
|
|
8881
|
-
text
|
|
8882
|
-
);
|
|
8883
|
-
}
|
|
8884
|
-
function isToolAccessDisclaimerResponse(text) {
|
|
8885
|
-
return /\b(i (don't|do not) have access to (active )?tool|tool results came back empty|prior results .* empty|cannot access .*tool|need to (run|load) .*tool .* first)\b/i.test(
|
|
8886
|
-
text
|
|
8887
|
-
);
|
|
8888
|
-
}
|
|
8889
|
-
function isExecutionEscapeResponse(text) {
|
|
8890
|
-
const trimmed = text.trim();
|
|
8891
|
-
if (!trimmed) return false;
|
|
8892
|
-
return isExecutionDeferralResponse(trimmed) || isToolAccessDisclaimerResponse(trimmed);
|
|
8893
|
-
}
|
|
8894
|
-
function parseJsonCandidate(text) {
|
|
8895
|
-
const trimmed = text.trim();
|
|
8896
|
-
if (!trimmed) return void 0;
|
|
8897
|
-
try {
|
|
8898
|
-
return JSON.parse(trimmed);
|
|
8899
|
-
} catch {
|
|
8900
|
-
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
8901
|
-
if (!fenced) return void 0;
|
|
8902
|
-
try {
|
|
8903
|
-
return JSON.parse(fenced[1]);
|
|
8904
|
-
} catch {
|
|
8905
|
-
return void 0;
|
|
8906
|
-
}
|
|
8907
|
-
}
|
|
8908
|
-
}
|
|
8909
|
-
function isToolPayloadShape(payload) {
|
|
8910
|
-
if (!payload || typeof payload !== "object") return false;
|
|
8911
|
-
const record = payload;
|
|
8912
|
-
const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
|
|
8913
|
-
if (type.startsWith("tool-")) return true;
|
|
8914
|
-
if (type === "tool_use" || type === "tool_call" || type === "tool_result" || type === "tool_error")
|
|
8915
|
-
return true;
|
|
8916
|
-
const hasToolName = typeof record.toolName === "string" || typeof record.name === "string";
|
|
8917
|
-
const hasToolInput = Object.prototype.hasOwnProperty.call(record, "input") || Object.prototype.hasOwnProperty.call(record, "args");
|
|
8918
|
-
if (hasToolName && hasToolInput) return true;
|
|
8919
|
-
return false;
|
|
8920
|
-
}
|
|
8921
|
-
function isRawToolPayloadResponse(text) {
|
|
8922
|
-
const parsed = parseJsonCandidate(text);
|
|
8923
|
-
if (Array.isArray(parsed)) {
|
|
8924
|
-
return parsed.some((entry) => isToolPayloadShape(entry));
|
|
8925
|
-
}
|
|
8926
|
-
if (isToolPayloadShape(parsed)) {
|
|
8927
|
-
return true;
|
|
8928
|
-
}
|
|
8929
|
-
const compact = text.replace(/\s+/g, " ");
|
|
8930
|
-
return /"type"\s*:\s*"tool[-_](use|call|result|error)"/i.test(compact);
|
|
8931
|
-
}
|
|
8932
|
-
function toObservablePromptPart(part) {
|
|
8933
|
-
if (part.type === "text") {
|
|
8934
|
-
return {
|
|
8935
|
-
type: "text",
|
|
8936
|
-
text: part.text
|
|
8937
|
-
};
|
|
8938
|
-
}
|
|
8939
|
-
return {
|
|
8940
|
-
type: "image",
|
|
8941
|
-
mimeType: part.mimeType,
|
|
8942
|
-
data: `[omitted:${part.data.length}]`
|
|
8943
|
-
};
|
|
8944
|
-
}
|
|
8945
|
-
function summarizeMessageText(text) {
|
|
8946
|
-
const normalized = text.trim().replace(/\s+/g, " ");
|
|
8947
|
-
if (!normalized) {
|
|
8948
|
-
return "[empty]";
|
|
8949
|
-
}
|
|
8950
|
-
return normalized.length > 1200 ? `${normalized.slice(0, 1200)}...` : normalized;
|
|
8951
|
-
}
|
|
8952
|
-
function buildUserTurnText(userInput, conversationContext, metadata) {
|
|
8953
|
-
const trimmedContext = conversationContext?.trim();
|
|
8954
|
-
const conversationId = metadata?.sessionContext?.conversationId;
|
|
8955
|
-
const traceId = metadata?.turnContext?.traceId;
|
|
8956
|
-
if (!trimmedContext && !conversationId && !traceId) {
|
|
8957
|
-
return userInput;
|
|
8958
|
-
}
|
|
8959
|
-
const sections = [];
|
|
8960
|
-
if (trimmedContext) {
|
|
8961
|
-
sections.push(
|
|
8962
|
-
"<thread-background>",
|
|
8963
|
-
trimmedContext,
|
|
8964
|
-
"</thread-background>",
|
|
8965
|
-
""
|
|
8966
|
-
);
|
|
8967
|
-
}
|
|
8968
|
-
if (conversationId) {
|
|
8969
|
-
sections.push(
|
|
8970
|
-
"<session-context>",
|
|
8971
|
-
`- gen_ai.conversation.id: ${conversationId}`,
|
|
8972
|
-
"</session-context>",
|
|
8973
|
-
""
|
|
8974
|
-
);
|
|
8975
|
-
}
|
|
8976
|
-
if (traceId) {
|
|
8977
|
-
sections.push(
|
|
8978
|
-
"<turn-context>",
|
|
8979
|
-
`- trace_id: ${traceId}`,
|
|
8980
|
-
"</turn-context>",
|
|
8981
|
-
""
|
|
8982
|
-
);
|
|
8983
|
-
}
|
|
8984
|
-
sections.push(
|
|
8985
|
-
'<current-instruction priority="highest">',
|
|
8986
|
-
userInput,
|
|
8987
|
-
"</current-instruction>"
|
|
8988
|
-
);
|
|
8989
|
-
return sections.join("\n");
|
|
8990
|
-
}
|
|
8991
|
-
function encodeNonImageAttachmentForPrompt(attachment) {
|
|
8992
|
-
const base64 = attachment.data.toString("base64");
|
|
8993
|
-
const wasTruncated = base64.length > MAX_INLINE_ATTACHMENT_BASE64_CHARS;
|
|
8994
|
-
const encodedPayload = wasTruncated ? `${base64.slice(0, MAX_INLINE_ATTACHMENT_BASE64_CHARS)}...` : base64;
|
|
8995
|
-
return [
|
|
8996
|
-
"<attachment>",
|
|
8997
|
-
`filename: ${attachment.filename ?? "unnamed"}`,
|
|
8998
|
-
`media_type: ${attachment.mediaType}`,
|
|
8999
|
-
"encoding: base64",
|
|
9000
|
-
`truncated: ${wasTruncated ? "true" : "false"}`,
|
|
9001
|
-
"<data_base64>",
|
|
9002
|
-
encodedPayload,
|
|
9003
|
-
"</data_base64>",
|
|
9004
|
-
"</attachment>"
|
|
9005
|
-
].join("\n");
|
|
9006
|
-
}
|
|
9007
|
-
function buildExecutionFailureMessage(toolErrorCount) {
|
|
9008
|
-
if (toolErrorCount > 0) {
|
|
9009
|
-
return "I couldn't complete this because one or more required tools failed in this turn. I've logged the failure details.";
|
|
9010
|
-
}
|
|
9011
|
-
return "I couldn't complete this request in this turn due to an execution failure. I've logged the details for debugging.";
|
|
9012
|
-
}
|
|
9013
|
-
function isToolResultMessage(value) {
|
|
9014
|
-
return typeof value === "object" && value !== null && value.role === "toolResult";
|
|
9015
|
-
}
|
|
9016
|
-
function normalizeToolNameFromResult(result) {
|
|
9017
|
-
if (!result || typeof result !== "object") return void 0;
|
|
9018
|
-
const record = result;
|
|
9019
|
-
if (typeof record.toolName === "string" && record.toolName.length > 0) {
|
|
9020
|
-
return record.toolName;
|
|
9021
|
-
}
|
|
9022
|
-
if (typeof record.name === "string" && record.name.length > 0) {
|
|
9023
|
-
return record.name;
|
|
9024
|
-
}
|
|
9025
|
-
return void 0;
|
|
9026
|
-
}
|
|
9027
|
-
function isToolResultError(result) {
|
|
9028
|
-
if (!result || typeof result !== "object") return false;
|
|
9029
|
-
return Boolean(result.isError);
|
|
9030
|
-
}
|
|
9031
|
-
function isAssistantMessage(value) {
|
|
9032
|
-
return typeof value === "object" && value !== null && value.role === "assistant";
|
|
9033
|
-
}
|
|
9034
|
-
function getPiMessageRole(value) {
|
|
9035
|
-
if (!value || typeof value !== "object") {
|
|
9036
|
-
return void 0;
|
|
9037
|
-
}
|
|
9038
|
-
const role = value.role;
|
|
9039
|
-
return typeof role === "string" ? role : void 0;
|
|
9040
|
-
}
|
|
9041
|
-
function extractAssistantText(message) {
|
|
9042
|
-
const content = message.content ?? [];
|
|
9043
|
-
return content.filter(
|
|
9044
|
-
(part) => part.type === "text" && typeof part.text === "string"
|
|
9045
|
-
).map((part) => part.text).join("\n");
|
|
9046
|
-
}
|
|
9047
|
-
function getTerminalAssistantMessages(messages) {
|
|
9048
|
-
let lastToolResultIndex = -1;
|
|
9049
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
9050
|
-
if (isToolResultMessage(messages[index])) {
|
|
9051
|
-
lastToolResultIndex = index;
|
|
9052
|
-
break;
|
|
9053
|
-
}
|
|
9054
|
-
}
|
|
9055
|
-
return messages.slice(lastToolResultIndex + 1).filter(isAssistantMessage);
|
|
9056
|
-
}
|
|
9057
|
-
function upsertActiveSkill(activeSkills, next) {
|
|
9058
|
-
const existing = activeSkills.find((skill) => skill.name === next.name);
|
|
9059
|
-
if (existing) {
|
|
9060
|
-
existing.body = next.body;
|
|
9061
|
-
existing.description = next.description;
|
|
9062
|
-
existing.skillPath = next.skillPath;
|
|
9063
|
-
existing.allowedTools = next.allowedTools;
|
|
9064
|
-
existing.pluginProvider = next.pluginProvider;
|
|
9065
|
-
return;
|
|
9115
|
+
}
|
|
9116
|
+
|
|
9117
|
+
// src/chat/tools/execution/tool-error-handler.ts
|
|
9118
|
+
function getToolErrorAttributes(error) {
|
|
9119
|
+
if (!(error instanceof SlackActionError)) {
|
|
9120
|
+
return {};
|
|
9066
9121
|
}
|
|
9067
|
-
|
|
9122
|
+
return {
|
|
9123
|
+
"app.slack.error_code": error.code,
|
|
9124
|
+
...error.apiError ? { "app.slack.api_error": error.apiError } : {},
|
|
9125
|
+
...error.detail ? { "app.slack.detail": error.detail } : {},
|
|
9126
|
+
...error.detailLine !== void 0 ? { "app.slack.detail_line": error.detailLine } : {},
|
|
9127
|
+
...error.detailRule ? { "app.slack.detail_rule": error.detailRule } : {}
|
|
9128
|
+
};
|
|
9068
9129
|
}
|
|
9069
|
-
function
|
|
9070
|
-
|
|
9071
|
-
|
|
9072
|
-
|
|
9130
|
+
function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, traceContext) {
|
|
9131
|
+
const errorType = getMcpAwareErrorType(error, "tool_execution_error");
|
|
9132
|
+
const errorMessage = getMcpAwareErrorMessage(error);
|
|
9133
|
+
setSpanAttributes({
|
|
9134
|
+
"error.type": errorType
|
|
9135
|
+
});
|
|
9136
|
+
if (shouldTrace) {
|
|
9137
|
+
logWarn(
|
|
9138
|
+
"agent_tool_call_failed",
|
|
9139
|
+
traceContext,
|
|
9140
|
+
{
|
|
9141
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
9142
|
+
"gen_ai.operation.name": "execute_tool",
|
|
9143
|
+
"gen_ai.tool.name": toolName,
|
|
9144
|
+
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
9145
|
+
"error.type": errorType,
|
|
9146
|
+
"error.message": errorMessage
|
|
9147
|
+
},
|
|
9148
|
+
"Agent tool call failed"
|
|
9149
|
+
);
|
|
9073
9150
|
}
|
|
9074
|
-
|
|
9151
|
+
if (!(error instanceof McpToolError)) {
|
|
9152
|
+
logException(
|
|
9153
|
+
error,
|
|
9154
|
+
"agent_tool_call_failed",
|
|
9155
|
+
{},
|
|
9156
|
+
{
|
|
9157
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
9158
|
+
"gen_ai.operation.name": "execute_tool",
|
|
9159
|
+
"gen_ai.tool.name": toolName,
|
|
9160
|
+
...toolCallId ? { "gen_ai.tool.call.id": toolCallId } : {},
|
|
9161
|
+
...getToolErrorAttributes(error)
|
|
9162
|
+
},
|
|
9163
|
+
"Agent tool call failed"
|
|
9164
|
+
);
|
|
9165
|
+
}
|
|
9166
|
+
throw error;
|
|
9167
|
+
}
|
|
9168
|
+
|
|
9169
|
+
// src/chat/tools/agent-tools.ts
|
|
9170
|
+
function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor, capabilityRuntime, pluginAuthOrchestration, onToolCall) {
|
|
9171
|
+
const shouldTrace = shouldEmitDevAgentTrace();
|
|
9172
|
+
return Object.entries(tools).map(([toolName, toolDef]) => ({
|
|
9173
|
+
name: toolName,
|
|
9174
|
+
label: toolName,
|
|
9175
|
+
description: toolDef.description,
|
|
9176
|
+
parameters: toolDef.inputSchema,
|
|
9177
|
+
execute: async (toolCallId, params) => {
|
|
9178
|
+
const normalizedToolCallId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
|
|
9179
|
+
const toolArgumentsAttribute = serializeGenAiAttribute(params);
|
|
9180
|
+
if (toolName === "reportProgress") {
|
|
9181
|
+
const status = buildReportedProgressStatus(params);
|
|
9182
|
+
if (status) {
|
|
9183
|
+
await onStatus?.(status);
|
|
9184
|
+
}
|
|
9185
|
+
}
|
|
9186
|
+
return withSpan(
|
|
9187
|
+
`execute_tool ${toolName}`,
|
|
9188
|
+
"gen_ai.execute_tool",
|
|
9189
|
+
spanContext,
|
|
9190
|
+
async () => {
|
|
9191
|
+
const parsed = params;
|
|
9192
|
+
onToolCall?.(toolName, parsed);
|
|
9193
|
+
try {
|
|
9194
|
+
if (typeof toolDef.execute !== "function") {
|
|
9195
|
+
const resultDetails = { ok: true };
|
|
9196
|
+
const toolResultAttribute2 = serializeGenAiAttribute(resultDetails);
|
|
9197
|
+
if (toolResultAttribute2) {
|
|
9198
|
+
setSpanAttributes({
|
|
9199
|
+
"gen_ai.tool.call.result": toolResultAttribute2
|
|
9200
|
+
});
|
|
9201
|
+
}
|
|
9202
|
+
return {
|
|
9203
|
+
content: [{ type: "text", text: "ok" }],
|
|
9204
|
+
details: resultDetails
|
|
9205
|
+
};
|
|
9206
|
+
}
|
|
9207
|
+
const bashCommand = toolName === "bash" && typeof parsed.command === "string" ? parsed.command.trim() : "";
|
|
9208
|
+
const injection = resolveCredentialInjection(
|
|
9209
|
+
toolName,
|
|
9210
|
+
bashCommand,
|
|
9211
|
+
capabilityRuntime,
|
|
9212
|
+
sandbox
|
|
9213
|
+
);
|
|
9214
|
+
const sandboxInput = buildSandboxInput(toolName, parsed);
|
|
9215
|
+
const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
|
|
9216
|
+
const result = isSandbox ? await sandboxExecutor.execute({
|
|
9217
|
+
toolName,
|
|
9218
|
+
input: toolName === "bash" && (injection.headerTransforms || injection.env) ? {
|
|
9219
|
+
...sandboxInput,
|
|
9220
|
+
...injection.headerTransforms ? { headerTransforms: injection.headerTransforms } : {},
|
|
9221
|
+
...injection.env ? { env: injection.env } : {}
|
|
9222
|
+
} : sandboxInput
|
|
9223
|
+
}) : await toolDef.execute(parsed, {
|
|
9224
|
+
experimental_context: sandbox
|
|
9225
|
+
});
|
|
9226
|
+
const normalized = normalizeToolResult(result, isSandbox);
|
|
9227
|
+
if (bashCommand && pluginAuthOrchestration) {
|
|
9228
|
+
await pluginAuthOrchestration.handleCommandFailure({
|
|
9229
|
+
activeSkill: sandbox.getActiveSkill(),
|
|
9230
|
+
command: bashCommand,
|
|
9231
|
+
details: normalized.details
|
|
9232
|
+
});
|
|
9233
|
+
}
|
|
9234
|
+
const resultAttributeValue = normalized.details && typeof normalized.details === "object" && "rawResult" in normalized.details && normalized.details.rawResult !== void 0 ? normalized.details.rawResult : normalized.details;
|
|
9235
|
+
const toolResultAttribute = serializeGenAiAttribute(resultAttributeValue);
|
|
9236
|
+
if (toolResultAttribute) {
|
|
9237
|
+
setSpanAttributes({
|
|
9238
|
+
"gen_ai.tool.call.result": toolResultAttribute
|
|
9239
|
+
});
|
|
9240
|
+
}
|
|
9241
|
+
return normalized;
|
|
9242
|
+
} catch (error) {
|
|
9243
|
+
if (error instanceof AuthorizationPauseError) {
|
|
9244
|
+
throw error;
|
|
9245
|
+
}
|
|
9246
|
+
handleToolExecutionError(
|
|
9247
|
+
error,
|
|
9248
|
+
toolName,
|
|
9249
|
+
normalizedToolCallId,
|
|
9250
|
+
shouldTrace,
|
|
9251
|
+
spanContext
|
|
9252
|
+
);
|
|
9253
|
+
}
|
|
9254
|
+
},
|
|
9255
|
+
{
|
|
9256
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
9257
|
+
"gen_ai.operation.name": "execute_tool",
|
|
9258
|
+
"gen_ai.tool.name": toolName,
|
|
9259
|
+
"gen_ai.tool.description": toolDef.description,
|
|
9260
|
+
...normalizedToolCallId ? { "gen_ai.tool.call.id": normalizedToolCallId } : {},
|
|
9261
|
+
...toolArgumentsAttribute ? { "gen_ai.tool.call.arguments": toolArgumentsAttribute } : {}
|
|
9262
|
+
}
|
|
9263
|
+
);
|
|
9264
|
+
}
|
|
9265
|
+
}));
|
|
9075
9266
|
}
|
|
9076
9267
|
|
|
9077
9268
|
// src/chat/services/reply-delivery-plan.ts
|
|
@@ -9181,6 +9372,7 @@ function buildBriefPostCanvasReply(artifactStatePatch) {
|
|
|
9181
9372
|
function buildTurnResult(input) {
|
|
9182
9373
|
const {
|
|
9183
9374
|
newMessages,
|
|
9375
|
+
piMessages,
|
|
9184
9376
|
userInput,
|
|
9185
9377
|
replyFiles,
|
|
9186
9378
|
artifactStatePatch,
|
|
@@ -9285,6 +9477,7 @@ function buildTurnResult(input) {
|
|
|
9285
9477
|
text: resolvedText,
|
|
9286
9478
|
files: replyFiles.length > 0 ? replyFiles : void 0,
|
|
9287
9479
|
artifactStatePatch: Object.keys(artifactStatePatch).length > 0 ? artifactStatePatch : void 0,
|
|
9480
|
+
piMessages,
|
|
9288
9481
|
deliveryPlan,
|
|
9289
9482
|
deliveryMode,
|
|
9290
9483
|
sandboxId,
|
|
@@ -9717,13 +9910,6 @@ function canReusePendingAuthLink(args) {
|
|
|
9717
9910
|
}
|
|
9718
9911
|
return pendingAuth.kind === args.kind && pendingAuth.provider === args.provider && pendingAuth.requesterId === args.requesterId && pendingAuth.linkSentAtMs + AUTH_LINK_REUSE_WINDOW_MS > (args.nowMs ?? Date.now());
|
|
9719
9912
|
}
|
|
9720
|
-
function buildAuthPauseReplyText(args) {
|
|
9721
|
-
const providerLabel = args.provider ? formatProviderLabel(args.provider) : "";
|
|
9722
|
-
if (args.disposition === "link_already_sent") {
|
|
9723
|
-
return providerLabel ? `I still need your ${providerLabel} access to continue. I already sent you a private link.` : "I still need additional access to continue. I already sent you a private link.";
|
|
9724
|
-
}
|
|
9725
|
-
return providerLabel ? `I need your ${providerLabel} access to continue. I sent you a private link.` : "I need additional access to continue. I sent you a private link.";
|
|
9726
|
-
}
|
|
9727
9913
|
function getConversationPendingAuth(args) {
|
|
9728
9914
|
const pendingAuth = args.conversation.processing.pendingAuth;
|
|
9729
9915
|
if (!pendingAuth) {
|
|
@@ -10103,6 +10289,69 @@ function buildUserTurnInput(args) {
|
|
|
10103
10289
|
}
|
|
10104
10290
|
return { routerBlocks, userContentParts };
|
|
10105
10291
|
}
|
|
10292
|
+
function refreshCheckpointTurnContext(messages, turnContextPrompt) {
|
|
10293
|
+
const marker = getTurnContextMarker(turnContextPrompt);
|
|
10294
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
10295
|
+
const content = getUserMessageContent(messages[index]);
|
|
10296
|
+
if (!content) {
|
|
10297
|
+
continue;
|
|
10298
|
+
}
|
|
10299
|
+
const contextIndex = content.findIndex(
|
|
10300
|
+
(part) => isTurnContextPart(part, marker)
|
|
10301
|
+
);
|
|
10302
|
+
if (contextIndex < 0) {
|
|
10303
|
+
continue;
|
|
10304
|
+
}
|
|
10305
|
+
const updatedMessages = [...messages];
|
|
10306
|
+
const updatedContent = [...content];
|
|
10307
|
+
updatedContent[contextIndex] = {
|
|
10308
|
+
...updatedContent[contextIndex],
|
|
10309
|
+
text: turnContextPrompt
|
|
10310
|
+
};
|
|
10311
|
+
updatedMessages[index] = {
|
|
10312
|
+
...messages[index],
|
|
10313
|
+
content: updatedContent
|
|
10314
|
+
};
|
|
10315
|
+
return updatedMessages;
|
|
10316
|
+
}
|
|
10317
|
+
return [
|
|
10318
|
+
...messages,
|
|
10319
|
+
{
|
|
10320
|
+
role: "user",
|
|
10321
|
+
content: [{ type: "text", text: turnContextPrompt }],
|
|
10322
|
+
timestamp: Date.now()
|
|
10323
|
+
}
|
|
10324
|
+
];
|
|
10325
|
+
}
|
|
10326
|
+
function stripTurnContextFromMessages(messages, turnContextPrompt) {
|
|
10327
|
+
const marker = getTurnContextMarker(turnContextPrompt);
|
|
10328
|
+
return messages.flatMap((message) => {
|
|
10329
|
+
const content = getUserMessageContent(message);
|
|
10330
|
+
if (!content) {
|
|
10331
|
+
return [message];
|
|
10332
|
+
}
|
|
10333
|
+
const strippedContent = content.filter(
|
|
10334
|
+
(part) => !isTurnContextPart(part, marker)
|
|
10335
|
+
);
|
|
10336
|
+
if (strippedContent.length === content.length) {
|
|
10337
|
+
return [message];
|
|
10338
|
+
}
|
|
10339
|
+
if (strippedContent.length === 0) {
|
|
10340
|
+
return [];
|
|
10341
|
+
}
|
|
10342
|
+
return [{ ...message, content: strippedContent }];
|
|
10343
|
+
});
|
|
10344
|
+
}
|
|
10345
|
+
function getTurnContextMarker(turnContextPrompt) {
|
|
10346
|
+
return turnContextPrompt.split("\n", 1)[0];
|
|
10347
|
+
}
|
|
10348
|
+
function getUserMessageContent(message) {
|
|
10349
|
+
const record = message;
|
|
10350
|
+
return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
|
|
10351
|
+
}
|
|
10352
|
+
function isTurnContextPart(part, marker) {
|
|
10353
|
+
return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(marker);
|
|
10354
|
+
}
|
|
10106
10355
|
async function generateAssistantReply(messageText, context = {}) {
|
|
10107
10356
|
const replyStartedAtMs = Date.now();
|
|
10108
10357
|
let timeoutResumeConversationId;
|
|
@@ -10135,7 +10384,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10135
10384
|
slackUserId: context.correlation?.requesterId,
|
|
10136
10385
|
slackChannelId: context.correlation?.channelId,
|
|
10137
10386
|
runId: context.correlation?.runId,
|
|
10138
|
-
assistantUserName:
|
|
10387
|
+
assistantUserName: botConfig.userName,
|
|
10139
10388
|
modelId: botConfig.modelId
|
|
10140
10389
|
};
|
|
10141
10390
|
const availableSkills = await discoverSkills({
|
|
@@ -10289,9 +10538,10 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10289
10538
|
upsertActiveSkill(activeSkills, preloaded);
|
|
10290
10539
|
}
|
|
10291
10540
|
}
|
|
10541
|
+
const promptConversationContext = context.piMessages && context.piMessages.length > 0 ? void 0 : context.conversationContext;
|
|
10292
10542
|
const userTurnText = buildUserTurnText(
|
|
10293
10543
|
userInput,
|
|
10294
|
-
|
|
10544
|
+
promptConversationContext,
|
|
10295
10545
|
{
|
|
10296
10546
|
sessionContext: { conversationId: sessionConversationId },
|
|
10297
10547
|
turnContext: { traceId: getActiveTraceId() }
|
|
@@ -10328,6 +10578,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10328
10578
|
const replyFiles = [];
|
|
10329
10579
|
const artifactStatePatch = {};
|
|
10330
10580
|
const toolCalls = [];
|
|
10581
|
+
let advisorTools = [];
|
|
10331
10582
|
let agent;
|
|
10332
10583
|
const mcpAuth = createMcpAuthOrchestration(
|
|
10333
10584
|
{
|
|
@@ -10397,7 +10648,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10397
10648
|
slackUserId: context.correlation?.requesterId,
|
|
10398
10649
|
slackChannelId: context.correlation?.channelId,
|
|
10399
10650
|
runId: context.correlation?.runId,
|
|
10400
|
-
assistantUserName:
|
|
10651
|
+
assistantUserName: botConfig.userName,
|
|
10401
10652
|
modelId: botConfig.modelId
|
|
10402
10653
|
});
|
|
10403
10654
|
const toolChannelId = context.toolChannelId ?? context.correlation?.channelId;
|
|
@@ -10464,7 +10715,13 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10464
10715
|
configuration: configurationValues,
|
|
10465
10716
|
getActiveSkills: () => activeSkills,
|
|
10466
10717
|
mcpToolManager: turnMcpToolManager,
|
|
10467
|
-
sandbox
|
|
10718
|
+
sandbox,
|
|
10719
|
+
advisor: {
|
|
10720
|
+
config: botConfig.advisor,
|
|
10721
|
+
conversationId: sessionConversationId,
|
|
10722
|
+
logContext: spanContext,
|
|
10723
|
+
getTools: () => advisorTools
|
|
10724
|
+
}
|
|
10468
10725
|
}
|
|
10469
10726
|
);
|
|
10470
10727
|
syncResumeState();
|
|
@@ -10481,7 +10738,8 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10481
10738
|
const activeMcpCatalogs = toActiveMcpCatalogSummaries(
|
|
10482
10739
|
turnMcpToolManager.getActiveToolCatalog(activeSkills)
|
|
10483
10740
|
);
|
|
10484
|
-
baseInstructions = buildSystemPrompt(
|
|
10741
|
+
baseInstructions = buildSystemPrompt();
|
|
10742
|
+
const turnContextPrompt = buildTurnContextPrompt({
|
|
10485
10743
|
availableSkills,
|
|
10486
10744
|
activeSkills,
|
|
10487
10745
|
activeMcpCatalogs,
|
|
@@ -10493,13 +10751,15 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10493
10751
|
thinkingLevel: thinkingSelection.thinkingLevel
|
|
10494
10752
|
},
|
|
10495
10753
|
invocation: skillInvocation,
|
|
10496
|
-
assistant: context.assistant,
|
|
10497
10754
|
requester: context.requester,
|
|
10498
10755
|
artifactState: context.artifactState,
|
|
10499
10756
|
configuration: configurationValues,
|
|
10500
|
-
threadParticipants: context.threadParticipants,
|
|
10501
10757
|
turnState: resumedFromCheckpoint ? "resumed" : "fresh"
|
|
10502
10758
|
});
|
|
10759
|
+
const promptContentParts = [
|
|
10760
|
+
{ type: "text", text: turnContextPrompt },
|
|
10761
|
+
...userContentParts
|
|
10762
|
+
];
|
|
10503
10763
|
const inputMessagesAttribute = serializeGenAiAttribute([
|
|
10504
10764
|
{
|
|
10505
10765
|
role: "system",
|
|
@@ -10507,7 +10767,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10507
10767
|
},
|
|
10508
10768
|
{
|
|
10509
10769
|
role: "user",
|
|
10510
|
-
content:
|
|
10770
|
+
content: promptContentParts.map((part) => toObservablePromptPart(part))
|
|
10511
10771
|
}
|
|
10512
10772
|
]);
|
|
10513
10773
|
const onToolCall = (toolName, params) => {
|
|
@@ -10536,7 +10796,8 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10536
10796
|
pluginAuth,
|
|
10537
10797
|
onToolCall
|
|
10538
10798
|
);
|
|
10539
|
-
|
|
10799
|
+
advisorTools = agentTools.filter((tool2) => isAdvisorToolAllowed(tool2.name));
|
|
10800
|
+
agent = new Agent2({
|
|
10540
10801
|
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
10541
10802
|
initialState: {
|
|
10542
10803
|
systemPrompt: baseInstructions,
|
|
@@ -10586,7 +10847,12 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10586
10847
|
beforeMessageCount = agent.state.messages.length;
|
|
10587
10848
|
try {
|
|
10588
10849
|
if (resumedFromCheckpoint) {
|
|
10589
|
-
agent.state.messages =
|
|
10850
|
+
agent.state.messages = refreshCheckpointTurnContext(
|
|
10851
|
+
existingCheckpoint.piMessages,
|
|
10852
|
+
turnContextPrompt
|
|
10853
|
+
);
|
|
10854
|
+
} else if (context.piMessages && context.piMessages.length > 0) {
|
|
10855
|
+
agent.state.messages = [...context.piMessages];
|
|
10590
10856
|
}
|
|
10591
10857
|
beforeMessageCount = agent.state.messages.length;
|
|
10592
10858
|
await withSpan(
|
|
@@ -10595,14 +10861,9 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10595
10861
|
spanContext,
|
|
10596
10862
|
async () => {
|
|
10597
10863
|
let promptResult;
|
|
10598
|
-
const promptPromise = resumedFromCheckpoint ? (
|
|
10599
|
-
// Checkpoint resumes continue from the persisted Pi message
|
|
10600
|
-
// state. Any reconstructed replyContext only matters when the
|
|
10601
|
-
// turn parked before the initial user prompt was recorded.
|
|
10602
|
-
agent.continue()
|
|
10603
|
-
) : agent.prompt({
|
|
10864
|
+
const promptPromise = resumedFromCheckpoint ? agent.continue() : agent.prompt({
|
|
10604
10865
|
role: "user",
|
|
10605
|
-
content:
|
|
10866
|
+
content: promptContentParts,
|
|
10606
10867
|
timestamp: Date.now()
|
|
10607
10868
|
});
|
|
10608
10869
|
let timeoutId;
|
|
@@ -10692,6 +10953,10 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10692
10953
|
}
|
|
10693
10954
|
return buildTurnResult({
|
|
10694
10955
|
newMessages,
|
|
10956
|
+
piMessages: stripTurnContextFromMessages(
|
|
10957
|
+
agent.state.messages,
|
|
10958
|
+
turnContextPrompt
|
|
10959
|
+
),
|
|
10695
10960
|
userInput,
|
|
10696
10961
|
replyFiles,
|
|
10697
10962
|
artifactStatePatch,
|
|
@@ -10705,7 +10970,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10705
10970
|
usage: turnUsage,
|
|
10706
10971
|
thinkingSelection,
|
|
10707
10972
|
correlation: context.correlation,
|
|
10708
|
-
assistantUserName:
|
|
10973
|
+
assistantUserName: botConfig.userName
|
|
10709
10974
|
});
|
|
10710
10975
|
} catch (error) {
|
|
10711
10976
|
if (timedOut && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
@@ -10721,7 +10986,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10721
10986
|
requesterId: context.correlation?.requesterId,
|
|
10722
10987
|
channelId: context.correlation?.channelId,
|
|
10723
10988
|
runId: context.correlation?.runId,
|
|
10724
|
-
assistantUserName:
|
|
10989
|
+
assistantUserName: botConfig.userName,
|
|
10725
10990
|
modelId: botConfig.modelId
|
|
10726
10991
|
}
|
|
10727
10992
|
});
|
|
@@ -10759,7 +11024,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10759
11024
|
requesterId: context.correlation?.requesterId,
|
|
10760
11025
|
channelId: context.correlation?.channelId,
|
|
10761
11026
|
runId: context.correlation?.runId,
|
|
10762
|
-
assistantUserName:
|
|
11027
|
+
assistantUserName: botConfig.userName,
|
|
10763
11028
|
modelId: botConfig.modelId
|
|
10764
11029
|
}
|
|
10765
11030
|
});
|
|
@@ -10790,7 +11055,7 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10790
11055
|
slackUserId: context.correlation?.requesterId,
|
|
10791
11056
|
slackChannelId: context.correlation?.channelId,
|
|
10792
11057
|
runId: context.correlation?.runId,
|
|
10793
|
-
assistantUserName:
|
|
11058
|
+
assistantUserName: botConfig.userName,
|
|
10794
11059
|
modelId: botConfig.modelId
|
|
10795
11060
|
},
|
|
10796
11061
|
{},
|
|
@@ -11491,10 +11756,6 @@ function createResumeReplyContext(args, statusSession) {
|
|
|
11491
11756
|
const persistedChannelConfiguration = replyContext.channelConfiguration ?? (replyContext.configuration ? createReadOnlyConfigService(replyContext.configuration) : void 0);
|
|
11492
11757
|
return {
|
|
11493
11758
|
...replyContext,
|
|
11494
|
-
assistant: {
|
|
11495
|
-
userName: botConfig.userName,
|
|
11496
|
-
...replyContext.assistant
|
|
11497
|
-
},
|
|
11498
11759
|
correlation: {
|
|
11499
11760
|
...replyContext.correlation,
|
|
11500
11761
|
threadId: replyContext.correlation?.threadId ?? threadId,
|
|
@@ -11647,17 +11908,7 @@ async function resumeAuthorizedRequest(args) {
|
|
|
11647
11908
|
});
|
|
11648
11909
|
}
|
|
11649
11910
|
|
|
11650
|
-
// src/chat/runtime/auth-pause-
|
|
11651
|
-
function buildAuthPauseSlackMessage(args) {
|
|
11652
|
-
const footer = buildSlackReplyFooter({
|
|
11653
|
-
conversationId: args.conversationId,
|
|
11654
|
-
durationMs: args.durationMs,
|
|
11655
|
-
thinkingLevel: args.thinkingLevel,
|
|
11656
|
-
usage: args.usage
|
|
11657
|
-
});
|
|
11658
|
-
const blocks = buildSlackReplyBlocks(args.text, footer);
|
|
11659
|
-
return blocks ? { text: args.text, blocks } : { text: args.text };
|
|
11660
|
-
}
|
|
11911
|
+
// src/chat/runtime/auth-pause-state.ts
|
|
11661
11912
|
function completeAuthPauseTurn(args) {
|
|
11662
11913
|
markConversationMessage(
|
|
11663
11914
|
args.conversation,
|
|
@@ -11667,19 +11918,6 @@ function completeAuthPauseTurn(args) {
|
|
|
11667
11918
|
skippedReason: void 0
|
|
11668
11919
|
}
|
|
11669
11920
|
);
|
|
11670
|
-
upsertConversationMessage(args.conversation, {
|
|
11671
|
-
id: generateConversationId("assistant"),
|
|
11672
|
-
role: "assistant",
|
|
11673
|
-
text: normalizeConversationText(args.text) || "[empty response]",
|
|
11674
|
-
createdAtMs: Date.now(),
|
|
11675
|
-
author: {
|
|
11676
|
-
userName: botConfig.userName,
|
|
11677
|
-
isBot: true
|
|
11678
|
-
},
|
|
11679
|
-
meta: {
|
|
11680
|
-
replied: true
|
|
11681
|
-
}
|
|
11682
|
-
});
|
|
11683
11921
|
markTurnCompleted({
|
|
11684
11922
|
conversation: args.conversation,
|
|
11685
11923
|
nowMs: Date.now(),
|
|
@@ -11687,41 +11925,15 @@ function completeAuthPauseTurn(args) {
|
|
|
11687
11925
|
updateConversationStats
|
|
11688
11926
|
});
|
|
11689
11927
|
}
|
|
11690
|
-
async function
|
|
11928
|
+
async function persistAuthPauseTurnState(args) {
|
|
11691
11929
|
const currentState = await getPersistedThreadState(args.threadStateId);
|
|
11692
11930
|
const conversation = coerceThreadConversationState(currentState);
|
|
11693
11931
|
completeAuthPauseTurn({
|
|
11694
11932
|
conversation,
|
|
11695
|
-
sessionId: args.sessionId
|
|
11696
|
-
text: args.text
|
|
11933
|
+
sessionId: args.sessionId
|
|
11697
11934
|
});
|
|
11698
11935
|
await persistThreadStateById(args.threadStateId, { conversation });
|
|
11699
11936
|
}
|
|
11700
|
-
async function deliverAuthPauseReply(args) {
|
|
11701
|
-
const retryable = isRetryableTurnError(args.error) ? args.error : void 0;
|
|
11702
|
-
const text = retryable ? buildAuthPauseReplyText({
|
|
11703
|
-
disposition: retryable.metadata?.authDisposition,
|
|
11704
|
-
provider: retryable.metadata?.authProvider
|
|
11705
|
-
}) : buildAuthPauseReplyText({ provider: args.fallbackProvider });
|
|
11706
|
-
const message = buildAuthPauseSlackMessage({
|
|
11707
|
-
conversationId: args.conversationId,
|
|
11708
|
-
durationMs: retryable?.metadata?.authDurationMs,
|
|
11709
|
-
text,
|
|
11710
|
-
thinkingLevel: retryable?.metadata?.authThinkingLevel,
|
|
11711
|
-
usage: retryable?.metadata?.authUsage
|
|
11712
|
-
});
|
|
11713
|
-
await postSlackMessage({
|
|
11714
|
-
channelId: args.channelId,
|
|
11715
|
-
threadTs: args.threadTs,
|
|
11716
|
-
text: message.text,
|
|
11717
|
-
...message.blocks ? { blocks: message.blocks } : {}
|
|
11718
|
-
});
|
|
11719
|
-
await persistAuthPauseReplyState({
|
|
11720
|
-
sessionId: args.sessionId,
|
|
11721
|
-
text,
|
|
11722
|
-
threadStateId: args.threadStateId
|
|
11723
|
-
});
|
|
11724
|
-
}
|
|
11725
11937
|
|
|
11726
11938
|
// src/chat/services/timeout-resume.ts
|
|
11727
11939
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
@@ -11913,6 +12125,9 @@ async function persistCompletedReplyState(channelId, threadTs, sessionId, reply)
|
|
|
11913
12125
|
replied: true
|
|
11914
12126
|
}
|
|
11915
12127
|
});
|
|
12128
|
+
if (reply.piMessages) {
|
|
12129
|
+
conversation.piMessages = reply.piMessages;
|
|
12130
|
+
}
|
|
11916
12131
|
markTurnCompleted({
|
|
11917
12132
|
conversation,
|
|
11918
12133
|
nowMs: Date.now(),
|
|
@@ -11993,7 +12208,6 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
11993
12208
|
connectedText: "",
|
|
11994
12209
|
failureText: "MCP authorization completed, but resuming the request failed. Please retry the original command.",
|
|
11995
12210
|
replyContext: {
|
|
11996
|
-
assistant: { userName: botConfig.userName },
|
|
11997
12211
|
requester: {
|
|
11998
12212
|
userId: authSession.userId,
|
|
11999
12213
|
userName: userMessage?.author?.userName,
|
|
@@ -12009,11 +12223,11 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
12009
12223
|
toolChannelId: authSession.toolChannelId ?? artifacts.assistantContextChannelId ?? authSession.channelId,
|
|
12010
12224
|
conversationContext,
|
|
12011
12225
|
artifactState: artifacts,
|
|
12226
|
+
piMessages: conversation.piMessages,
|
|
12012
12227
|
configuration: authSession.configuration,
|
|
12013
12228
|
pendingAuth,
|
|
12014
12229
|
channelConfiguration,
|
|
12015
12230
|
sandbox: getPersistedSandboxState(currentState),
|
|
12016
|
-
threadParticipants: buildThreadParticipants(conversation.messages),
|
|
12017
12231
|
onAuthPending: async (nextPendingAuth) => {
|
|
12018
12232
|
await applyPendingAuthUpdate({
|
|
12019
12233
|
conversation,
|
|
@@ -12067,19 +12281,17 @@ async function resumeAuthorizedMcpTurn(args) {
|
|
|
12067
12281
|
}
|
|
12068
12282
|
},
|
|
12069
12283
|
onAuthPause: async (error) => {
|
|
12070
|
-
await
|
|
12071
|
-
channelId: authSession.channelId,
|
|
12072
|
-
conversationId: authSession.conversationId,
|
|
12073
|
-
error,
|
|
12074
|
-
fallbackProvider: provider,
|
|
12284
|
+
await persistAuthPauseTurnState({
|
|
12075
12285
|
sessionId: resolvedSessionId,
|
|
12076
|
-
threadStateId: `slack:${authSession.channelId}:${authSession.threadTs}
|
|
12077
|
-
threadTs: authSession.threadTs
|
|
12286
|
+
threadStateId: `slack:${authSession.channelId}:${authSession.threadTs}`
|
|
12078
12287
|
});
|
|
12079
12288
|
logWarn(
|
|
12080
12289
|
"mcp_oauth_callback_resume_reparked_for_auth",
|
|
12081
12290
|
{},
|
|
12082
|
-
{
|
|
12291
|
+
{
|
|
12292
|
+
"app.credential.provider": provider,
|
|
12293
|
+
...isRetryableTurnError(error) ? { "app.turn.retryable_reason": error.reason } : {}
|
|
12294
|
+
},
|
|
12083
12295
|
"Resumed MCP turn requested another authorization flow"
|
|
12084
12296
|
);
|
|
12085
12297
|
},
|
|
@@ -12311,15 +12523,6 @@ async function publishAppHomeView(slackClient, userId, userTokenStore) {
|
|
|
12311
12523
|
function htmlErrorResponse(title, message, status) {
|
|
12312
12524
|
return htmlCallbackResponse(escapeXml(title), escapeXml(message), status);
|
|
12313
12525
|
}
|
|
12314
|
-
async function buildResumeConversationContext2(channelId, threadTs) {
|
|
12315
|
-
const conversation = coerceThreadConversationState(
|
|
12316
|
-
await getPersistedThreadState(`slack:${channelId}:${threadTs}`)
|
|
12317
|
-
);
|
|
12318
|
-
const latestUserMessageId = [...conversation.messages].reverse().find((message) => message.role === "user")?.id;
|
|
12319
|
-
return buildConversationContext(conversation, {
|
|
12320
|
-
excludeMessageId: latestUserMessageId
|
|
12321
|
-
});
|
|
12322
|
-
}
|
|
12323
12526
|
async function buildCheckpointConversationContext(conversationId, sessionId) {
|
|
12324
12527
|
const conversation = coerceThreadConversationState(
|
|
12325
12528
|
await getPersistedThreadState(conversationId)
|
|
@@ -12353,6 +12556,9 @@ async function persistCompletedOAuthReplyState(args) {
|
|
|
12353
12556
|
replied: true
|
|
12354
12557
|
}
|
|
12355
12558
|
});
|
|
12559
|
+
if (args.reply.piMessages) {
|
|
12560
|
+
conversation.piMessages = args.reply.piMessages;
|
|
12561
|
+
}
|
|
12356
12562
|
markTurnCompleted({
|
|
12357
12563
|
conversation,
|
|
12358
12564
|
nowMs: Date.now(),
|
|
@@ -12451,7 +12657,6 @@ async function resumeCheckpointedOAuthTurn(stored) {
|
|
|
12451
12657
|
initialText: "",
|
|
12452
12658
|
failureText: "I connected your account but hit an error processing your request. Please try the command again.",
|
|
12453
12659
|
replyContext: {
|
|
12454
|
-
assistant: { userName: botConfig.userName },
|
|
12455
12660
|
requester: {
|
|
12456
12661
|
userId: userMessage.author.userId,
|
|
12457
12662
|
userName: userMessage.author.userName,
|
|
@@ -12467,8 +12672,8 @@ async function resumeCheckpointedOAuthTurn(stored) {
|
|
|
12467
12672
|
pendingAuth,
|
|
12468
12673
|
conversationContext,
|
|
12469
12674
|
channelConfiguration,
|
|
12675
|
+
piMessages: conversation.piMessages,
|
|
12470
12676
|
sandbox: getPersistedSandboxState(currentState),
|
|
12471
|
-
threadParticipants: buildThreadParticipants(conversation.messages),
|
|
12472
12677
|
onAuthPending: async (nextPendingAuth) => {
|
|
12473
12678
|
await applyPendingAuthUpdate({
|
|
12474
12679
|
conversation,
|
|
@@ -12511,15 +12716,10 @@ async function resumeCheckpointedOAuthTurn(stored) {
|
|
|
12511
12716
|
sessionId: resolvedSessionId
|
|
12512
12717
|
});
|
|
12513
12718
|
},
|
|
12514
|
-
onAuthPause: async (
|
|
12515
|
-
await
|
|
12516
|
-
channelId: stored.channelId,
|
|
12517
|
-
conversationId: stored.resumeConversationId,
|
|
12518
|
-
error,
|
|
12519
|
-
fallbackProvider: stored.provider,
|
|
12719
|
+
onAuthPause: async () => {
|
|
12720
|
+
await persistAuthPauseTurnState({
|
|
12520
12721
|
sessionId: resolvedSessionId,
|
|
12521
|
-
threadStateId: stored.resumeConversationId
|
|
12522
|
-
threadTs: stored.threadTs
|
|
12722
|
+
threadStateId: stored.resumeConversationId
|
|
12523
12723
|
});
|
|
12524
12724
|
},
|
|
12525
12725
|
onTimeoutPause: async (error) => {
|
|
@@ -12549,10 +12749,14 @@ async function resumeCheckpointedOAuthTurn(stored) {
|
|
|
12549
12749
|
}
|
|
12550
12750
|
async function resumePendingOAuthMessage(stored) {
|
|
12551
12751
|
if (!stored.pendingMessage || !stored.channelId || !stored.threadTs) return;
|
|
12552
|
-
const
|
|
12553
|
-
|
|
12554
|
-
|
|
12752
|
+
const threadId = `slack:${stored.channelId}:${stored.threadTs}`;
|
|
12753
|
+
const conversation = coerceThreadConversationState(
|
|
12754
|
+
await getPersistedThreadState(threadId)
|
|
12555
12755
|
);
|
|
12756
|
+
const latestUserMessageId = [...conversation.messages].reverse().find((message) => message.role === "user")?.id;
|
|
12757
|
+
const conversationContext = buildConversationContext(conversation, {
|
|
12758
|
+
excludeMessageId: latestUserMessageId
|
|
12759
|
+
});
|
|
12556
12760
|
await resumeAuthorizedRequest({
|
|
12557
12761
|
messageText: stored.pendingMessage,
|
|
12558
12762
|
channelId: stored.channelId,
|
|
@@ -12562,6 +12766,7 @@ async function resumePendingOAuthMessage(stored) {
|
|
|
12562
12766
|
replyContext: {
|
|
12563
12767
|
requester: { userId: stored.userId },
|
|
12564
12768
|
conversationContext,
|
|
12769
|
+
piMessages: conversation.piMessages,
|
|
12565
12770
|
configuration: stored.configuration
|
|
12566
12771
|
},
|
|
12567
12772
|
onSuccess: async (reply) => {
|
|
@@ -12840,6 +13045,9 @@ async function persistCompletedReplyState2(args) {
|
|
|
12840
13045
|
replied: true
|
|
12841
13046
|
}
|
|
12842
13047
|
});
|
|
13048
|
+
if (args.reply.piMessages) {
|
|
13049
|
+
conversation.piMessages = args.reply.piMessages;
|
|
13050
|
+
}
|
|
12843
13051
|
markTurnCompleted({
|
|
12844
13052
|
conversation,
|
|
12845
13053
|
nowMs: Date.now(),
|
|
@@ -12909,7 +13117,6 @@ async function resumeTimedOutTurn(payload) {
|
|
|
12909
13117
|
lockKey: payload.conversationId,
|
|
12910
13118
|
failureText: "I hit an error while resuming that request. Please try the command again.",
|
|
12911
13119
|
replyContext: {
|
|
12912
|
-
assistant: { userName: botConfig.userName },
|
|
12913
13120
|
requester: {
|
|
12914
13121
|
userId: userMessage.author.userId,
|
|
12915
13122
|
userName: userMessage.author.userName,
|
|
@@ -12927,8 +13134,8 @@ async function resumeTimedOutTurn(payload) {
|
|
|
12927
13134
|
pendingAuth: conversation.processing.pendingAuth,
|
|
12928
13135
|
conversationContext,
|
|
12929
13136
|
channelConfiguration,
|
|
13137
|
+
piMessages: conversation.piMessages,
|
|
12930
13138
|
sandbox,
|
|
12931
|
-
threadParticipants: buildThreadParticipants(conversation.messages),
|
|
12932
13139
|
onAuthPending: async (nextPendingAuth) => {
|
|
12933
13140
|
await applyPendingAuthUpdate({
|
|
12934
13141
|
conversation,
|
|
@@ -12964,20 +13171,17 @@ async function resumeTimedOutTurn(payload) {
|
|
|
12964
13171
|
{},
|
|
12965
13172
|
{
|
|
12966
13173
|
"app.ai.conversation_id": payload.conversationId,
|
|
12967
|
-
"app.ai.session_id": payload.sessionId
|
|
13174
|
+
"app.ai.session_id": payload.sessionId,
|
|
13175
|
+
...isRetryableTurnError(error) ? { "app.turn.retryable_reason": error.reason } : {}
|
|
12968
13176
|
},
|
|
12969
13177
|
"Failed to resume timed-out turn"
|
|
12970
13178
|
);
|
|
12971
13179
|
await persistFailedReplyState2(checkpoint);
|
|
12972
13180
|
},
|
|
12973
|
-
onAuthPause: async (
|
|
12974
|
-
await
|
|
12975
|
-
channelId: thread.channelId,
|
|
12976
|
-
conversationId: payload.conversationId,
|
|
12977
|
-
error,
|
|
13181
|
+
onAuthPause: async () => {
|
|
13182
|
+
await persistAuthPauseTurnState({
|
|
12978
13183
|
sessionId: payload.sessionId,
|
|
12979
|
-
threadStateId: payload.conversationId
|
|
12980
|
-
threadTs: thread.threadTs
|
|
13184
|
+
threadStateId: payload.conversationId
|
|
12981
13185
|
});
|
|
12982
13186
|
logWarn(
|
|
12983
13187
|
"timeout_resume_reparked_for_auth",
|
|
@@ -14704,13 +14908,7 @@ function createReplyToThread(deps) {
|
|
|
14704
14908
|
let shouldPersistFailureState = true;
|
|
14705
14909
|
try {
|
|
14706
14910
|
const toolChannelId = preparedState.artifacts.assistantContextChannelId ?? channelId;
|
|
14707
|
-
const threadParticipants = buildThreadParticipants(
|
|
14708
|
-
preparedState.conversation.messages
|
|
14709
|
-
);
|
|
14710
14911
|
const reply = await deps.services.generateAssistantReply(userText, {
|
|
14711
|
-
assistant: {
|
|
14712
|
-
userName: botConfig.userName
|
|
14713
|
-
},
|
|
14714
14912
|
requester: {
|
|
14715
14913
|
userId: message.author.userId,
|
|
14716
14914
|
userName: message.author.userName ?? fallbackIdentity?.userName,
|
|
@@ -14718,6 +14916,7 @@ function createReplyToThread(deps) {
|
|
|
14718
14916
|
},
|
|
14719
14917
|
conversationContext: preparedState.routingContext ?? preparedState.conversationContext,
|
|
14720
14918
|
artifactState: preparedState.artifacts,
|
|
14919
|
+
piMessages: preparedState.conversation.piMessages,
|
|
14721
14920
|
pendingAuth: preparedState.conversation.processing.pendingAuth,
|
|
14722
14921
|
configuration: preparedState.configuration,
|
|
14723
14922
|
channelConfiguration: preparedState.channelConfiguration,
|
|
@@ -14758,7 +14957,6 @@ function createReplyToThread(deps) {
|
|
|
14758
14957
|
conversation: preparedState.conversation
|
|
14759
14958
|
});
|
|
14760
14959
|
},
|
|
14761
|
-
threadParticipants,
|
|
14762
14960
|
onStatus: (nextStatus) => status.update(nextStatus)
|
|
14763
14961
|
});
|
|
14764
14962
|
const diagnosticsContext = {
|
|
@@ -14834,6 +15032,9 @@ function createReplyToThread(deps) {
|
|
|
14834
15032
|
replied: true
|
|
14835
15033
|
}
|
|
14836
15034
|
});
|
|
15035
|
+
if (reply.piMessages) {
|
|
15036
|
+
preparedState.conversation.piMessages = reply.piMessages;
|
|
15037
|
+
}
|
|
14837
15038
|
const artifactStatePatch = reply.artifactStatePatch ? { ...reply.artifactStatePatch } : {};
|
|
14838
15039
|
const reactionPerformed = reply.diagnostics.toolCalls.includes(
|
|
14839
15040
|
"slackMessageAddReaction"
|
|
@@ -14935,35 +15136,9 @@ function createReplyToThread(deps) {
|
|
|
14935
15136
|
}
|
|
14936
15137
|
} catch (error) {
|
|
14937
15138
|
if (isRetryableTurnError(error, "mcp_auth_resume") || isRetryableTurnError(error, "plugin_auth_resume")) {
|
|
14938
|
-
const authPauseText = buildAuthPauseReplyText({
|
|
14939
|
-
disposition: error.metadata?.authDisposition,
|
|
14940
|
-
provider: error.metadata?.authProvider
|
|
14941
|
-
});
|
|
14942
|
-
const authPauseFooter = buildSlackReplyFooter({
|
|
14943
|
-
conversationId,
|
|
14944
|
-
durationMs: error.metadata?.authDurationMs,
|
|
14945
|
-
thinkingLevel: error.metadata?.authThinkingLevel,
|
|
14946
|
-
usage: error.metadata?.authUsage
|
|
14947
|
-
});
|
|
14948
|
-
const useSlackFooterForAuthPause = Boolean(authPauseFooter) && Boolean(channelId && threadTs) && thread.adapter?.name === "slack";
|
|
14949
|
-
if (useSlackFooterForAuthPause && channelId && threadTs) {
|
|
14950
|
-
await beforeFirstResponsePost();
|
|
14951
|
-
await postSlackApiReplyPosts({
|
|
14952
|
-
channelId,
|
|
14953
|
-
threadTs,
|
|
14954
|
-
footer: authPauseFooter,
|
|
14955
|
-
posts: [{ stage: "thread_reply", text: authPauseText }]
|
|
14956
|
-
});
|
|
14957
|
-
} else {
|
|
14958
|
-
await postThreadReply(
|
|
14959
|
-
buildSlackOutputMessage(authPauseText),
|
|
14960
|
-
"thread_reply"
|
|
14961
|
-
);
|
|
14962
|
-
}
|
|
14963
15139
|
completeAuthPauseTurn({
|
|
14964
15140
|
conversation: preparedState.conversation,
|
|
14965
|
-
sessionId: error.metadata?.sessionId ?? turnId
|
|
14966
|
-
text: authPauseText
|
|
15141
|
+
sessionId: error.metadata?.sessionId ?? turnId
|
|
14967
15142
|
});
|
|
14968
15143
|
await persistThreadState(thread, {
|
|
14969
15144
|
conversation: preparedState.conversation
|
|
@@ -15928,6 +16103,7 @@ async function handleAuthenticatedSlackMessageChangedMention(args) {
|
|
|
15928
16103
|
if (!authAdapter.verifySignature(args.rawBody, timestamp, signature)) {
|
|
15929
16104
|
return;
|
|
15930
16105
|
}
|
|
16106
|
+
await args.bot.initialize();
|
|
15931
16107
|
const webhookOptions = {
|
|
15932
16108
|
waitUntil: (task) => args.waitUntil(task)
|
|
15933
16109
|
};
|
|
@@ -15953,7 +16129,7 @@ async function handleAuthenticatedSlackMessageChangedMention(args) {
|
|
|
15953
16129
|
);
|
|
15954
16130
|
return true;
|
|
15955
16131
|
};
|
|
15956
|
-
if (authAdapter.
|
|
16132
|
+
if (authAdapter.defaultBotTokenProvider) {
|
|
15957
16133
|
dispatch();
|
|
15958
16134
|
return;
|
|
15959
16135
|
}
|