@sentry/junior 0.27.0 → 0.27.1
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 +965 -1316
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -2701,135 +2701,6 @@ function truncateStatusText(text) {
|
|
|
2701
2701
|
}
|
|
2702
2702
|
return truncateWithEllipsis(trimmed, SLACK_STATUS_MAX_LENGTH);
|
|
2703
2703
|
}
|
|
2704
|
-
function compactStatusPath(value) {
|
|
2705
|
-
if (typeof value !== "string") {
|
|
2706
|
-
return void 0;
|
|
2707
|
-
}
|
|
2708
|
-
const trimmed = value.trim();
|
|
2709
|
-
if (!trimmed) {
|
|
2710
|
-
return void 0;
|
|
2711
|
-
}
|
|
2712
|
-
if (trimmed.length <= 80) {
|
|
2713
|
-
return trimmed;
|
|
2714
|
-
}
|
|
2715
|
-
return `...${trimmed.slice(-77)}`;
|
|
2716
|
-
}
|
|
2717
|
-
function compactStatusText(value, maxLength = 80) {
|
|
2718
|
-
if (typeof value !== "string") {
|
|
2719
|
-
return void 0;
|
|
2720
|
-
}
|
|
2721
|
-
const trimmed = value.trim();
|
|
2722
|
-
if (!trimmed) {
|
|
2723
|
-
return void 0;
|
|
2724
|
-
}
|
|
2725
|
-
return truncateWithEllipsis(trimmed, maxLength);
|
|
2726
|
-
}
|
|
2727
|
-
function readShellToken(command, startIndex) {
|
|
2728
|
-
let index = startIndex;
|
|
2729
|
-
while (index < command.length && /\s/.test(command[index] ?? "")) {
|
|
2730
|
-
index += 1;
|
|
2731
|
-
}
|
|
2732
|
-
if (index >= command.length) {
|
|
2733
|
-
return void 0;
|
|
2734
|
-
}
|
|
2735
|
-
let token = "";
|
|
2736
|
-
let quote;
|
|
2737
|
-
while (index < command.length) {
|
|
2738
|
-
const char = command[index];
|
|
2739
|
-
if (!char) {
|
|
2740
|
-
break;
|
|
2741
|
-
}
|
|
2742
|
-
if (quote) {
|
|
2743
|
-
if (char === quote) {
|
|
2744
|
-
quote = void 0;
|
|
2745
|
-
index += 1;
|
|
2746
|
-
continue;
|
|
2747
|
-
}
|
|
2748
|
-
if (char === "\\" && quote === '"' && index + 1 < command.length) {
|
|
2749
|
-
token += command[index + 1];
|
|
2750
|
-
index += 2;
|
|
2751
|
-
continue;
|
|
2752
|
-
}
|
|
2753
|
-
token += char;
|
|
2754
|
-
index += 1;
|
|
2755
|
-
continue;
|
|
2756
|
-
}
|
|
2757
|
-
if (/\s/.test(char)) {
|
|
2758
|
-
break;
|
|
2759
|
-
}
|
|
2760
|
-
if (char === '"' || char === "'") {
|
|
2761
|
-
quote = char;
|
|
2762
|
-
index += 1;
|
|
2763
|
-
continue;
|
|
2764
|
-
}
|
|
2765
|
-
if (char === "\\" && index + 1 < command.length) {
|
|
2766
|
-
token += command[index + 1];
|
|
2767
|
-
index += 2;
|
|
2768
|
-
continue;
|
|
2769
|
-
}
|
|
2770
|
-
token += char;
|
|
2771
|
-
index += 1;
|
|
2772
|
-
}
|
|
2773
|
-
return { token, nextIndex: index };
|
|
2774
|
-
}
|
|
2775
|
-
function compactStatusCommand(value) {
|
|
2776
|
-
if (typeof value !== "string") {
|
|
2777
|
-
return void 0;
|
|
2778
|
-
}
|
|
2779
|
-
const trimmed = value.trim();
|
|
2780
|
-
if (!trimmed) {
|
|
2781
|
-
return void 0;
|
|
2782
|
-
}
|
|
2783
|
-
let index = 0;
|
|
2784
|
-
while (index < trimmed.length) {
|
|
2785
|
-
const parsed = readShellToken(trimmed, index);
|
|
2786
|
-
if (!parsed) {
|
|
2787
|
-
return void 0;
|
|
2788
|
-
}
|
|
2789
|
-
index = parsed.nextIndex;
|
|
2790
|
-
if (!parsed.token) {
|
|
2791
|
-
continue;
|
|
2792
|
-
}
|
|
2793
|
-
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(parsed.token)) {
|
|
2794
|
-
continue;
|
|
2795
|
-
}
|
|
2796
|
-
const normalized = parsed.token.replace(/[\\/]+$/g, "");
|
|
2797
|
-
if (!normalized) {
|
|
2798
|
-
return void 0;
|
|
2799
|
-
}
|
|
2800
|
-
const parts = normalized.split(/[\\/]/).filter((part) => part.length > 0);
|
|
2801
|
-
const command = parts.length > 0 ? parts[parts.length - 1] : normalized;
|
|
2802
|
-
return compactStatusText(command, 40);
|
|
2803
|
-
}
|
|
2804
|
-
return void 0;
|
|
2805
|
-
}
|
|
2806
|
-
function compactStatusFilename(value) {
|
|
2807
|
-
if (typeof value !== "string") {
|
|
2808
|
-
return void 0;
|
|
2809
|
-
}
|
|
2810
|
-
const trimmed = value.trim().replace(/[\\/]+$/g, "");
|
|
2811
|
-
if (!trimmed) {
|
|
2812
|
-
return void 0;
|
|
2813
|
-
}
|
|
2814
|
-
const parts = trimmed.split(/[\\/]/).filter((part) => part.length > 0);
|
|
2815
|
-
const filename = parts.length > 0 ? parts[parts.length - 1] : trimmed;
|
|
2816
|
-
return compactStatusText(filename, 80);
|
|
2817
|
-
}
|
|
2818
|
-
function extractStatusUrlDomain(value) {
|
|
2819
|
-
if (typeof value !== "string") {
|
|
2820
|
-
return void 0;
|
|
2821
|
-
}
|
|
2822
|
-
const trimmed = value.trim();
|
|
2823
|
-
if (!trimmed) {
|
|
2824
|
-
return void 0;
|
|
2825
|
-
}
|
|
2826
|
-
try {
|
|
2827
|
-
const parsed = new URL(trimmed);
|
|
2828
|
-
return parsed.hostname || void 0;
|
|
2829
|
-
} catch {
|
|
2830
|
-
return void 0;
|
|
2831
|
-
}
|
|
2832
|
-
}
|
|
2833
2704
|
|
|
2834
2705
|
// src/chat/slack/mrkdwn.ts
|
|
2835
2706
|
function ensureBlockSpacing(text) {
|
|
@@ -3483,7 +3354,7 @@ function buildSystemPrompt(params) {
|
|
|
3483
3354
|
[
|
|
3484
3355
|
"- For factual or external questions, run tools/skills first, then answer from evidence.",
|
|
3485
3356
|
"- Use tool descriptions as the source of truth for when each tool should or should not be called.",
|
|
3486
|
-
"- Use `reportProgress` only
|
|
3357
|
+
"- Use `reportProgress` only for sparse, meaningful progress updates. Pass a short user-facing status message, and do not call it for every tool or small substep.",
|
|
3487
3358
|
"- When using CLI tools through `bash`, prefer deterministic non-interactive flags and avoid commands that wait for prompts or editors.",
|
|
3488
3359
|
"- Keep routine setup and research steps silent in user-facing replies. Do not narrate duplicate checks, credential issuance, file writes, or similar internal progress unless the result is user-relevant.",
|
|
3489
3360
|
"- If a routine prerequisite check finds nothing notable, omit it entirely from the final reply and report only the user-relevant outcome.",
|
|
@@ -5193,23 +5064,12 @@ function createReadFileTool() {
|
|
|
5193
5064
|
import { Type as Type6 } from "@sinclair/typebox";
|
|
5194
5065
|
function createReportProgressTool() {
|
|
5195
5066
|
return tool({
|
|
5196
|
-
description: "Update assistant status
|
|
5067
|
+
description: "Update assistant status with a short user-facing progress message. Use this sparingly for meaningful progress changes, not for every tool call or minor substep.",
|
|
5197
5068
|
inputSchema: Type6.Object({
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
Type6.Literal("executing"),
|
|
5203
|
-
Type6.Literal("reviewing"),
|
|
5204
|
-
Type6.Literal("drafting")
|
|
5205
|
-
]),
|
|
5206
|
-
detail: Type6.Optional(
|
|
5207
|
-
Type6.String({
|
|
5208
|
-
minLength: 1,
|
|
5209
|
-
maxLength: 40,
|
|
5210
|
-
description: "Optional short user-facing detail, such as docs, tests, source files, or reply."
|
|
5211
|
-
})
|
|
5212
|
-
)
|
|
5069
|
+
message: Type6.String({
|
|
5070
|
+
minLength: 1,
|
|
5071
|
+
description: "Short user-facing progress message. The UI truncates it if needed."
|
|
5072
|
+
})
|
|
5213
5073
|
})
|
|
5214
5074
|
});
|
|
5215
5075
|
}
|
|
@@ -7254,563 +7114,144 @@ function throwSandboxOperationError(action, error, includeMissingPath = false) {
|
|
|
7254
7114
|
import { Sandbox } from "@vercel/sandbox";
|
|
7255
7115
|
import { createBashTool as createBashTool2 } from "bash-tool";
|
|
7256
7116
|
|
|
7257
|
-
// src/chat/
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7285
|
-
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
|
|
7290
|
-
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
listing: {
|
|
7296
|
-
defaultContext: "items",
|
|
7297
|
-
variants: ["Listing", "Gathering", "Collecting", "Enumerating"]
|
|
7298
|
-
},
|
|
7299
|
-
posting: {
|
|
7300
|
-
defaultContext: "reply",
|
|
7301
|
-
variants: ["Posting", "Sending", "Delivering", "Dispatching"]
|
|
7302
|
-
},
|
|
7303
|
-
adding: {
|
|
7304
|
-
defaultContext: "details",
|
|
7305
|
-
variants: ["Adding", "Applying", "Attaching", "Dropping in"]
|
|
7306
|
-
},
|
|
7307
|
-
running: {
|
|
7308
|
-
defaultContext: "tasks",
|
|
7309
|
-
variants: ["Running", "Executing", "Launching", "Processing"]
|
|
7117
|
+
// src/chat/sandbox/skill-sync.ts
|
|
7118
|
+
import fs3 from "fs/promises";
|
|
7119
|
+
import path5 from "path";
|
|
7120
|
+
|
|
7121
|
+
// src/chat/sandbox/eval-gh-stub.ts
|
|
7122
|
+
function buildEvalGitHubCliStub() {
|
|
7123
|
+
return `#!/usr/bin/env node
|
|
7124
|
+
const fs = require("node:fs");
|
|
7125
|
+
const path = require("node:path");
|
|
7126
|
+
const { spawnSync } = require("node:child_process");
|
|
7127
|
+
|
|
7128
|
+
const args = process.argv.slice(2);
|
|
7129
|
+
const statePath = "/vercel/sandbox/.junior/eval-gh-state.json";
|
|
7130
|
+
const fallbackBinaries = ["/usr/bin/gh", "/usr/local/bin/gh", "/bin/gh"];
|
|
7131
|
+
const flagsWithValues = new Set([
|
|
7132
|
+
"--repo",
|
|
7133
|
+
"--title",
|
|
7134
|
+
"--body",
|
|
7135
|
+
"--body-file",
|
|
7136
|
+
"--json",
|
|
7137
|
+
"--search",
|
|
7138
|
+
"--state",
|
|
7139
|
+
"--limit",
|
|
7140
|
+
"--method",
|
|
7141
|
+
"--jq",
|
|
7142
|
+
"--template",
|
|
7143
|
+
"--hostname",
|
|
7144
|
+
]);
|
|
7145
|
+
|
|
7146
|
+
function getFlag(name) {
|
|
7147
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7148
|
+
const value = args[index];
|
|
7149
|
+
if (value === name) {
|
|
7150
|
+
return args[index + 1];
|
|
7151
|
+
}
|
|
7152
|
+
if (value.startsWith(name + "=")) {
|
|
7153
|
+
return value.slice(name.length + 1);
|
|
7154
|
+
}
|
|
7310
7155
|
}
|
|
7311
|
-
|
|
7312
|
-
function makeAssistantStatus(kind, context, options) {
|
|
7313
|
-
return {
|
|
7314
|
-
kind,
|
|
7315
|
-
...context ? { context } : {},
|
|
7316
|
-
...options?.source ? { source: options.source } : {}
|
|
7317
|
-
};
|
|
7156
|
+
return undefined;
|
|
7318
7157
|
}
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
const
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7158
|
+
|
|
7159
|
+
function getPositionals() {
|
|
7160
|
+
const values = [];
|
|
7161
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7162
|
+
const value = args[index];
|
|
7163
|
+
if (flagsWithValues.has(value)) {
|
|
7164
|
+
index += 1;
|
|
7165
|
+
continue;
|
|
7166
|
+
}
|
|
7167
|
+
if (value.startsWith("--") && value.includes("=")) {
|
|
7168
|
+
continue;
|
|
7169
|
+
}
|
|
7170
|
+
if (value.startsWith("-")) {
|
|
7171
|
+
continue;
|
|
7172
|
+
}
|
|
7173
|
+
values.push(value);
|
|
7174
|
+
}
|
|
7175
|
+
return values;
|
|
7176
|
+
}
|
|
7177
|
+
|
|
7178
|
+
function loadState() {
|
|
7179
|
+
try {
|
|
7180
|
+
return JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
7181
|
+
} catch {
|
|
7182
|
+
return { nextIssueNumber: 101, issues: {} };
|
|
7183
|
+
}
|
|
7184
|
+
}
|
|
7185
|
+
|
|
7186
|
+
function saveState(state) {
|
|
7187
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
7188
|
+
fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
7189
|
+
}
|
|
7190
|
+
|
|
7191
|
+
function issueUrl(repo, number) {
|
|
7192
|
+
return "https://github.com/" + repo + "/issues/" + number;
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
function repoValue() {
|
|
7196
|
+
return getFlag("--repo") || "getsentry/junior";
|
|
7197
|
+
}
|
|
7198
|
+
|
|
7199
|
+
function readBody() {
|
|
7200
|
+
const bodyFile = getFlag("--body-file");
|
|
7201
|
+
if (bodyFile) {
|
|
7202
|
+
try {
|
|
7203
|
+
return fs.readFileSync(bodyFile, "utf8");
|
|
7204
|
+
} catch {
|
|
7205
|
+
return "";
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7208
|
+
return getFlag("--body") || "";
|
|
7209
|
+
}
|
|
7210
|
+
|
|
7211
|
+
function defaultIssue(repo, number) {
|
|
7327
7212
|
return {
|
|
7328
|
-
|
|
7329
|
-
|
|
7213
|
+
number,
|
|
7214
|
+
title: "Eval issue",
|
|
7215
|
+
body: "",
|
|
7216
|
+
state: "OPEN",
|
|
7217
|
+
url: issueUrl(repo, number),
|
|
7218
|
+
labels: [],
|
|
7219
|
+
assignees: [],
|
|
7220
|
+
author: { login: "junior-eval" },
|
|
7330
7221
|
};
|
|
7331
7222
|
}
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
args.messages.map((message) => truncateStatusText(normalizeSlackStatusText(message))).filter((message) => message.length > 0)
|
|
7337
|
-
)
|
|
7338
|
-
);
|
|
7339
|
-
if (normalized.length === 0) {
|
|
7340
|
-
return void 0;
|
|
7223
|
+
|
|
7224
|
+
function pickFields(record, csv) {
|
|
7225
|
+
if (!csv) {
|
|
7226
|
+
return record;
|
|
7341
7227
|
}
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7228
|
+
return Object.fromEntries(
|
|
7229
|
+
csv
|
|
7230
|
+
.split(",")
|
|
7231
|
+
.map((value) => value.trim())
|
|
7232
|
+
.filter(Boolean)
|
|
7233
|
+
.map((key) => [key, key in record ? record[key] : null]),
|
|
7234
|
+
);
|
|
7235
|
+
}
|
|
7236
|
+
|
|
7237
|
+
function outputJson(value) {
|
|
7238
|
+
fs.writeFileSync(process.stdout.fd, JSON.stringify(value, null, 2) + "\\n");
|
|
7239
|
+
}
|
|
7240
|
+
|
|
7241
|
+
function outputText(value) {
|
|
7242
|
+
fs.writeFileSync(process.stdout.fd, value);
|
|
7243
|
+
}
|
|
7244
|
+
|
|
7245
|
+
function fallbackToRealGh() {
|
|
7246
|
+
for (const binary of fallbackBinaries) {
|
|
7247
|
+
if (!fs.existsSync(binary)) {
|
|
7248
|
+
continue;
|
|
7249
|
+
}
|
|
7250
|
+
const result = spawnSync(binary, args, { stdio: "inherit" });
|
|
7251
|
+
process.exit(result.status ?? 1);
|
|
7349
7252
|
}
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
// src/chat/slack/assistant-thread/status-scheduler.ts
|
|
7354
|
-
var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
7355
|
-
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
7356
|
-
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
7357
|
-
function createAssistantStatusScheduler(args) {
|
|
7358
|
-
const now = args.now ?? (() => Date.now());
|
|
7359
|
-
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
7360
|
-
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
7361
|
-
const random = args.random ?? Math.random;
|
|
7362
|
-
const loadingMessages = selectAssistantLoadingMessages({
|
|
7363
|
-
messages: args.loadingMessages ?? [],
|
|
7364
|
-
random
|
|
7365
|
-
});
|
|
7366
|
-
const defaultStatus = makeAssistantStatus("thinking");
|
|
7367
|
-
let active = false;
|
|
7368
|
-
let currentKey = "";
|
|
7369
|
-
let currentStatus = defaultStatus;
|
|
7370
|
-
let currentVisibleStatus = "";
|
|
7371
|
-
let currentLoadingMessages;
|
|
7372
|
-
let lastStatusAt = 0;
|
|
7373
|
-
let pendingStatus = null;
|
|
7374
|
-
let pendingKey = "";
|
|
7375
|
-
let pendingTimer = null;
|
|
7376
|
-
let rotationTimer = null;
|
|
7377
|
-
let inflightStatusUpdate = Promise.resolve();
|
|
7378
|
-
const enqueueStatusUpdate = (task) => {
|
|
7379
|
-
const request = inflightStatusUpdate.catch(() => void 0).then(async () => {
|
|
7380
|
-
await task();
|
|
7381
|
-
});
|
|
7382
|
-
inflightStatusUpdate = request.catch(() => void 0);
|
|
7383
|
-
return request;
|
|
7384
|
-
};
|
|
7385
|
-
const scheduleRotation = () => {
|
|
7386
|
-
if (rotationTimer) {
|
|
7387
|
-
clearTimer(rotationTimer);
|
|
7388
|
-
rotationTimer = null;
|
|
7389
|
-
}
|
|
7390
|
-
if (!active || !currentVisibleStatus) {
|
|
7391
|
-
return;
|
|
7392
|
-
}
|
|
7393
|
-
rotationTimer = setTimer(() => {
|
|
7394
|
-
rotationTimer = null;
|
|
7395
|
-
if (!active || !currentVisibleStatus) {
|
|
7396
|
-
return;
|
|
7397
|
-
}
|
|
7398
|
-
void postStatus(currentVisibleStatus, currentLoadingMessages);
|
|
7399
|
-
}, STATUS_ROTATION_INTERVAL_MS);
|
|
7400
|
-
};
|
|
7401
|
-
const getLoadingMessagesForStatus = (_status, visible) => {
|
|
7402
|
-
if (!visible) {
|
|
7403
|
-
return void 0;
|
|
7404
|
-
}
|
|
7405
|
-
return [visible];
|
|
7406
|
-
};
|
|
7407
|
-
const getInitialStatusText = () => {
|
|
7408
|
-
if (loadingMessages?.length) {
|
|
7409
|
-
return loadingMessages[0];
|
|
7410
|
-
}
|
|
7411
|
-
return renderAssistantStatus({
|
|
7412
|
-
status: defaultStatus,
|
|
7413
|
-
random
|
|
7414
|
-
}).visible;
|
|
7415
|
-
};
|
|
7416
|
-
const haveSameLoadingMessages = (left, right) => {
|
|
7417
|
-
if (left === right) {
|
|
7418
|
-
return true;
|
|
7419
|
-
}
|
|
7420
|
-
if (!left || !right || left.length !== right.length) {
|
|
7421
|
-
return false;
|
|
7422
|
-
}
|
|
7423
|
-
return left.every((message, index) => message === right[index]);
|
|
7424
|
-
};
|
|
7425
|
-
const postStatus = async (text, nextLoadingMessages) => {
|
|
7426
|
-
if (!text && !currentVisibleStatus) {
|
|
7427
|
-
return;
|
|
7428
|
-
}
|
|
7429
|
-
currentVisibleStatus = text;
|
|
7430
|
-
currentLoadingMessages = nextLoadingMessages;
|
|
7431
|
-
lastStatusAt = now();
|
|
7432
|
-
scheduleRotation();
|
|
7433
|
-
await enqueueStatusUpdate(async () => {
|
|
7434
|
-
await args.sendStatus(text, nextLoadingMessages);
|
|
7435
|
-
});
|
|
7436
|
-
};
|
|
7437
|
-
const postRenderedStatus = async (status) => {
|
|
7438
|
-
const presentation = renderAssistantStatus({
|
|
7439
|
-
status,
|
|
7440
|
-
random
|
|
7441
|
-
});
|
|
7442
|
-
const nextLoadingMessages = getLoadingMessagesForStatus(
|
|
7443
|
-
status,
|
|
7444
|
-
presentation.visible
|
|
7445
|
-
);
|
|
7446
|
-
currentStatus = status;
|
|
7447
|
-
currentKey = presentation.key;
|
|
7448
|
-
await postStatus(presentation.visible, nextLoadingMessages);
|
|
7449
|
-
};
|
|
7450
|
-
const clearPending = () => {
|
|
7451
|
-
if (pendingTimer) {
|
|
7452
|
-
clearTimer(pendingTimer);
|
|
7453
|
-
pendingTimer = null;
|
|
7454
|
-
}
|
|
7455
|
-
pendingStatus = null;
|
|
7456
|
-
pendingKey = "";
|
|
7457
|
-
};
|
|
7458
|
-
const flushPending = async () => {
|
|
7459
|
-
if (!active || !pendingStatus) {
|
|
7460
|
-
clearPending();
|
|
7461
|
-
return;
|
|
7462
|
-
}
|
|
7463
|
-
const next = pendingStatus;
|
|
7464
|
-
clearPending();
|
|
7465
|
-
const nextPresentation = renderAssistantStatus({
|
|
7466
|
-
status: next,
|
|
7467
|
-
random
|
|
7468
|
-
});
|
|
7469
|
-
if (nextPresentation.key !== currentKey) {
|
|
7470
|
-
await postRenderedStatus(next);
|
|
7471
|
-
}
|
|
7472
|
-
};
|
|
7473
|
-
return {
|
|
7474
|
-
start() {
|
|
7475
|
-
active = true;
|
|
7476
|
-
clearPending();
|
|
7477
|
-
currentStatus = defaultStatus;
|
|
7478
|
-
currentKey = "initial";
|
|
7479
|
-
void postStatus(getInitialStatusText(), loadingMessages);
|
|
7480
|
-
},
|
|
7481
|
-
async stop() {
|
|
7482
|
-
active = false;
|
|
7483
|
-
clearPending();
|
|
7484
|
-
if (rotationTimer) {
|
|
7485
|
-
clearTimer(rotationTimer);
|
|
7486
|
-
rotationTimer = null;
|
|
7487
|
-
}
|
|
7488
|
-
currentKey = "";
|
|
7489
|
-
await postStatus("");
|
|
7490
|
-
},
|
|
7491
|
-
update(status) {
|
|
7492
|
-
if (!active) {
|
|
7493
|
-
return;
|
|
7494
|
-
}
|
|
7495
|
-
const presentation = renderAssistantStatus({
|
|
7496
|
-
status,
|
|
7497
|
-
random
|
|
7498
|
-
});
|
|
7499
|
-
if (!presentation.visible) {
|
|
7500
|
-
return;
|
|
7501
|
-
}
|
|
7502
|
-
if (status.source !== "major" && (currentStatus.source === "major" || pendingStatus?.source === "major")) {
|
|
7503
|
-
return;
|
|
7504
|
-
}
|
|
7505
|
-
if (presentation.key === currentKey || presentation.key === pendingKey) {
|
|
7506
|
-
return;
|
|
7507
|
-
}
|
|
7508
|
-
if (presentation.visible === currentVisibleStatus) {
|
|
7509
|
-
clearPending();
|
|
7510
|
-
currentStatus = status;
|
|
7511
|
-
currentKey = presentation.key;
|
|
7512
|
-
const nextLoadingMessages = getLoadingMessagesForStatus(
|
|
7513
|
-
status,
|
|
7514
|
-
presentation.visible
|
|
7515
|
-
);
|
|
7516
|
-
if (!haveSameLoadingMessages(currentLoadingMessages, nextLoadingMessages)) {
|
|
7517
|
-
void postStatus(presentation.visible, nextLoadingMessages);
|
|
7518
|
-
}
|
|
7519
|
-
return;
|
|
7520
|
-
}
|
|
7521
|
-
const elapsed = now() - lastStatusAt;
|
|
7522
|
-
const waitMs = Math.max(
|
|
7523
|
-
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
7524
|
-
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
7525
|
-
0
|
|
7526
|
-
);
|
|
7527
|
-
if (waitMs <= 0) {
|
|
7528
|
-
clearPending();
|
|
7529
|
-
void postRenderedStatus(status);
|
|
7530
|
-
return;
|
|
7531
|
-
}
|
|
7532
|
-
pendingStatus = status;
|
|
7533
|
-
pendingKey = presentation.key;
|
|
7534
|
-
if (pendingTimer) {
|
|
7535
|
-
return;
|
|
7536
|
-
}
|
|
7537
|
-
pendingTimer = setTimer(
|
|
7538
|
-
() => {
|
|
7539
|
-
pendingTimer = null;
|
|
7540
|
-
void flushPending();
|
|
7541
|
-
},
|
|
7542
|
-
Math.max(1, waitMs)
|
|
7543
|
-
);
|
|
7544
|
-
}
|
|
7545
|
-
};
|
|
7546
|
-
}
|
|
7547
|
-
|
|
7548
|
-
// src/chat/slack/assistant-thread/status-send.ts
|
|
7549
|
-
var SLACK_ASSISTANT_ACTIVE_STATUS = "is working on your request...";
|
|
7550
|
-
function createSlackAdapterStatusSender(args) {
|
|
7551
|
-
const adapter = args.getSlackAdapter();
|
|
7552
|
-
const boundToken = getSlackAdapterRequestToken(adapter);
|
|
7553
|
-
return async (text, loadingMessages) => {
|
|
7554
|
-
const channelId = args.channelId;
|
|
7555
|
-
const threadTs = args.threadTs;
|
|
7556
|
-
if (!channelId || !threadTs) {
|
|
7557
|
-
return;
|
|
7558
|
-
}
|
|
7559
|
-
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
7560
|
-
if (!normalizedChannelId) {
|
|
7561
|
-
return;
|
|
7562
|
-
}
|
|
7563
|
-
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
7564
|
-
try {
|
|
7565
|
-
await runWithBoundSlackToken(
|
|
7566
|
-
adapter,
|
|
7567
|
-
boundToken,
|
|
7568
|
-
() => adapter.setAssistantStatus(
|
|
7569
|
-
normalizedChannelId,
|
|
7570
|
-
threadTs,
|
|
7571
|
-
text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
7572
|
-
nextLoadingMessages
|
|
7573
|
-
)
|
|
7574
|
-
);
|
|
7575
|
-
} catch (error) {
|
|
7576
|
-
logAssistantStatusFailure({
|
|
7577
|
-
status: text,
|
|
7578
|
-
error,
|
|
7579
|
-
channelId,
|
|
7580
|
-
normalizedChannelId,
|
|
7581
|
-
threadTs
|
|
7582
|
-
});
|
|
7583
|
-
}
|
|
7584
|
-
};
|
|
7585
|
-
}
|
|
7586
|
-
function createSlackWebApiStatusSender(args) {
|
|
7587
|
-
const getClient2 = args.getSlackClient ?? getSlackClient;
|
|
7588
|
-
return async (text, loadingMessages) => {
|
|
7589
|
-
const channelId = args.channelId;
|
|
7590
|
-
const threadTs = args.threadTs;
|
|
7591
|
-
if (!channelId || !threadTs) {
|
|
7592
|
-
return;
|
|
7593
|
-
}
|
|
7594
|
-
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
7595
|
-
if (!normalizedChannelId) {
|
|
7596
|
-
return;
|
|
7597
|
-
}
|
|
7598
|
-
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
7599
|
-
try {
|
|
7600
|
-
await getClient2().assistant.threads.setStatus({
|
|
7601
|
-
channel_id: normalizedChannelId,
|
|
7602
|
-
thread_ts: threadTs,
|
|
7603
|
-
status: text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
7604
|
-
...nextLoadingMessages ? { loading_messages: nextLoadingMessages } : {}
|
|
7605
|
-
});
|
|
7606
|
-
} catch (error) {
|
|
7607
|
-
logAssistantStatusFailure({
|
|
7608
|
-
status: text,
|
|
7609
|
-
error,
|
|
7610
|
-
channelId,
|
|
7611
|
-
normalizedChannelId,
|
|
7612
|
-
threadTs
|
|
7613
|
-
});
|
|
7614
|
-
}
|
|
7615
|
-
};
|
|
7616
|
-
}
|
|
7617
|
-
function getSlackAdapterRequestToken(adapter) {
|
|
7618
|
-
const token = adapter.requestContext?.getStore()?.token;
|
|
7619
|
-
if (typeof token !== "string") {
|
|
7620
|
-
return void 0;
|
|
7621
|
-
}
|
|
7622
|
-
const trimmed = token.trim();
|
|
7623
|
-
return trimmed || void 0;
|
|
7624
|
-
}
|
|
7625
|
-
async function runWithBoundSlackToken(adapter, token, task) {
|
|
7626
|
-
if (!token) {
|
|
7627
|
-
return await task();
|
|
7628
|
-
}
|
|
7629
|
-
return await adapter.withBotToken(token, task);
|
|
7630
|
-
}
|
|
7631
|
-
function logAssistantStatusFailure(args) {
|
|
7632
|
-
logWarn(
|
|
7633
|
-
"assistant_status_update_failed",
|
|
7634
|
-
{},
|
|
7635
|
-
{
|
|
7636
|
-
"app.slack.status_text": args.status || "(clear)",
|
|
7637
|
-
"app.slack.channel_id_raw": args.channelId,
|
|
7638
|
-
"app.slack.channel_id": args.normalizedChannelId,
|
|
7639
|
-
"app.slack.thread_ts": args.threadTs,
|
|
7640
|
-
"error.message": args.error instanceof Error ? args.error.message : String(args.error)
|
|
7641
|
-
},
|
|
7642
|
-
`Failed to update assistant status channel=${args.normalizedChannelId} raw=${args.channelId} thread=${args.threadTs}`
|
|
7643
|
-
);
|
|
7644
|
-
}
|
|
7645
|
-
|
|
7646
|
-
// src/chat/slack/assistant-thread/status.ts
|
|
7647
|
-
function createSlackAdapterAssistantStatusSession(args) {
|
|
7648
|
-
return createAssistantStatusScheduler({
|
|
7649
|
-
sendStatus: createSlackAdapterStatusSender({
|
|
7650
|
-
channelId: args.channelId,
|
|
7651
|
-
threadTs: args.threadTs,
|
|
7652
|
-
getSlackAdapter: args.getSlackAdapter
|
|
7653
|
-
}),
|
|
7654
|
-
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
7655
|
-
now: args.now,
|
|
7656
|
-
setTimer: args.setTimer,
|
|
7657
|
-
clearTimer: args.clearTimer,
|
|
7658
|
-
random: args.random
|
|
7659
|
-
});
|
|
7660
|
-
}
|
|
7661
|
-
function createSlackWebApiAssistantStatusSession(args) {
|
|
7662
|
-
return createAssistantStatusScheduler({
|
|
7663
|
-
sendStatus: createSlackWebApiStatusSender({
|
|
7664
|
-
channelId: args.channelId,
|
|
7665
|
-
threadTs: args.threadTs,
|
|
7666
|
-
getSlackClient: args.getSlackClient
|
|
7667
|
-
}),
|
|
7668
|
-
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
7669
|
-
now: args.now,
|
|
7670
|
-
setTimer: args.setTimer,
|
|
7671
|
-
clearTimer: args.clearTimer,
|
|
7672
|
-
random: args.random
|
|
7673
|
-
});
|
|
7674
|
-
}
|
|
7675
|
-
|
|
7676
|
-
// src/chat/sandbox/skill-sync.ts
|
|
7677
|
-
import fs3 from "fs/promises";
|
|
7678
|
-
import path5 from "path";
|
|
7679
|
-
|
|
7680
|
-
// src/chat/sandbox/eval-gh-stub.ts
|
|
7681
|
-
function buildEvalGitHubCliStub() {
|
|
7682
|
-
return `#!/usr/bin/env node
|
|
7683
|
-
const fs = require("node:fs");
|
|
7684
|
-
const path = require("node:path");
|
|
7685
|
-
const { spawnSync } = require("node:child_process");
|
|
7686
|
-
|
|
7687
|
-
const args = process.argv.slice(2);
|
|
7688
|
-
const statePath = "/vercel/sandbox/.junior/eval-gh-state.json";
|
|
7689
|
-
const fallbackBinaries = ["/usr/bin/gh", "/usr/local/bin/gh", "/bin/gh"];
|
|
7690
|
-
const flagsWithValues = new Set([
|
|
7691
|
-
"--repo",
|
|
7692
|
-
"--title",
|
|
7693
|
-
"--body",
|
|
7694
|
-
"--body-file",
|
|
7695
|
-
"--json",
|
|
7696
|
-
"--search",
|
|
7697
|
-
"--state",
|
|
7698
|
-
"--limit",
|
|
7699
|
-
"--method",
|
|
7700
|
-
"--jq",
|
|
7701
|
-
"--template",
|
|
7702
|
-
"--hostname",
|
|
7703
|
-
]);
|
|
7704
|
-
|
|
7705
|
-
function getFlag(name) {
|
|
7706
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
7707
|
-
const value = args[index];
|
|
7708
|
-
if (value === name) {
|
|
7709
|
-
return args[index + 1];
|
|
7710
|
-
}
|
|
7711
|
-
if (value.startsWith(name + "=")) {
|
|
7712
|
-
return value.slice(name.length + 1);
|
|
7713
|
-
}
|
|
7714
|
-
}
|
|
7715
|
-
return undefined;
|
|
7716
|
-
}
|
|
7717
|
-
|
|
7718
|
-
function getPositionals() {
|
|
7719
|
-
const values = [];
|
|
7720
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
7721
|
-
const value = args[index];
|
|
7722
|
-
if (flagsWithValues.has(value)) {
|
|
7723
|
-
index += 1;
|
|
7724
|
-
continue;
|
|
7725
|
-
}
|
|
7726
|
-
if (value.startsWith("--") && value.includes("=")) {
|
|
7727
|
-
continue;
|
|
7728
|
-
}
|
|
7729
|
-
if (value.startsWith("-")) {
|
|
7730
|
-
continue;
|
|
7731
|
-
}
|
|
7732
|
-
values.push(value);
|
|
7733
|
-
}
|
|
7734
|
-
return values;
|
|
7735
|
-
}
|
|
7736
|
-
|
|
7737
|
-
function loadState() {
|
|
7738
|
-
try {
|
|
7739
|
-
return JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
7740
|
-
} catch {
|
|
7741
|
-
return { nextIssueNumber: 101, issues: {} };
|
|
7742
|
-
}
|
|
7743
|
-
}
|
|
7744
|
-
|
|
7745
|
-
function saveState(state) {
|
|
7746
|
-
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
7747
|
-
fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
7748
|
-
}
|
|
7749
|
-
|
|
7750
|
-
function issueUrl(repo, number) {
|
|
7751
|
-
return "https://github.com/" + repo + "/issues/" + number;
|
|
7752
|
-
}
|
|
7753
|
-
|
|
7754
|
-
function repoValue() {
|
|
7755
|
-
return getFlag("--repo") || "getsentry/junior";
|
|
7756
|
-
}
|
|
7757
|
-
|
|
7758
|
-
function readBody() {
|
|
7759
|
-
const bodyFile = getFlag("--body-file");
|
|
7760
|
-
if (bodyFile) {
|
|
7761
|
-
try {
|
|
7762
|
-
return fs.readFileSync(bodyFile, "utf8");
|
|
7763
|
-
} catch {
|
|
7764
|
-
return "";
|
|
7765
|
-
}
|
|
7766
|
-
}
|
|
7767
|
-
return getFlag("--body") || "";
|
|
7768
|
-
}
|
|
7769
|
-
|
|
7770
|
-
function defaultIssue(repo, number) {
|
|
7771
|
-
return {
|
|
7772
|
-
number,
|
|
7773
|
-
title: "Eval issue",
|
|
7774
|
-
body: "",
|
|
7775
|
-
state: "OPEN",
|
|
7776
|
-
url: issueUrl(repo, number),
|
|
7777
|
-
labels: [],
|
|
7778
|
-
assignees: [],
|
|
7779
|
-
author: { login: "junior-eval" },
|
|
7780
|
-
};
|
|
7781
|
-
}
|
|
7782
|
-
|
|
7783
|
-
function pickFields(record, csv) {
|
|
7784
|
-
if (!csv) {
|
|
7785
|
-
return record;
|
|
7786
|
-
}
|
|
7787
|
-
return Object.fromEntries(
|
|
7788
|
-
csv
|
|
7789
|
-
.split(",")
|
|
7790
|
-
.map((value) => value.trim())
|
|
7791
|
-
.filter(Boolean)
|
|
7792
|
-
.map((key) => [key, key in record ? record[key] : null]),
|
|
7793
|
-
);
|
|
7794
|
-
}
|
|
7795
|
-
|
|
7796
|
-
function outputJson(value) {
|
|
7797
|
-
fs.writeFileSync(process.stdout.fd, JSON.stringify(value, null, 2) + "\\n");
|
|
7798
|
-
}
|
|
7799
|
-
|
|
7800
|
-
function outputText(value) {
|
|
7801
|
-
fs.writeFileSync(process.stdout.fd, value);
|
|
7802
|
-
}
|
|
7803
|
-
|
|
7804
|
-
function fallbackToRealGh() {
|
|
7805
|
-
for (const binary of fallbackBinaries) {
|
|
7806
|
-
if (!fs.existsSync(binary)) {
|
|
7807
|
-
continue;
|
|
7808
|
-
}
|
|
7809
|
-
const result = spawnSync(binary, args, { stdio: "inherit" });
|
|
7810
|
-
process.exit(result.status ?? 1);
|
|
7811
|
-
}
|
|
7812
|
-
process.stderr.write("gh stub: unsupported command\\n");
|
|
7813
|
-
process.exit(1);
|
|
7253
|
+
process.stderr.write("gh stub: unsupported command\\n");
|
|
7254
|
+
process.exit(1);
|
|
7814
7255
|
}
|
|
7815
7256
|
|
|
7816
7257
|
if (args.length === 0 || args[0] === "--version" || args[0] === "version") {
|
|
@@ -8233,12 +7674,6 @@ var SANDBOX_RUNTIME = "node22";
|
|
|
8233
7674
|
var SANDBOX_RUNTIME_BIN_DIR = `${SANDBOX_WORKSPACE_ROOT}/.junior/bin`;
|
|
8234
7675
|
var SNAPSHOT_BOOT_RETRY_COUNT = 3;
|
|
8235
7676
|
var SNAPSHOT_BOOT_RETRY_DELAY_MS = 1e3;
|
|
8236
|
-
var SNAPSHOT_PHASE_STATUS = {
|
|
8237
|
-
resolve_start: { kind: "loading", context: "sandbox snapshot cache" },
|
|
8238
|
-
waiting_for_lock: { kind: "loading", context: "sandbox snapshot build" },
|
|
8239
|
-
building_snapshot: { kind: "creating", context: "sandbox snapshot" },
|
|
8240
|
-
cache_hit: { kind: "loading", context: "sandbox snapshot" }
|
|
8241
|
-
};
|
|
8242
7677
|
function mergeNetworkPolicyWithHeaderTransforms(networkPolicy, headerTransforms) {
|
|
8243
7678
|
const basePolicy = networkPolicy && typeof networkPolicy === "object" && !Array.isArray(networkPolicy) ? { ...networkPolicy } : {};
|
|
8244
7679
|
const existingAllowRaw = basePolicy.allow;
|
|
@@ -8279,26 +7714,6 @@ function sleep2(ms) {
|
|
|
8279
7714
|
setTimeout(resolve, ms);
|
|
8280
7715
|
});
|
|
8281
7716
|
}
|
|
8282
|
-
function createStatusEmitter(emitStatus) {
|
|
8283
|
-
let statusCount = 0;
|
|
8284
|
-
const sentStatuses = /* @__PURE__ */ new Set();
|
|
8285
|
-
const emit = async (status) => {
|
|
8286
|
-
const statusKey = `${status.kind}:${status.context ?? ""}`;
|
|
8287
|
-
if (!emitStatus || statusCount >= 4 || sentStatuses.has(statusKey)) {
|
|
8288
|
-
return;
|
|
8289
|
-
}
|
|
8290
|
-
sentStatuses.add(statusKey);
|
|
8291
|
-
statusCount += 1;
|
|
8292
|
-
await emitStatus(status);
|
|
8293
|
-
};
|
|
8294
|
-
const reportSnapshotPhase = async (phase) => {
|
|
8295
|
-
const status = SNAPSHOT_PHASE_STATUS[phase];
|
|
8296
|
-
if (status) {
|
|
8297
|
-
await emit(makeAssistantStatus(status.kind, status.context));
|
|
8298
|
-
}
|
|
8299
|
-
};
|
|
8300
|
-
return { emit, reportSnapshotPhase };
|
|
8301
|
-
}
|
|
8302
7717
|
function parseKeepAliveMs() {
|
|
8303
7718
|
const parsed = Number.parseInt(
|
|
8304
7719
|
process.env.VERCEL_SANDBOX_KEEPALIVE_MS ?? "0",
|
|
@@ -8316,23 +7731,6 @@ function createSandboxSessionManager(options) {
|
|
|
8316
7731
|
const traceContext = options?.traceContext ?? {};
|
|
8317
7732
|
const dependencyProfileHash = getRuntimeDependencyProfileHash(SANDBOX_RUNTIME);
|
|
8318
7733
|
const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
|
|
8319
|
-
const emitSandboxStatus = async (source, statusEmitter, status) => {
|
|
8320
|
-
logInfo(
|
|
8321
|
-
"sandbox_status_emitted",
|
|
8322
|
-
traceContext,
|
|
8323
|
-
{
|
|
8324
|
-
"app.sandbox.status.source": source,
|
|
8325
|
-
"app.sandbox.status.kind": status.kind,
|
|
8326
|
-
...status.context ? { "app.sandbox.status.context": status.context } : {}
|
|
8327
|
-
},
|
|
8328
|
-
"Sandbox status emitted"
|
|
8329
|
-
);
|
|
8330
|
-
if (typeof statusEmitter === "function") {
|
|
8331
|
-
await statusEmitter(status);
|
|
8332
|
-
return;
|
|
8333
|
-
}
|
|
8334
|
-
await statusEmitter.emit(status);
|
|
8335
|
-
};
|
|
8336
7734
|
const clearSession = () => {
|
|
8337
7735
|
sandbox = null;
|
|
8338
7736
|
sandboxIdHint = void 0;
|
|
@@ -8408,16 +7806,9 @@ function createSandboxSessionManager(options) {
|
|
|
8408
7806
|
});
|
|
8409
7807
|
return replacement;
|
|
8410
7808
|
};
|
|
8411
|
-
const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials
|
|
7809
|
+
const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials) => {
|
|
8412
7810
|
for (let attempt = 0; attempt < SNAPSHOT_BOOT_RETRY_COUNT; attempt += 1) {
|
|
8413
7811
|
try {
|
|
8414
|
-
if (emitStatus) {
|
|
8415
|
-
await emitSandboxStatus(
|
|
8416
|
-
"snapshot_boot",
|
|
8417
|
-
emitStatus,
|
|
8418
|
-
makeAssistantStatus("loading", "sandbox")
|
|
8419
|
-
);
|
|
8420
|
-
}
|
|
8421
7812
|
return await Sandbox.create({
|
|
8422
7813
|
timeout: timeoutMs,
|
|
8423
7814
|
source: {
|
|
@@ -8450,13 +7841,8 @@ function createSandboxSessionManager(options) {
|
|
|
8450
7841
|
});
|
|
8451
7842
|
};
|
|
8452
7843
|
const createSandboxFromResolvedSnapshot = async (params) => {
|
|
8453
|
-
const { runtime, snapshot, sandboxCredentials
|
|
7844
|
+
const { runtime, snapshot, sandboxCredentials } = params;
|
|
8454
7845
|
if (!snapshot.snapshotId) {
|
|
8455
|
-
await emitSandboxStatus(
|
|
8456
|
-
"fresh_runtime_boot",
|
|
8457
|
-
status,
|
|
8458
|
-
makeAssistantStatus("loading", "sandbox")
|
|
8459
|
-
);
|
|
8460
7846
|
return await Sandbox.create({
|
|
8461
7847
|
timeout: timeoutMs,
|
|
8462
7848
|
runtime,
|
|
@@ -8466,8 +7852,7 @@ function createSandboxSessionManager(options) {
|
|
|
8466
7852
|
try {
|
|
8467
7853
|
return await createSandboxFromSnapshot(
|
|
8468
7854
|
snapshot.snapshotId,
|
|
8469
|
-
sandboxCredentials
|
|
8470
|
-
status.emit
|
|
7855
|
+
sandboxCredentials
|
|
8471
7856
|
);
|
|
8472
7857
|
} catch (error) {
|
|
8473
7858
|
if (!isSnapshotMissingError(error)) {
|
|
@@ -8480,23 +7865,20 @@ function createSandboxSessionManager(options) {
|
|
|
8480
7865
|
runtime,
|
|
8481
7866
|
timeoutMs,
|
|
8482
7867
|
forceRebuild: true,
|
|
8483
|
-
staleSnapshotId: snapshot.snapshotId
|
|
8484
|
-
onProgress: status.reportSnapshotPhase
|
|
7868
|
+
staleSnapshotId: snapshot.snapshotId
|
|
8485
7869
|
});
|
|
8486
7870
|
if (!rebuiltSnapshot.snapshotId) {
|
|
8487
7871
|
throw error;
|
|
8488
7872
|
}
|
|
8489
7873
|
return await createSandboxFromSnapshot(
|
|
8490
7874
|
rebuiltSnapshot.snapshotId,
|
|
8491
|
-
sandboxCredentials
|
|
8492
|
-
status.emit
|
|
7875
|
+
sandboxCredentials
|
|
8493
7876
|
);
|
|
8494
7877
|
}
|
|
8495
7878
|
};
|
|
8496
7879
|
const createFreshSandbox = async () => {
|
|
8497
7880
|
const runtime = SANDBOX_RUNTIME;
|
|
8498
7881
|
const sandboxCredentials = getVercelSandboxCredentials();
|
|
8499
|
-
const status = createStatusEmitter(options?.onStatus);
|
|
8500
7882
|
let createdSandbox;
|
|
8501
7883
|
try {
|
|
8502
7884
|
createdSandbox = await withSandboxSpan(
|
|
@@ -8508,22 +7890,15 @@ function createSandboxSessionManager(options) {
|
|
|
8508
7890
|
"app.sandbox.runtime": runtime
|
|
8509
7891
|
},
|
|
8510
7892
|
async () => {
|
|
8511
|
-
await emitSandboxStatus(
|
|
8512
|
-
"runtime_dependency_resolve",
|
|
8513
|
-
status,
|
|
8514
|
-
makeAssistantStatus("loading", "sandbox runtime")
|
|
8515
|
-
);
|
|
8516
7893
|
const snapshot = await resolveRuntimeDependencySnapshot({
|
|
8517
7894
|
runtime,
|
|
8518
|
-
timeoutMs
|
|
8519
|
-
onProgress: status.reportSnapshotPhase
|
|
7895
|
+
timeoutMs
|
|
8520
7896
|
});
|
|
8521
7897
|
setSnapshotAttributes(snapshot);
|
|
8522
7898
|
return await createSandboxFromResolvedSnapshot({
|
|
8523
7899
|
runtime,
|
|
8524
7900
|
snapshot,
|
|
8525
|
-
sandboxCredentials
|
|
8526
|
-
status
|
|
7901
|
+
sandboxCredentials
|
|
8527
7902
|
});
|
|
8528
7903
|
}
|
|
8529
7904
|
);
|
|
@@ -8837,7 +8212,6 @@ function createSandboxExecutor(options) {
|
|
|
8837
8212
|
sandboxDependencyProfileHash: options?.sandboxDependencyProfileHash,
|
|
8838
8213
|
timeoutMs: options?.timeoutMs,
|
|
8839
8214
|
traceContext,
|
|
8840
|
-
onStatus: options?.onStatus,
|
|
8841
8215
|
onSandboxAcquired: options?.onSandboxAcquired
|
|
8842
8216
|
});
|
|
8843
8217
|
const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
|
|
@@ -9195,102 +8569,15 @@ function buildReportedProgressStatus(input) {
|
|
|
9195
8569
|
if (!input || typeof input !== "object") {
|
|
9196
8570
|
return void 0;
|
|
9197
8571
|
}
|
|
9198
|
-
const
|
|
9199
|
-
if (typeof
|
|
8572
|
+
const message = input.message;
|
|
8573
|
+
if (typeof message !== "string") {
|
|
9200
8574
|
return void 0;
|
|
9201
8575
|
}
|
|
9202
|
-
const
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
return makeAssistantStatus("thinking", detail, { source: "major" });
|
|
9206
|
-
case "researching":
|
|
9207
|
-
return makeAssistantStatus("searching", detail, { source: "major" });
|
|
9208
|
-
case "reading":
|
|
9209
|
-
return makeAssistantStatus("reading", detail, { source: "major" });
|
|
9210
|
-
case "executing":
|
|
9211
|
-
return makeAssistantStatus("running", detail, { source: "major" });
|
|
9212
|
-
case "reviewing":
|
|
9213
|
-
return makeAssistantStatus("reviewing", detail, { source: "major" });
|
|
9214
|
-
case "drafting":
|
|
9215
|
-
return makeAssistantStatus("drafting", detail, { source: "major" });
|
|
9216
|
-
default:
|
|
9217
|
-
return void 0;
|
|
9218
|
-
}
|
|
9219
|
-
}
|
|
9220
|
-
|
|
9221
|
-
// src/chat/runtime/tool-status.ts
|
|
9222
|
-
function buildToolStatus(toolName, input) {
|
|
9223
|
-
const obj = input && typeof input === "object" ? input : void 0;
|
|
9224
|
-
const command = obj ? compactStatusCommand(obj.command) : void 0;
|
|
9225
|
-
const path7 = obj ? compactStatusPath(obj.path) : void 0;
|
|
9226
|
-
const filename = obj ? compactStatusFilename(obj.path) : void 0;
|
|
9227
|
-
const query = obj ? compactStatusText(obj.query, 70) : void 0;
|
|
9228
|
-
const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
|
|
9229
|
-
const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
|
|
9230
|
-
const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
|
|
9231
|
-
const reportedProgress = buildReportedProgressStatus(obj);
|
|
9232
|
-
if (toolName === "reportProgress" && reportedProgress) {
|
|
9233
|
-
return reportedProgress;
|
|
9234
|
-
}
|
|
9235
|
-
if (command && toolName === "bash") {
|
|
9236
|
-
return makeAssistantStatus("running", command);
|
|
9237
|
-
}
|
|
9238
|
-
if (filename && toolName === "readFile") {
|
|
9239
|
-
return makeAssistantStatus("reading", filename);
|
|
9240
|
-
}
|
|
9241
|
-
if (filename && toolName === "writeFile") {
|
|
9242
|
-
return makeAssistantStatus("updating", filename);
|
|
9243
|
-
}
|
|
9244
|
-
if (path7 && toolName === "writeFile") {
|
|
9245
|
-
return makeAssistantStatus("updating", path7);
|
|
9246
|
-
}
|
|
9247
|
-
if (skillName && toolName === "loadSkill") {
|
|
9248
|
-
return makeAssistantStatus("loading", skillName);
|
|
9249
|
-
}
|
|
9250
|
-
if (query && toolName === "webSearch") {
|
|
9251
|
-
return makeAssistantStatus("searching", "sources");
|
|
9252
|
-
}
|
|
9253
|
-
if (query && toolName === "searchTools") {
|
|
9254
|
-
return makeAssistantStatus(
|
|
9255
|
-
"searching",
|
|
9256
|
-
provider ? `${provider} tools` : "tools"
|
|
9257
|
-
);
|
|
9258
|
-
}
|
|
9259
|
-
if (domain && toolName === "webFetch") {
|
|
9260
|
-
return makeAssistantStatus("fetching", domain);
|
|
9261
|
-
}
|
|
9262
|
-
const known = {
|
|
9263
|
-
loadSkill: makeAssistantStatus("loading", "skill instructions"),
|
|
9264
|
-
systemTime: makeAssistantStatus("reading", "system time"),
|
|
9265
|
-
bash: makeAssistantStatus("running", "shell"),
|
|
9266
|
-
readFile: makeAssistantStatus("reading", "file"),
|
|
9267
|
-
writeFile: makeAssistantStatus("updating", "file"),
|
|
9268
|
-
webSearch: makeAssistantStatus("searching", "sources"),
|
|
9269
|
-
webFetch: makeAssistantStatus("fetching", "pages"),
|
|
9270
|
-
slackChannelPostMessage: makeAssistantStatus("posting", "channel"),
|
|
9271
|
-
slackMessageAddReaction: makeAssistantStatus("adding", "reaction"),
|
|
9272
|
-
slackChannelListMessages: makeAssistantStatus("listing", "messages"),
|
|
9273
|
-
slackCanvasCreate: makeAssistantStatus("creating", "brief"),
|
|
9274
|
-
slackCanvasUpdate: makeAssistantStatus("updating", "brief"),
|
|
9275
|
-
slackCanvasRead: makeAssistantStatus("reading", "brief"),
|
|
9276
|
-
slackListCreate: makeAssistantStatus("creating", "tracking list"),
|
|
9277
|
-
slackListAddItems: makeAssistantStatus("updating", "tracking list"),
|
|
9278
|
-
slackListUpdateItem: makeAssistantStatus("updating", "tracking list"),
|
|
9279
|
-
imageGenerate: makeAssistantStatus("creating", "image"),
|
|
9280
|
-
searchTools: makeAssistantStatus(
|
|
9281
|
-
"searching",
|
|
9282
|
-
provider ? `${provider} tools` : "tools"
|
|
9283
|
-
)
|
|
9284
|
-
};
|
|
9285
|
-
if (known[toolName]) {
|
|
9286
|
-
return known[toolName];
|
|
9287
|
-
}
|
|
9288
|
-
const mcpMatch = /^mcp__([^_]+)__(.+)$/.exec(toolName);
|
|
9289
|
-
if (mcpMatch) {
|
|
9290
|
-
return makeAssistantStatus("running", `${mcpMatch[1]}/${mcpMatch[2]}`);
|
|
8576
|
+
const text = message.trim();
|
|
8577
|
+
if (!text) {
|
|
8578
|
+
return void 0;
|
|
9291
8579
|
}
|
|
9292
|
-
|
|
9293
|
-
return makeAssistantStatus("running", readable || "tool");
|
|
8580
|
+
return { text };
|
|
9294
8581
|
}
|
|
9295
8582
|
|
|
9296
8583
|
// src/chat/tools/execution/build-sandbox-input.ts
|
|
@@ -9441,7 +8728,12 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
9441
8728
|
turnId: spanContext.turnId,
|
|
9442
8729
|
agentId: spanContext.agentId
|
|
9443
8730
|
};
|
|
9444
|
-
|
|
8731
|
+
if (toolName === "reportProgress") {
|
|
8732
|
+
const status = buildReportedProgressStatus(params);
|
|
8733
|
+
if (status) {
|
|
8734
|
+
await onStatus?.(status);
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
9445
8737
|
return withSpan(
|
|
9446
8738
|
`execute_tool ${toolName}`,
|
|
9447
8739
|
"gen_ai.execute_tool",
|
|
@@ -10344,7 +9636,6 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10344
9636
|
sandboxId: context.sandbox?.sandboxId,
|
|
10345
9637
|
sandboxDependencyProfileHash: context.sandbox?.sandboxDependencyProfileHash,
|
|
10346
9638
|
traceContext: spanContext,
|
|
10347
|
-
onStatus: context.onStatus,
|
|
10348
9639
|
onSandboxAcquired: async (sandbox2) => {
|
|
10349
9640
|
lastKnownSandboxId = sandbox2.sandboxId;
|
|
10350
9641
|
lastKnownSandboxDependencyProfileHash = sandbox2.sandboxDependencyProfileHash;
|
|
@@ -10476,492 +9767,850 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10476
9767
|
const syncResumeState = () => {
|
|
10477
9768
|
loadedSkillNamesForResume = activeSkills.map((skill) => skill.name);
|
|
10478
9769
|
};
|
|
10479
|
-
const enableSkillCredentials = async (skill, reason) => {
|
|
10480
|
-
if (!skill?.pluginProvider) {
|
|
10481
|
-
return;
|
|
9770
|
+
const enableSkillCredentials = async (skill, reason) => {
|
|
9771
|
+
if (!skill?.pluginProvider) {
|
|
9772
|
+
return;
|
|
9773
|
+
}
|
|
9774
|
+
try {
|
|
9775
|
+
await capabilityRuntime.enableCredentialsForTurn({
|
|
9776
|
+
activeSkill: skill,
|
|
9777
|
+
reason
|
|
9778
|
+
});
|
|
9779
|
+
} catch (error) {
|
|
9780
|
+
if (error instanceof CredentialUnavailableError && context.requester?.userId) {
|
|
9781
|
+
await pluginAuth.handleCredentialUnavailable({
|
|
9782
|
+
activeSkill: skill,
|
|
9783
|
+
error
|
|
9784
|
+
});
|
|
9785
|
+
}
|
|
9786
|
+
throw error;
|
|
9787
|
+
}
|
|
9788
|
+
};
|
|
9789
|
+
setTags({
|
|
9790
|
+
conversationId: spanContext.conversationId,
|
|
9791
|
+
turnId: spanContext.turnId,
|
|
9792
|
+
agentId: spanContext.agentId,
|
|
9793
|
+
slackThreadId: context.correlation?.threadId,
|
|
9794
|
+
slackUserId: context.correlation?.requesterId,
|
|
9795
|
+
slackChannelId: context.correlation?.channelId,
|
|
9796
|
+
runId: context.correlation?.runId,
|
|
9797
|
+
assistantUserName: context.assistant?.userName,
|
|
9798
|
+
modelId: botConfig.modelId
|
|
9799
|
+
});
|
|
9800
|
+
const tools = createTools(
|
|
9801
|
+
availableSkills,
|
|
9802
|
+
{
|
|
9803
|
+
getGeneratedFile: (filename) => generatedFiles.find((file) => file.filename === filename),
|
|
9804
|
+
onGeneratedArtifactFiles: (files) => {
|
|
9805
|
+
generatedFiles.push(...files);
|
|
9806
|
+
},
|
|
9807
|
+
onGeneratedFiles: (files) => {
|
|
9808
|
+
replyFiles.push(...files);
|
|
9809
|
+
},
|
|
9810
|
+
onArtifactStatePatch: async (patch) => {
|
|
9811
|
+
Object.assign(artifactStatePatch, patch);
|
|
9812
|
+
await context.onArtifactStateUpdated?.(
|
|
9813
|
+
mergeArtifactsState(
|
|
9814
|
+
context.artifactState ?? {},
|
|
9815
|
+
artifactStatePatch
|
|
9816
|
+
)
|
|
9817
|
+
);
|
|
9818
|
+
},
|
|
9819
|
+
toolOverrides: context.toolOverrides,
|
|
9820
|
+
onSkillLoaded: async (loadedSkill) => {
|
|
9821
|
+
const resolvedSkill = await skillSandbox.loadSkill(loadedSkill.name);
|
|
9822
|
+
const effective = resolvedSkill ?? loadedSkill;
|
|
9823
|
+
upsertActiveSkill(activeSkills, effective);
|
|
9824
|
+
syncResumeState();
|
|
9825
|
+
await turnMcpToolManager.activateForSkill(effective);
|
|
9826
|
+
syncResumeState();
|
|
9827
|
+
if (mcpAuth.getPendingPause()) {
|
|
9828
|
+
return void 0;
|
|
9829
|
+
}
|
|
9830
|
+
await enableSkillCredentials(
|
|
9831
|
+
effective,
|
|
9832
|
+
`skill:${effective.name}:turn:load`
|
|
9833
|
+
);
|
|
9834
|
+
if (!effective.pluginProvider) {
|
|
9835
|
+
return void 0;
|
|
9836
|
+
}
|
|
9837
|
+
syncMcpAgentTools();
|
|
9838
|
+
return {
|
|
9839
|
+
available_tools: turnMcpToolManager.getActiveToolCatalog(activeSkills, {
|
|
9840
|
+
provider: effective.pluginProvider
|
|
9841
|
+
}).map(toExposedToolSummary)
|
|
9842
|
+
};
|
|
9843
|
+
}
|
|
9844
|
+
},
|
|
9845
|
+
{
|
|
9846
|
+
channelId: context.toolChannelId ?? context.correlation?.channelId,
|
|
9847
|
+
channelCapabilities: resolveChannelCapabilities(
|
|
9848
|
+
context.toolChannelId ?? context.correlation?.channelId
|
|
9849
|
+
),
|
|
9850
|
+
messageTs: context.correlation?.messageTs,
|
|
9851
|
+
threadTs: context.correlation?.threadTs,
|
|
9852
|
+
userText: userInput,
|
|
9853
|
+
artifactState: context.artifactState,
|
|
9854
|
+
configuration: configurationValues,
|
|
9855
|
+
getActiveSkills: () => activeSkills,
|
|
9856
|
+
mcpToolManager: turnMcpToolManager,
|
|
9857
|
+
sandbox
|
|
9858
|
+
}
|
|
9859
|
+
);
|
|
9860
|
+
syncResumeState();
|
|
9861
|
+
for (const skill of activeSkills) {
|
|
9862
|
+
await turnMcpToolManager.activateForSkill(skill);
|
|
9863
|
+
syncResumeState();
|
|
9864
|
+
if (mcpAuth.getPendingPause()) {
|
|
9865
|
+
timeoutResumeMessages = existingCheckpoint?.piMessages ?? [];
|
|
9866
|
+
throw mcpAuth.getPendingPause();
|
|
9867
|
+
}
|
|
9868
|
+
await enableSkillCredentials(skill, `skill:${skill.name}:turn:resume`);
|
|
9869
|
+
}
|
|
9870
|
+
syncResumeState();
|
|
9871
|
+
const activeToolSummaries = turnMcpToolManager.getActiveToolCatalog(activeSkills).map(toExposedToolSummary);
|
|
9872
|
+
baseInstructions = buildSystemPrompt({
|
|
9873
|
+
availableSkills,
|
|
9874
|
+
activeSkills,
|
|
9875
|
+
activeTools: activeToolSummaries,
|
|
9876
|
+
invocation: skillInvocation,
|
|
9877
|
+
assistant: context.assistant,
|
|
9878
|
+
requester: context.requester,
|
|
9879
|
+
artifactState: context.artifactState,
|
|
9880
|
+
configuration: configurationValues,
|
|
9881
|
+
relevantConfigurationKeys: collectRelevantConfigurationKeys(
|
|
9882
|
+
activeSkills,
|
|
9883
|
+
invokedSkill
|
|
9884
|
+
),
|
|
9885
|
+
runtimeMetadata: getRuntimeMetadata(),
|
|
9886
|
+
threadParticipants: context.threadParticipants
|
|
9887
|
+
});
|
|
9888
|
+
const userContentParts = [{ type: "text", text: userTurnText }];
|
|
9889
|
+
const omittedImageAttachmentCount = context.omittedImageAttachmentCount ?? 0;
|
|
9890
|
+
if (omittedImageAttachmentCount > 0) {
|
|
9891
|
+
userContentParts.push({
|
|
9892
|
+
type: "text",
|
|
9893
|
+
text: buildOmittedImageAttachmentNotice(omittedImageAttachmentCount)
|
|
9894
|
+
});
|
|
9895
|
+
}
|
|
9896
|
+
for (const attachment of context.userAttachments ?? []) {
|
|
9897
|
+
if (attachment.promptText) {
|
|
9898
|
+
userContentParts.push({
|
|
9899
|
+
type: "text",
|
|
9900
|
+
text: attachment.promptText
|
|
9901
|
+
});
|
|
9902
|
+
} else if (attachment.mediaType.startsWith("image/")) {
|
|
9903
|
+
if (!attachment.data) {
|
|
9904
|
+
throw new Error("Image attachment is missing image data");
|
|
9905
|
+
}
|
|
9906
|
+
userContentParts.push({
|
|
9907
|
+
type: "image",
|
|
9908
|
+
data: attachment.data.toString("base64"),
|
|
9909
|
+
mimeType: attachment.mediaType
|
|
9910
|
+
});
|
|
9911
|
+
} else {
|
|
9912
|
+
if (!attachment.data) {
|
|
9913
|
+
throw new Error("Attachment is missing attachment data");
|
|
9914
|
+
}
|
|
9915
|
+
const promptAttachment = {
|
|
9916
|
+
data: attachment.data,
|
|
9917
|
+
mediaType: attachment.mediaType,
|
|
9918
|
+
filename: attachment.filename
|
|
9919
|
+
};
|
|
9920
|
+
userContentParts.push({
|
|
9921
|
+
type: "text",
|
|
9922
|
+
text: encodeNonImageAttachmentForPrompt(promptAttachment)
|
|
9923
|
+
});
|
|
9924
|
+
}
|
|
9925
|
+
}
|
|
9926
|
+
const inputMessagesAttribute = serializeGenAiAttribute([
|
|
9927
|
+
{
|
|
9928
|
+
role: "system",
|
|
9929
|
+
content: [{ type: "text", text: baseInstructions }]
|
|
9930
|
+
},
|
|
9931
|
+
{
|
|
9932
|
+
role: "user",
|
|
9933
|
+
content: userContentParts.map((part) => toObservablePromptPart(part))
|
|
9934
|
+
}
|
|
9935
|
+
]);
|
|
9936
|
+
const agentToolHooks = {
|
|
9937
|
+
onToolCall: (toolName) => {
|
|
9938
|
+
toolCalls.push(toolName);
|
|
9939
|
+
Promise.resolve(context.onToolCall?.(toolName)).catch((error) => {
|
|
9940
|
+
logWarn(
|
|
9941
|
+
"streaming_tool_call_error",
|
|
9942
|
+
{},
|
|
9943
|
+
{
|
|
9944
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
9945
|
+
"gen_ai.tool.name": toolName
|
|
9946
|
+
},
|
|
9947
|
+
"Failed to deliver tool call event to stream coordinator"
|
|
9948
|
+
);
|
|
9949
|
+
});
|
|
9950
|
+
}
|
|
9951
|
+
};
|
|
9952
|
+
const baseAgentTools = createAgentTools(
|
|
9953
|
+
tools,
|
|
9954
|
+
skillSandbox,
|
|
9955
|
+
spanContext,
|
|
9956
|
+
context.onStatus,
|
|
9957
|
+
sandboxExecutor,
|
|
9958
|
+
capabilityRuntime,
|
|
9959
|
+
pluginAuth,
|
|
9960
|
+
agentToolHooks
|
|
9961
|
+
);
|
|
9962
|
+
const agentTools = [...baseAgentTools];
|
|
9963
|
+
const syncMcpAgentTools = () => {
|
|
9964
|
+
const mcpTools = turnMcpToolManager.getResolvedActiveTools(activeSkills);
|
|
9965
|
+
const mcpDefs = mcpToolsToDefinitions(mcpTools);
|
|
9966
|
+
const mcpAgentTools = createAgentTools(
|
|
9967
|
+
mcpDefs,
|
|
9968
|
+
skillSandbox,
|
|
9969
|
+
spanContext,
|
|
9970
|
+
context.onStatus,
|
|
9971
|
+
sandboxExecutor,
|
|
9972
|
+
capabilityRuntime,
|
|
9973
|
+
pluginAuth,
|
|
9974
|
+
agentToolHooks
|
|
9975
|
+
);
|
|
9976
|
+
agentTools.length = 0;
|
|
9977
|
+
agentTools.push(...baseAgentTools, ...mcpAgentTools);
|
|
9978
|
+
};
|
|
9979
|
+
syncMcpAgentTools();
|
|
9980
|
+
agent = new Agent({
|
|
9981
|
+
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
9982
|
+
initialState: {
|
|
9983
|
+
systemPrompt: baseInstructions,
|
|
9984
|
+
model: resolveGatewayModel(botConfig.modelId),
|
|
9985
|
+
tools: agentTools
|
|
10482
9986
|
}
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
9987
|
+
});
|
|
9988
|
+
let hasEmittedText = false;
|
|
9989
|
+
let needsSeparator = false;
|
|
9990
|
+
const unsubscribe = agent.subscribe((event) => {
|
|
9991
|
+
if (event.type === "message_start") {
|
|
9992
|
+
Promise.resolve(context.onAssistantMessageStart?.()).catch((error) => {
|
|
9993
|
+
logWarn(
|
|
9994
|
+
"streaming_message_start_error",
|
|
9995
|
+
{},
|
|
9996
|
+
{
|
|
9997
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
9998
|
+
},
|
|
9999
|
+
"Failed to deliver assistant message start to stream coordinator"
|
|
10000
|
+
);
|
|
10487
10001
|
});
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
await pluginAuth.handleCredentialUnavailable({
|
|
10491
|
-
activeSkill: skill,
|
|
10492
|
-
error
|
|
10493
|
-
});
|
|
10002
|
+
if (hasEmittedText) {
|
|
10003
|
+
needsSeparator = true;
|
|
10494
10004
|
}
|
|
10495
|
-
|
|
10005
|
+
return;
|
|
10496
10006
|
}
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10007
|
+
if (event.type !== "message_update") return;
|
|
10008
|
+
if (event.assistantMessageEvent.type !== "text_delta") return;
|
|
10009
|
+
const deltaText = event.assistantMessageEvent.delta;
|
|
10010
|
+
if (!deltaText) return;
|
|
10011
|
+
const text = needsSeparator ? "\n\n" + deltaText : deltaText;
|
|
10012
|
+
needsSeparator = false;
|
|
10013
|
+
hasEmittedText = true;
|
|
10014
|
+
Promise.resolve(context.onTextDelta?.(text)).catch((error) => {
|
|
10015
|
+
logWarn(
|
|
10016
|
+
"streaming_text_delta_error",
|
|
10017
|
+
{},
|
|
10018
|
+
{
|
|
10019
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
10020
|
+
},
|
|
10021
|
+
"Failed to deliver text delta to stream"
|
|
10022
|
+
);
|
|
10023
|
+
});
|
|
10508
10024
|
});
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
|
|
10519
|
-
|
|
10520
|
-
|
|
10521
|
-
|
|
10522
|
-
|
|
10523
|
-
|
|
10524
|
-
|
|
10525
|
-
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10025
|
+
let beforeMessageCount = agent.state.messages.length;
|
|
10026
|
+
let newMessages = [];
|
|
10027
|
+
let completedAssistantTurn = false;
|
|
10028
|
+
try {
|
|
10029
|
+
if (resumedFromCheckpoint) {
|
|
10030
|
+
agent.replaceMessages(existingCheckpoint.piMessages);
|
|
10031
|
+
}
|
|
10032
|
+
beforeMessageCount = agent.state.messages.length;
|
|
10033
|
+
await withSpan(
|
|
10034
|
+
"ai.generate_assistant_reply",
|
|
10035
|
+
"gen_ai.invoke_agent",
|
|
10036
|
+
spanContext,
|
|
10037
|
+
async () => {
|
|
10038
|
+
let promptResult;
|
|
10039
|
+
const promptPromise = resumedFromCheckpoint ? (
|
|
10040
|
+
// Checkpoint resumes continue from the persisted Pi message
|
|
10041
|
+
// state. Any reconstructed replyContext only matters when the
|
|
10042
|
+
// turn parked before the initial user prompt was recorded.
|
|
10043
|
+
agent.continue()
|
|
10044
|
+
) : agent.prompt({
|
|
10045
|
+
role: "user",
|
|
10046
|
+
content: userContentParts,
|
|
10047
|
+
timestamp: Date.now()
|
|
10048
|
+
});
|
|
10049
|
+
let timeoutId;
|
|
10050
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
10051
|
+
timeoutId = setTimeout(() => {
|
|
10052
|
+
timedOut = true;
|
|
10053
|
+
agent.abort();
|
|
10054
|
+
reject(
|
|
10055
|
+
new Error(
|
|
10056
|
+
`Agent turn timed out after ${botConfig.turnTimeoutMs}ms`
|
|
10057
|
+
)
|
|
10058
|
+
);
|
|
10059
|
+
}, botConfig.turnTimeoutMs);
|
|
10060
|
+
});
|
|
10061
|
+
try {
|
|
10062
|
+
promptResult = await Promise.race([promptPromise, timeoutPromise]);
|
|
10063
|
+
} catch (error) {
|
|
10064
|
+
if (timedOut) {
|
|
10065
|
+
logWarn(
|
|
10066
|
+
"agent_turn_timeout",
|
|
10067
|
+
{},
|
|
10068
|
+
{
|
|
10069
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10070
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
10071
|
+
"gen_ai.request.model": botConfig.modelId,
|
|
10072
|
+
"app.ai.turn_timeout_ms": botConfig.turnTimeoutMs
|
|
10073
|
+
},
|
|
10074
|
+
"Agent turn timed out and was aborted"
|
|
10075
|
+
);
|
|
10076
|
+
await promptPromise.catch(() => {
|
|
10077
|
+
});
|
|
10078
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10079
|
+
}
|
|
10080
|
+
if (getPendingAuthPause()) {
|
|
10081
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10082
|
+
throw getPendingAuthPause();
|
|
10083
|
+
}
|
|
10084
|
+
throw error;
|
|
10085
|
+
} finally {
|
|
10086
|
+
if (timeoutId) {
|
|
10087
|
+
clearTimeout(timeoutId);
|
|
10088
|
+
}
|
|
10538
10089
|
}
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
return void 0;
|
|
10090
|
+
newMessages = agent.state.messages.slice(beforeMessageCount);
|
|
10091
|
+
completedAssistantTurn = hasCompletedAssistantTurn(newMessages);
|
|
10092
|
+
if (getPendingAuthPause() && !completedAssistantTurn) {
|
|
10093
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10094
|
+
throw getPendingAuthPause();
|
|
10545
10095
|
}
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10096
|
+
const outputMessages = newMessages.filter(isAssistantMessage);
|
|
10097
|
+
const outputMessagesAttribute = serializeGenAiAttribute(outputMessages);
|
|
10098
|
+
const usageSummary = extractGenAiUsageSummary(
|
|
10099
|
+
promptResult,
|
|
10100
|
+
agent.state,
|
|
10101
|
+
...outputMessages
|
|
10102
|
+
);
|
|
10103
|
+
turnUsage = Object.values(usageSummary).some(
|
|
10104
|
+
(value) => value !== void 0
|
|
10105
|
+
) ? usageSummary : void 0;
|
|
10106
|
+
setSpanAttributes({
|
|
10107
|
+
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
10108
|
+
...usageSummary.inputTokens !== void 0 ? { "gen_ai.usage.input_tokens": usageSummary.inputTokens } : {},
|
|
10109
|
+
...usageSummary.outputTokens !== void 0 ? { "gen_ai.usage.output_tokens": usageSummary.outputTokens } : {}
|
|
10110
|
+
});
|
|
10111
|
+
},
|
|
10112
|
+
{
|
|
10113
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10114
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
10115
|
+
"gen_ai.request.model": botConfig.modelId,
|
|
10116
|
+
...inputMessagesAttribute ? { "gen_ai.input.messages": inputMessagesAttribute } : {}
|
|
10117
|
+
}
|
|
10118
|
+
);
|
|
10119
|
+
} finally {
|
|
10120
|
+
unsubscribe();
|
|
10121
|
+
}
|
|
10122
|
+
if (getPendingAuthPause() && !completedAssistantTurn) {
|
|
10123
|
+
throw getPendingAuthPause();
|
|
10124
|
+
}
|
|
10125
|
+
if (checkpointState.canUseTurnSession && sessionConversationId && sessionId) {
|
|
10126
|
+
await persistCompletedCheckpoint({
|
|
10127
|
+
conversationId: sessionConversationId,
|
|
10128
|
+
sessionId,
|
|
10129
|
+
sliceId: currentSliceId,
|
|
10130
|
+
allMessages: agent.state.messages,
|
|
10131
|
+
loadedSkillNames: activeSkills.map((skill) => skill.name)
|
|
10132
|
+
});
|
|
10133
|
+
}
|
|
10134
|
+
return buildTurnResult({
|
|
10135
|
+
newMessages,
|
|
10136
|
+
userInput,
|
|
10137
|
+
replyFiles,
|
|
10138
|
+
artifactStatePatch,
|
|
10139
|
+
toolCalls,
|
|
10140
|
+
sandboxId: currentSandboxExecutor.getSandboxId(),
|
|
10141
|
+
sandboxDependencyProfileHash: currentSandboxExecutor.getDependencyProfileHash(),
|
|
10142
|
+
durationMs: Date.now() - replyStartedAtMs,
|
|
10143
|
+
generatedFileCount: generatedFiles.length,
|
|
10144
|
+
shouldTrace,
|
|
10145
|
+
spanContext,
|
|
10146
|
+
usage: turnUsage,
|
|
10147
|
+
correlation: context.correlation,
|
|
10148
|
+
assistantUserName: context.assistant?.userName
|
|
10149
|
+
});
|
|
10150
|
+
} catch (error) {
|
|
10151
|
+
if (timedOut && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
10152
|
+
const checkpoint = await persistTimeoutCheckpoint({
|
|
10153
|
+
conversationId: timeoutResumeConversationId,
|
|
10154
|
+
sessionId: timeoutResumeSessionId,
|
|
10155
|
+
currentSliceId: timeoutResumeSliceId,
|
|
10156
|
+
messages: timeoutResumeMessages,
|
|
10157
|
+
loadedSkillNames: loadedSkillNamesForResume,
|
|
10158
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
10159
|
+
logContext: {
|
|
10160
|
+
threadId: context.correlation?.threadId,
|
|
10161
|
+
requesterId: context.correlation?.requesterId,
|
|
10162
|
+
channelId: context.correlation?.channelId,
|
|
10163
|
+
runId: context.correlation?.runId,
|
|
10164
|
+
assistantUserName: context.assistant?.userName,
|
|
10165
|
+
modelId: botConfig.modelId
|
|
10166
|
+
}
|
|
10167
|
+
});
|
|
10168
|
+
if (checkpoint) {
|
|
10169
|
+
throw new RetryableTurnError(
|
|
10170
|
+
"turn_timeout_resume",
|
|
10171
|
+
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${checkpoint.sliceId} version=${checkpoint.checkpointVersion}`,
|
|
10172
|
+
{
|
|
10173
|
+
conversationId: timeoutResumeConversationId,
|
|
10174
|
+
sessionId: timeoutResumeSessionId,
|
|
10175
|
+
sliceId: checkpoint.sliceId,
|
|
10176
|
+
checkpointVersion: checkpoint.checkpointVersion
|
|
10177
|
+
}
|
|
10178
|
+
);
|
|
10179
|
+
}
|
|
10180
|
+
}
|
|
10181
|
+
if ((error instanceof McpAuthorizationPauseError || error instanceof PluginAuthorizationPauseError) && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
10182
|
+
const nextSliceId = await persistAuthPauseCheckpoint({
|
|
10183
|
+
conversationId: timeoutResumeConversationId,
|
|
10184
|
+
sessionId: timeoutResumeSessionId,
|
|
10185
|
+
currentSliceId: timeoutResumeSliceId,
|
|
10186
|
+
messages: timeoutResumeMessages,
|
|
10187
|
+
loadedSkillNames: loadedSkillNamesForResume,
|
|
10188
|
+
errorMessage: error.message,
|
|
10189
|
+
logContext: {
|
|
10190
|
+
threadId: context.correlation?.threadId,
|
|
10191
|
+
requesterId: context.correlation?.requesterId,
|
|
10192
|
+
channelId: context.correlation?.channelId,
|
|
10193
|
+
runId: context.correlation?.runId,
|
|
10194
|
+
assistantUserName: context.assistant?.userName,
|
|
10195
|
+
modelId: botConfig.modelId
|
|
10552
10196
|
}
|
|
10553
|
-
}
|
|
10197
|
+
});
|
|
10198
|
+
throw new RetryableTurnError(
|
|
10199
|
+
error instanceof PluginAuthorizationPauseError ? "plugin_auth_resume" : "mcp_auth_resume",
|
|
10200
|
+
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${nextSliceId}`,
|
|
10201
|
+
{
|
|
10202
|
+
conversationId: timeoutResumeConversationId,
|
|
10203
|
+
sessionId: timeoutResumeSessionId,
|
|
10204
|
+
sliceId: nextSliceId
|
|
10205
|
+
}
|
|
10206
|
+
);
|
|
10207
|
+
}
|
|
10208
|
+
if (isRetryableTurnError(error)) {
|
|
10209
|
+
throw error;
|
|
10210
|
+
}
|
|
10211
|
+
logException(
|
|
10212
|
+
error,
|
|
10213
|
+
"assistant_reply_generation_failed",
|
|
10554
10214
|
{
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
getActiveSkills: () => activeSkills,
|
|
10565
|
-
mcpToolManager: turnMcpToolManager,
|
|
10566
|
-
sandbox
|
|
10567
|
-
}
|
|
10215
|
+
slackThreadId: context.correlation?.threadId,
|
|
10216
|
+
slackUserId: context.correlation?.requesterId,
|
|
10217
|
+
slackChannelId: context.correlation?.channelId,
|
|
10218
|
+
runId: context.correlation?.runId,
|
|
10219
|
+
assistantUserName: context.assistant?.userName,
|
|
10220
|
+
modelId: botConfig.modelId
|
|
10221
|
+
},
|
|
10222
|
+
{},
|
|
10223
|
+
"generateAssistantReply failed"
|
|
10568
10224
|
);
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10225
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10226
|
+
return {
|
|
10227
|
+
text: `Error: ${message}`,
|
|
10228
|
+
...getSandboxMetadata(),
|
|
10229
|
+
diagnostics: {
|
|
10230
|
+
outcome: "provider_error",
|
|
10231
|
+
modelId: botConfig.modelId,
|
|
10232
|
+
assistantMessageCount: 0,
|
|
10233
|
+
toolCalls: [],
|
|
10234
|
+
toolResultCount: 0,
|
|
10235
|
+
toolErrorCount: 0,
|
|
10236
|
+
usedPrimaryText: false,
|
|
10237
|
+
durationMs: Date.now() - replyStartedAtMs,
|
|
10238
|
+
errorMessage: message,
|
|
10239
|
+
providerError: error
|
|
10576
10240
|
}
|
|
10577
|
-
|
|
10241
|
+
};
|
|
10242
|
+
} finally {
|
|
10243
|
+
try {
|
|
10244
|
+
await mcpToolManager?.close();
|
|
10245
|
+
} catch (closeError) {
|
|
10246
|
+
logWarn(
|
|
10247
|
+
"mcp_tool_manager_close_failed",
|
|
10248
|
+
{},
|
|
10249
|
+
{
|
|
10250
|
+
"error.message": closeError instanceof Error ? closeError.message : String(closeError)
|
|
10251
|
+
},
|
|
10252
|
+
"Failed to close MCP tool manager"
|
|
10253
|
+
);
|
|
10578
10254
|
}
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10255
|
+
}
|
|
10256
|
+
}
|
|
10257
|
+
|
|
10258
|
+
// src/chat/slack/assistant-thread/status-render.ts
|
|
10259
|
+
var DEFAULT_STATUS_CONTEXTS = {
|
|
10260
|
+
thinking: "\u2026",
|
|
10261
|
+
searching: "sources",
|
|
10262
|
+
reading: "task",
|
|
10263
|
+
reviewing: "results",
|
|
10264
|
+
drafting: "reply",
|
|
10265
|
+
running: "tasks"
|
|
10266
|
+
};
|
|
10267
|
+
function formatAssistantStatusText(verb, context) {
|
|
10268
|
+
const normalizedVerb = normalizeSlackStatusText(verb).trim().toLowerCase();
|
|
10269
|
+
const normalizedContext = normalizeSlackStatusText(context ?? "") || DEFAULT_STATUS_CONTEXTS[normalizedVerb] || "";
|
|
10270
|
+
if (!normalizedVerb) {
|
|
10271
|
+
return truncateStatusText(normalizedContext || "Working");
|
|
10272
|
+
}
|
|
10273
|
+
const displayVerb = `${normalizedVerb[0]?.toUpperCase() ?? ""}${normalizedVerb.slice(1)}`;
|
|
10274
|
+
return truncateStatusText(
|
|
10275
|
+
normalizedContext ? `${displayVerb} ${normalizedContext}` : displayVerb
|
|
10276
|
+
);
|
|
10277
|
+
}
|
|
10278
|
+
function makeAssistantStatus(verb, context) {
|
|
10279
|
+
return {
|
|
10280
|
+
text: formatAssistantStatusText(verb, context)
|
|
10281
|
+
};
|
|
10282
|
+
}
|
|
10283
|
+
function renderAssistantStatus(args) {
|
|
10284
|
+
const visible = truncateStatusText(
|
|
10285
|
+
normalizeSlackStatusText(args.status.text)
|
|
10286
|
+
);
|
|
10287
|
+
return {
|
|
10288
|
+
key: visible,
|
|
10289
|
+
visible
|
|
10290
|
+
};
|
|
10291
|
+
}
|
|
10292
|
+
function selectAssistantLoadingMessages(args) {
|
|
10293
|
+
const random = args.random ?? Math.random;
|
|
10294
|
+
const normalized = Array.from(
|
|
10295
|
+
new Set(
|
|
10296
|
+
args.messages.map((message) => truncateStatusText(normalizeSlackStatusText(message))).filter((message) => message.length > 0)
|
|
10297
|
+
)
|
|
10298
|
+
);
|
|
10299
|
+
if (normalized.length === 0) {
|
|
10300
|
+
return void 0;
|
|
10301
|
+
}
|
|
10302
|
+
const shuffled = [...normalized];
|
|
10303
|
+
for (let index = shuffled.length - 1; index > 0; index -= 1) {
|
|
10304
|
+
const otherIndex = Math.floor(random() * (index + 1));
|
|
10305
|
+
[shuffled[index], shuffled[otherIndex]] = [
|
|
10306
|
+
shuffled[otherIndex],
|
|
10307
|
+
shuffled[index]
|
|
10308
|
+
];
|
|
10309
|
+
}
|
|
10310
|
+
return shuffled.slice(0, 10);
|
|
10311
|
+
}
|
|
10312
|
+
|
|
10313
|
+
// src/chat/slack/assistant-thread/status-scheduler.ts
|
|
10314
|
+
var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
10315
|
+
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
10316
|
+
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
10317
|
+
function createAssistantStatusScheduler(args) {
|
|
10318
|
+
const now = args.now ?? (() => Date.now());
|
|
10319
|
+
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
10320
|
+
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
10321
|
+
const random = args.random ?? Math.random;
|
|
10322
|
+
const loadingMessages = selectAssistantLoadingMessages({
|
|
10323
|
+
messages: args.loadingMessages ?? [],
|
|
10324
|
+
random
|
|
10325
|
+
});
|
|
10326
|
+
const defaultStatus = makeAssistantStatus("thinking");
|
|
10327
|
+
let active = false;
|
|
10328
|
+
let currentKey = "";
|
|
10329
|
+
let currentVisibleStatus = "";
|
|
10330
|
+
let currentLoadingMessages;
|
|
10331
|
+
let lastStatusAt = 0;
|
|
10332
|
+
let pendingStatus = null;
|
|
10333
|
+
let pendingKey = "";
|
|
10334
|
+
let pendingTimer = null;
|
|
10335
|
+
let rotationTimer = null;
|
|
10336
|
+
let inflightStatusUpdate = Promise.resolve();
|
|
10337
|
+
const enqueueStatusUpdate = (task) => {
|
|
10338
|
+
const request = inflightStatusUpdate.catch(() => void 0).then(async () => {
|
|
10339
|
+
await task();
|
|
10596
10340
|
});
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10341
|
+
inflightStatusUpdate = request.catch(() => void 0);
|
|
10342
|
+
return request;
|
|
10343
|
+
};
|
|
10344
|
+
const scheduleRotation = () => {
|
|
10345
|
+
if (rotationTimer) {
|
|
10346
|
+
clearTimer(rotationTimer);
|
|
10347
|
+
rotationTimer = null;
|
|
10604
10348
|
}
|
|
10605
|
-
|
|
10606
|
-
|
|
10607
|
-
|
|
10608
|
-
|
|
10609
|
-
|
|
10610
|
-
|
|
10611
|
-
|
|
10612
|
-
if (!attachment.data) {
|
|
10613
|
-
throw new Error("Image attachment is missing image data");
|
|
10614
|
-
}
|
|
10615
|
-
userContentParts.push({
|
|
10616
|
-
type: "image",
|
|
10617
|
-
data: attachment.data.toString("base64"),
|
|
10618
|
-
mimeType: attachment.mediaType
|
|
10619
|
-
});
|
|
10620
|
-
} else {
|
|
10621
|
-
if (!attachment.data) {
|
|
10622
|
-
throw new Error("Attachment is missing attachment data");
|
|
10623
|
-
}
|
|
10624
|
-
const promptAttachment = {
|
|
10625
|
-
data: attachment.data,
|
|
10626
|
-
mediaType: attachment.mediaType,
|
|
10627
|
-
filename: attachment.filename
|
|
10628
|
-
};
|
|
10629
|
-
userContentParts.push({
|
|
10630
|
-
type: "text",
|
|
10631
|
-
text: encodeNonImageAttachmentForPrompt(promptAttachment)
|
|
10632
|
-
});
|
|
10349
|
+
if (!active || !currentVisibleStatus) {
|
|
10350
|
+
return;
|
|
10351
|
+
}
|
|
10352
|
+
rotationTimer = setTimer(() => {
|
|
10353
|
+
rotationTimer = null;
|
|
10354
|
+
if (!active || !currentVisibleStatus) {
|
|
10355
|
+
return;
|
|
10633
10356
|
}
|
|
10357
|
+
void postStatus(currentVisibleStatus, currentLoadingMessages);
|
|
10358
|
+
}, STATUS_ROTATION_INTERVAL_MS);
|
|
10359
|
+
};
|
|
10360
|
+
const getLoadingMessagesForVisibleStatus = (visible) => visible ? [visible] : void 0;
|
|
10361
|
+
const getInitialStatusText = () => {
|
|
10362
|
+
if (loadingMessages?.length) {
|
|
10363
|
+
return loadingMessages[0];
|
|
10364
|
+
}
|
|
10365
|
+
return defaultStatus.text;
|
|
10366
|
+
};
|
|
10367
|
+
const haveSameLoadingMessages = (left, right) => {
|
|
10368
|
+
if (left === right) {
|
|
10369
|
+
return true;
|
|
10370
|
+
}
|
|
10371
|
+
if (!left || !right || left.length !== right.length) {
|
|
10372
|
+
return false;
|
|
10373
|
+
}
|
|
10374
|
+
return left.every((message, index) => message === right[index]);
|
|
10375
|
+
};
|
|
10376
|
+
const postStatus = async (text, nextLoadingMessages) => {
|
|
10377
|
+
if (!text && !currentVisibleStatus) {
|
|
10378
|
+
return;
|
|
10379
|
+
}
|
|
10380
|
+
currentVisibleStatus = text;
|
|
10381
|
+
currentLoadingMessages = nextLoadingMessages;
|
|
10382
|
+
lastStatusAt = now();
|
|
10383
|
+
scheduleRotation();
|
|
10384
|
+
await enqueueStatusUpdate(async () => {
|
|
10385
|
+
await args.sendStatus(text, nextLoadingMessages);
|
|
10386
|
+
});
|
|
10387
|
+
};
|
|
10388
|
+
const postRenderedStatus = async (status) => {
|
|
10389
|
+
const presentation = renderAssistantStatus({
|
|
10390
|
+
status
|
|
10391
|
+
});
|
|
10392
|
+
const nextLoadingMessages = getLoadingMessagesForVisibleStatus(
|
|
10393
|
+
presentation.visible
|
|
10394
|
+
);
|
|
10395
|
+
currentKey = presentation.key;
|
|
10396
|
+
await postStatus(presentation.visible, nextLoadingMessages);
|
|
10397
|
+
};
|
|
10398
|
+
const clearPending = () => {
|
|
10399
|
+
if (pendingTimer) {
|
|
10400
|
+
clearTimer(pendingTimer);
|
|
10401
|
+
pendingTimer = null;
|
|
10634
10402
|
}
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10403
|
+
pendingStatus = null;
|
|
10404
|
+
pendingKey = "";
|
|
10405
|
+
};
|
|
10406
|
+
const flushPending = async () => {
|
|
10407
|
+
if (!active || !pendingStatus) {
|
|
10408
|
+
clearPending();
|
|
10409
|
+
return;
|
|
10410
|
+
}
|
|
10411
|
+
const next = pendingStatus;
|
|
10412
|
+
clearPending();
|
|
10413
|
+
const nextPresentation = renderAssistantStatus({
|
|
10414
|
+
status: next
|
|
10415
|
+
});
|
|
10416
|
+
if (nextPresentation.key !== currentKey) {
|
|
10417
|
+
await postRenderedStatus(next);
|
|
10418
|
+
}
|
|
10419
|
+
};
|
|
10420
|
+
return {
|
|
10421
|
+
start() {
|
|
10422
|
+
active = true;
|
|
10423
|
+
clearPending();
|
|
10424
|
+
currentKey = "initial";
|
|
10425
|
+
void postStatus(getInitialStatusText(), loadingMessages);
|
|
10426
|
+
},
|
|
10427
|
+
async stop() {
|
|
10428
|
+
active = false;
|
|
10429
|
+
clearPending();
|
|
10430
|
+
if (rotationTimer) {
|
|
10431
|
+
clearTimer(rotationTimer);
|
|
10432
|
+
rotationTimer = null;
|
|
10643
10433
|
}
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10648
|
-
|
|
10649
|
-
|
|
10650
|
-
"streaming_tool_call_error",
|
|
10651
|
-
{},
|
|
10652
|
-
{
|
|
10653
|
-
"error.message": error instanceof Error ? error.message : String(error),
|
|
10654
|
-
"gen_ai.tool.name": toolName
|
|
10655
|
-
},
|
|
10656
|
-
"Failed to deliver tool call event to stream coordinator"
|
|
10657
|
-
);
|
|
10658
|
-
});
|
|
10434
|
+
currentKey = "";
|
|
10435
|
+
await postStatus("");
|
|
10436
|
+
},
|
|
10437
|
+
update(status) {
|
|
10438
|
+
if (!active) {
|
|
10439
|
+
return;
|
|
10659
10440
|
}
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
|
|
10664
|
-
|
|
10665
|
-
context.onStatus,
|
|
10666
|
-
sandboxExecutor,
|
|
10667
|
-
capabilityRuntime,
|
|
10668
|
-
pluginAuth,
|
|
10669
|
-
agentToolHooks
|
|
10670
|
-
);
|
|
10671
|
-
const agentTools = [...baseAgentTools];
|
|
10672
|
-
const syncMcpAgentTools = () => {
|
|
10673
|
-
const mcpTools = turnMcpToolManager.getResolvedActiveTools(activeSkills);
|
|
10674
|
-
const mcpDefs = mcpToolsToDefinitions(mcpTools);
|
|
10675
|
-
const mcpAgentTools = createAgentTools(
|
|
10676
|
-
mcpDefs,
|
|
10677
|
-
skillSandbox,
|
|
10678
|
-
spanContext,
|
|
10679
|
-
context.onStatus,
|
|
10680
|
-
sandboxExecutor,
|
|
10681
|
-
capabilityRuntime,
|
|
10682
|
-
pluginAuth,
|
|
10683
|
-
agentToolHooks
|
|
10684
|
-
);
|
|
10685
|
-
agentTools.length = 0;
|
|
10686
|
-
agentTools.push(...baseAgentTools, ...mcpAgentTools);
|
|
10687
|
-
};
|
|
10688
|
-
syncMcpAgentTools();
|
|
10689
|
-
agent = new Agent({
|
|
10690
|
-
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
10691
|
-
initialState: {
|
|
10692
|
-
systemPrompt: baseInstructions,
|
|
10693
|
-
model: resolveGatewayModel(botConfig.modelId),
|
|
10694
|
-
tools: agentTools
|
|
10441
|
+
const presentation = renderAssistantStatus({
|
|
10442
|
+
status
|
|
10443
|
+
});
|
|
10444
|
+
if (!presentation.visible) {
|
|
10445
|
+
return;
|
|
10695
10446
|
}
|
|
10696
|
-
|
|
10697
|
-
let hasEmittedText = false;
|
|
10698
|
-
let needsSeparator = false;
|
|
10699
|
-
const unsubscribe = agent.subscribe((event) => {
|
|
10700
|
-
if (event.type === "message_start") {
|
|
10701
|
-
Promise.resolve(context.onAssistantMessageStart?.()).catch((error) => {
|
|
10702
|
-
logWarn(
|
|
10703
|
-
"streaming_message_start_error",
|
|
10704
|
-
{},
|
|
10705
|
-
{
|
|
10706
|
-
"error.message": error instanceof Error ? error.message : String(error)
|
|
10707
|
-
},
|
|
10708
|
-
"Failed to deliver assistant message start to stream coordinator"
|
|
10709
|
-
);
|
|
10710
|
-
});
|
|
10711
|
-
if (hasEmittedText) {
|
|
10712
|
-
needsSeparator = true;
|
|
10713
|
-
}
|
|
10447
|
+
if (presentation.key === currentKey || presentation.key === pendingKey) {
|
|
10714
10448
|
return;
|
|
10715
10449
|
}
|
|
10716
|
-
if (
|
|
10717
|
-
|
|
10718
|
-
|
|
10719
|
-
|
|
10720
|
-
|
|
10721
|
-
needsSeparator = false;
|
|
10722
|
-
hasEmittedText = true;
|
|
10723
|
-
Promise.resolve(context.onTextDelta?.(text)).catch((error) => {
|
|
10724
|
-
logWarn(
|
|
10725
|
-
"streaming_text_delta_error",
|
|
10726
|
-
{},
|
|
10727
|
-
{
|
|
10728
|
-
"error.message": error instanceof Error ? error.message : String(error)
|
|
10729
|
-
},
|
|
10730
|
-
"Failed to deliver text delta to stream"
|
|
10450
|
+
if (presentation.visible === currentVisibleStatus) {
|
|
10451
|
+
clearPending();
|
|
10452
|
+
currentKey = presentation.key;
|
|
10453
|
+
const nextLoadingMessages = getLoadingMessagesForVisibleStatus(
|
|
10454
|
+
presentation.visible
|
|
10731
10455
|
);
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
let completedAssistantTurn = false;
|
|
10737
|
-
try {
|
|
10738
|
-
if (resumedFromCheckpoint) {
|
|
10739
|
-
agent.replaceMessages(existingCheckpoint.piMessages);
|
|
10456
|
+
if (!haveSameLoadingMessages(currentLoadingMessages, nextLoadingMessages)) {
|
|
10457
|
+
void postStatus(presentation.visible, nextLoadingMessages);
|
|
10458
|
+
}
|
|
10459
|
+
return;
|
|
10740
10460
|
}
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
|
|
10745
|
-
|
|
10746
|
-
|
|
10747
|
-
|
|
10748
|
-
|
|
10749
|
-
|
|
10750
|
-
|
|
10751
|
-
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10761
|
-
timedOut = true;
|
|
10762
|
-
agent.abort();
|
|
10763
|
-
reject(
|
|
10764
|
-
new Error(
|
|
10765
|
-
`Agent turn timed out after ${botConfig.turnTimeoutMs}ms`
|
|
10766
|
-
)
|
|
10767
|
-
);
|
|
10768
|
-
}, botConfig.turnTimeoutMs);
|
|
10769
|
-
});
|
|
10770
|
-
try {
|
|
10771
|
-
promptResult = await Promise.race([promptPromise, timeoutPromise]);
|
|
10772
|
-
} catch (error) {
|
|
10773
|
-
if (timedOut) {
|
|
10774
|
-
logWarn(
|
|
10775
|
-
"agent_turn_timeout",
|
|
10776
|
-
{},
|
|
10777
|
-
{
|
|
10778
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10779
|
-
"gen_ai.operation.name": "invoke_agent",
|
|
10780
|
-
"gen_ai.request.model": botConfig.modelId,
|
|
10781
|
-
"app.ai.turn_timeout_ms": botConfig.turnTimeoutMs
|
|
10782
|
-
},
|
|
10783
|
-
"Agent turn timed out and was aborted"
|
|
10784
|
-
);
|
|
10785
|
-
await promptPromise.catch(() => {
|
|
10786
|
-
});
|
|
10787
|
-
timeoutResumeMessages = [...agent.state.messages];
|
|
10788
|
-
}
|
|
10789
|
-
if (getPendingAuthPause()) {
|
|
10790
|
-
timeoutResumeMessages = [...agent.state.messages];
|
|
10791
|
-
throw getPendingAuthPause();
|
|
10792
|
-
}
|
|
10793
|
-
throw error;
|
|
10794
|
-
} finally {
|
|
10795
|
-
if (timeoutId) {
|
|
10796
|
-
clearTimeout(timeoutId);
|
|
10797
|
-
}
|
|
10798
|
-
}
|
|
10799
|
-
newMessages = agent.state.messages.slice(beforeMessageCount);
|
|
10800
|
-
completedAssistantTurn = hasCompletedAssistantTurn(newMessages);
|
|
10801
|
-
if (getPendingAuthPause() && !completedAssistantTurn) {
|
|
10802
|
-
timeoutResumeMessages = [...agent.state.messages];
|
|
10803
|
-
throw getPendingAuthPause();
|
|
10804
|
-
}
|
|
10805
|
-
const outputMessages = newMessages.filter(isAssistantMessage);
|
|
10806
|
-
const outputMessagesAttribute = serializeGenAiAttribute(outputMessages);
|
|
10807
|
-
const usageSummary = extractGenAiUsageSummary(
|
|
10808
|
-
promptResult,
|
|
10809
|
-
agent.state,
|
|
10810
|
-
...outputMessages
|
|
10811
|
-
);
|
|
10812
|
-
turnUsage = Object.values(usageSummary).some(
|
|
10813
|
-
(value) => value !== void 0
|
|
10814
|
-
) ? usageSummary : void 0;
|
|
10815
|
-
setSpanAttributes({
|
|
10816
|
-
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
10817
|
-
...usageSummary.inputTokens !== void 0 ? { "gen_ai.usage.input_tokens": usageSummary.inputTokens } : {},
|
|
10818
|
-
...usageSummary.outputTokens !== void 0 ? { "gen_ai.usage.output_tokens": usageSummary.outputTokens } : {}
|
|
10819
|
-
});
|
|
10461
|
+
const elapsed = now() - lastStatusAt;
|
|
10462
|
+
const waitMs = Math.max(
|
|
10463
|
+
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
10464
|
+
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
10465
|
+
0
|
|
10466
|
+
);
|
|
10467
|
+
if (waitMs <= 0) {
|
|
10468
|
+
clearPending();
|
|
10469
|
+
void postRenderedStatus(status);
|
|
10470
|
+
return;
|
|
10471
|
+
}
|
|
10472
|
+
pendingStatus = status;
|
|
10473
|
+
pendingKey = presentation.key;
|
|
10474
|
+
if (pendingTimer) {
|
|
10475
|
+
return;
|
|
10476
|
+
}
|
|
10477
|
+
pendingTimer = setTimer(
|
|
10478
|
+
() => {
|
|
10479
|
+
pendingTimer = null;
|
|
10480
|
+
void flushPending();
|
|
10820
10481
|
},
|
|
10821
|
-
|
|
10822
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10823
|
-
"gen_ai.operation.name": "invoke_agent",
|
|
10824
|
-
"gen_ai.request.model": botConfig.modelId,
|
|
10825
|
-
...inputMessagesAttribute ? { "gen_ai.input.messages": inputMessagesAttribute } : {}
|
|
10826
|
-
}
|
|
10482
|
+
Math.max(1, waitMs)
|
|
10827
10483
|
);
|
|
10828
|
-
} finally {
|
|
10829
|
-
unsubscribe();
|
|
10830
10484
|
}
|
|
10831
|
-
|
|
10832
|
-
|
|
10485
|
+
};
|
|
10486
|
+
}
|
|
10487
|
+
|
|
10488
|
+
// src/chat/slack/assistant-thread/status-send.ts
|
|
10489
|
+
var SLACK_ASSISTANT_ACTIVE_STATUS = "is working on your request...";
|
|
10490
|
+
function createSlackAdapterStatusSender(args) {
|
|
10491
|
+
const adapter = args.getSlackAdapter();
|
|
10492
|
+
const boundToken = getSlackAdapterRequestToken(adapter);
|
|
10493
|
+
return async (text, loadingMessages) => {
|
|
10494
|
+
const channelId = args.channelId;
|
|
10495
|
+
const threadTs = args.threadTs;
|
|
10496
|
+
if (!channelId || !threadTs) {
|
|
10497
|
+
return;
|
|
10833
10498
|
}
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
sessionId,
|
|
10838
|
-
sliceId: currentSliceId,
|
|
10839
|
-
allMessages: agent.state.messages,
|
|
10840
|
-
loadedSkillNames: activeSkills.map((skill) => skill.name)
|
|
10841
|
-
});
|
|
10499
|
+
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
10500
|
+
if (!normalizedChannelId) {
|
|
10501
|
+
return;
|
|
10842
10502
|
}
|
|
10843
|
-
|
|
10844
|
-
|
|
10845
|
-
|
|
10846
|
-
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
conversationId: timeoutResumeConversationId,
|
|
10863
|
-
sessionId: timeoutResumeSessionId,
|
|
10864
|
-
currentSliceId: timeoutResumeSliceId,
|
|
10865
|
-
messages: timeoutResumeMessages,
|
|
10866
|
-
loadedSkillNames: loadedSkillNamesForResume,
|
|
10867
|
-
errorMessage: error instanceof Error ? error.message : String(error),
|
|
10868
|
-
logContext: {
|
|
10869
|
-
threadId: context.correlation?.threadId,
|
|
10870
|
-
requesterId: context.correlation?.requesterId,
|
|
10871
|
-
channelId: context.correlation?.channelId,
|
|
10872
|
-
runId: context.correlation?.runId,
|
|
10873
|
-
assistantUserName: context.assistant?.userName,
|
|
10874
|
-
modelId: botConfig.modelId
|
|
10875
|
-
}
|
|
10503
|
+
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
10504
|
+
try {
|
|
10505
|
+
await runWithBoundSlackToken(
|
|
10506
|
+
adapter,
|
|
10507
|
+
boundToken,
|
|
10508
|
+
() => adapter.setAssistantStatus(
|
|
10509
|
+
normalizedChannelId,
|
|
10510
|
+
threadTs,
|
|
10511
|
+
text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
10512
|
+
nextLoadingMessages
|
|
10513
|
+
)
|
|
10514
|
+
);
|
|
10515
|
+
} catch (error) {
|
|
10516
|
+
logAssistantStatusFailure({
|
|
10517
|
+
status: text,
|
|
10518
|
+
error,
|
|
10519
|
+
channelId,
|
|
10520
|
+
normalizedChannelId,
|
|
10521
|
+
threadTs
|
|
10876
10522
|
});
|
|
10877
|
-
if (checkpoint) {
|
|
10878
|
-
throw new RetryableTurnError(
|
|
10879
|
-
"turn_timeout_resume",
|
|
10880
|
-
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${checkpoint.sliceId} version=${checkpoint.checkpointVersion}`,
|
|
10881
|
-
{
|
|
10882
|
-
conversationId: timeoutResumeConversationId,
|
|
10883
|
-
sessionId: timeoutResumeSessionId,
|
|
10884
|
-
sliceId: checkpoint.sliceId,
|
|
10885
|
-
checkpointVersion: checkpoint.checkpointVersion
|
|
10886
|
-
}
|
|
10887
|
-
);
|
|
10888
|
-
}
|
|
10889
10523
|
}
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
threadId: context.correlation?.threadId,
|
|
10900
|
-
requesterId: context.correlation?.requesterId,
|
|
10901
|
-
channelId: context.correlation?.channelId,
|
|
10902
|
-
runId: context.correlation?.runId,
|
|
10903
|
-
assistantUserName: context.assistant?.userName,
|
|
10904
|
-
modelId: botConfig.modelId
|
|
10905
|
-
}
|
|
10906
|
-
});
|
|
10907
|
-
throw new RetryableTurnError(
|
|
10908
|
-
error instanceof PluginAuthorizationPauseError ? "plugin_auth_resume" : "mcp_auth_resume",
|
|
10909
|
-
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${nextSliceId}`,
|
|
10910
|
-
{
|
|
10911
|
-
conversationId: timeoutResumeConversationId,
|
|
10912
|
-
sessionId: timeoutResumeSessionId,
|
|
10913
|
-
sliceId: nextSliceId
|
|
10914
|
-
}
|
|
10915
|
-
);
|
|
10524
|
+
};
|
|
10525
|
+
}
|
|
10526
|
+
function createSlackWebApiStatusSender(args) {
|
|
10527
|
+
const getClient2 = args.getSlackClient ?? getSlackClient;
|
|
10528
|
+
return async (text, loadingMessages) => {
|
|
10529
|
+
const channelId = args.channelId;
|
|
10530
|
+
const threadTs = args.threadTs;
|
|
10531
|
+
if (!channelId || !threadTs) {
|
|
10532
|
+
return;
|
|
10916
10533
|
}
|
|
10917
|
-
|
|
10918
|
-
|
|
10534
|
+
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
10535
|
+
if (!normalizedChannelId) {
|
|
10536
|
+
return;
|
|
10919
10537
|
}
|
|
10920
|
-
|
|
10921
|
-
error,
|
|
10922
|
-
"assistant_reply_generation_failed",
|
|
10923
|
-
{
|
|
10924
|
-
slackThreadId: context.correlation?.threadId,
|
|
10925
|
-
slackUserId: context.correlation?.requesterId,
|
|
10926
|
-
slackChannelId: context.correlation?.channelId,
|
|
10927
|
-
runId: context.correlation?.runId,
|
|
10928
|
-
assistantUserName: context.assistant?.userName,
|
|
10929
|
-
modelId: botConfig.modelId
|
|
10930
|
-
},
|
|
10931
|
-
{},
|
|
10932
|
-
"generateAssistantReply failed"
|
|
10933
|
-
);
|
|
10934
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
10935
|
-
return {
|
|
10936
|
-
text: `Error: ${message}`,
|
|
10937
|
-
...getSandboxMetadata(),
|
|
10938
|
-
diagnostics: {
|
|
10939
|
-
outcome: "provider_error",
|
|
10940
|
-
modelId: botConfig.modelId,
|
|
10941
|
-
assistantMessageCount: 0,
|
|
10942
|
-
toolCalls: [],
|
|
10943
|
-
toolResultCount: 0,
|
|
10944
|
-
toolErrorCount: 0,
|
|
10945
|
-
usedPrimaryText: false,
|
|
10946
|
-
durationMs: Date.now() - replyStartedAtMs,
|
|
10947
|
-
errorMessage: message,
|
|
10948
|
-
providerError: error
|
|
10949
|
-
}
|
|
10950
|
-
};
|
|
10951
|
-
} finally {
|
|
10538
|
+
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
10952
10539
|
try {
|
|
10953
|
-
await
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
"
|
|
10957
|
-
{}
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10540
|
+
await getClient2().assistant.threads.setStatus({
|
|
10541
|
+
channel_id: normalizedChannelId,
|
|
10542
|
+
thread_ts: threadTs,
|
|
10543
|
+
status: text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
10544
|
+
...nextLoadingMessages ? { loading_messages: nextLoadingMessages } : {}
|
|
10545
|
+
});
|
|
10546
|
+
} catch (error) {
|
|
10547
|
+
logAssistantStatusFailure({
|
|
10548
|
+
status: text,
|
|
10549
|
+
error,
|
|
10550
|
+
channelId,
|
|
10551
|
+
normalizedChannelId,
|
|
10552
|
+
threadTs
|
|
10553
|
+
});
|
|
10963
10554
|
}
|
|
10555
|
+
};
|
|
10556
|
+
}
|
|
10557
|
+
function getSlackAdapterRequestToken(adapter) {
|
|
10558
|
+
const token = adapter.requestContext?.getStore()?.token;
|
|
10559
|
+
if (typeof token !== "string") {
|
|
10560
|
+
return void 0;
|
|
10561
|
+
}
|
|
10562
|
+
const trimmed = token.trim();
|
|
10563
|
+
return trimmed || void 0;
|
|
10564
|
+
}
|
|
10565
|
+
async function runWithBoundSlackToken(adapter, token, task) {
|
|
10566
|
+
if (!token) {
|
|
10567
|
+
return await task();
|
|
10964
10568
|
}
|
|
10569
|
+
return await adapter.withBotToken(token, task);
|
|
10570
|
+
}
|
|
10571
|
+
function logAssistantStatusFailure(args) {
|
|
10572
|
+
logWarn(
|
|
10573
|
+
"assistant_status_update_failed",
|
|
10574
|
+
{},
|
|
10575
|
+
{
|
|
10576
|
+
"app.slack.status_text": args.status || "(clear)",
|
|
10577
|
+
"app.slack.channel_id_raw": args.channelId,
|
|
10578
|
+
"app.slack.channel_id": args.normalizedChannelId,
|
|
10579
|
+
"app.slack.thread_ts": args.threadTs,
|
|
10580
|
+
"error.message": args.error instanceof Error ? args.error.message : String(args.error)
|
|
10581
|
+
},
|
|
10582
|
+
`Failed to update assistant status channel=${args.normalizedChannelId} raw=${args.channelId} thread=${args.threadTs}`
|
|
10583
|
+
);
|
|
10584
|
+
}
|
|
10585
|
+
|
|
10586
|
+
// src/chat/slack/assistant-thread/status.ts
|
|
10587
|
+
function createSlackAdapterAssistantStatusSession(args) {
|
|
10588
|
+
return createAssistantStatusScheduler({
|
|
10589
|
+
sendStatus: createSlackAdapterStatusSender({
|
|
10590
|
+
channelId: args.channelId,
|
|
10591
|
+
threadTs: args.threadTs,
|
|
10592
|
+
getSlackAdapter: args.getSlackAdapter
|
|
10593
|
+
}),
|
|
10594
|
+
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
10595
|
+
now: args.now,
|
|
10596
|
+
setTimer: args.setTimer,
|
|
10597
|
+
clearTimer: args.clearTimer,
|
|
10598
|
+
random: args.random
|
|
10599
|
+
});
|
|
10600
|
+
}
|
|
10601
|
+
function createSlackWebApiAssistantStatusSession(args) {
|
|
10602
|
+
return createAssistantStatusScheduler({
|
|
10603
|
+
sendStatus: createSlackWebApiStatusSender({
|
|
10604
|
+
channelId: args.channelId,
|
|
10605
|
+
threadTs: args.threadTs,
|
|
10606
|
+
getSlackClient: args.getSlackClient
|
|
10607
|
+
}),
|
|
10608
|
+
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
10609
|
+
now: args.now,
|
|
10610
|
+
setTimer: args.setTimer,
|
|
10611
|
+
clearTimer: args.clearTimer,
|
|
10612
|
+
random: args.random
|
|
10613
|
+
});
|
|
10965
10614
|
}
|
|
10966
10615
|
|
|
10967
10616
|
// src/chat/slack/footer.ts
|