@sentry/junior 0.27.0 → 0.27.2
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 +970 -1317
- 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.",
|
|
@@ -3538,6 +3409,10 @@ function buildSystemPrompt(params) {
|
|
|
3538
3409
|
"- Use Slack-friendly markdown, not full CommonMark. Prefer bold section labels over markdown headings, and use bullets and short code blocks when helpful.",
|
|
3539
3410
|
"- Keep normal responses brief and scannable.",
|
|
3540
3411
|
"- If depth is needed, start with a concise summary and then provide fuller detail.",
|
|
3412
|
+
"- Prefer a single compact thread reply when the full answer comfortably fits within this inline budget.",
|
|
3413
|
+
"- When canvas creation is available and a research or document-style answer would likely need continuation, multiple sections, or future reference value, create a Slack canvas and keep the thread reply to a short summary plus the canvas link.",
|
|
3414
|
+
"- Typical canvas-first cases include long-form research summaries, timelines, bios or profiles, structured notes, plans, comparisons, and other reusable reference documents.",
|
|
3415
|
+
"- Do not create a canvas for short factual answers that fit cleanly in one normal thread reply.",
|
|
3541
3416
|
"- For tool-heavy research, discovery, or source-checking requests, do not send an initial acknowledgment. Start the visible reply only once you can present the actual answer.",
|
|
3542
3417
|
"- Do not narrate tool execution or repeated status updates in the visible reply.",
|
|
3543
3418
|
"- Avoid tables and markdown links like `[label](url)` unless explicitly requested. Prefer plain URLs or Slack-native entities when exact rendering matters.",
|
|
@@ -5193,23 +5068,12 @@ function createReadFileTool() {
|
|
|
5193
5068
|
import { Type as Type6 } from "@sinclair/typebox";
|
|
5194
5069
|
function createReportProgressTool() {
|
|
5195
5070
|
return tool({
|
|
5196
|
-
description: "Update assistant status
|
|
5071
|
+
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
5072
|
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
|
-
)
|
|
5073
|
+
message: Type6.String({
|
|
5074
|
+
minLength: 1,
|
|
5075
|
+
description: "Short user-facing progress message. The UI truncates it if needed."
|
|
5076
|
+
})
|
|
5213
5077
|
})
|
|
5214
5078
|
});
|
|
5215
5079
|
}
|
|
@@ -5805,7 +5669,7 @@ function mergeRecentCanvases(existing, created) {
|
|
|
5805
5669
|
}
|
|
5806
5670
|
function createSlackCanvasCreateTool(context, state) {
|
|
5807
5671
|
return tool({
|
|
5808
|
-
description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when
|
|
5672
|
+
description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, keep the thread reply brief and include the canvas link. Do not use for short answers that fit cleanly in one normal thread reply.",
|
|
5809
5673
|
inputSchema: Type11.Object({
|
|
5810
5674
|
title: Type11.String({
|
|
5811
5675
|
minLength: 1,
|
|
@@ -7254,563 +7118,144 @@ function throwSandboxOperationError(action, error, includeMissingPath = false) {
|
|
|
7254
7118
|
import { Sandbox } from "@vercel/sandbox";
|
|
7255
7119
|
import { createBashTool as createBashTool2 } from "bash-tool";
|
|
7256
7120
|
|
|
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"]
|
|
7121
|
+
// src/chat/sandbox/skill-sync.ts
|
|
7122
|
+
import fs3 from "fs/promises";
|
|
7123
|
+
import path5 from "path";
|
|
7124
|
+
|
|
7125
|
+
// src/chat/sandbox/eval-gh-stub.ts
|
|
7126
|
+
function buildEvalGitHubCliStub() {
|
|
7127
|
+
return `#!/usr/bin/env node
|
|
7128
|
+
const fs = require("node:fs");
|
|
7129
|
+
const path = require("node:path");
|
|
7130
|
+
const { spawnSync } = require("node:child_process");
|
|
7131
|
+
|
|
7132
|
+
const args = process.argv.slice(2);
|
|
7133
|
+
const statePath = "/vercel/sandbox/.junior/eval-gh-state.json";
|
|
7134
|
+
const fallbackBinaries = ["/usr/bin/gh", "/usr/local/bin/gh", "/bin/gh"];
|
|
7135
|
+
const flagsWithValues = new Set([
|
|
7136
|
+
"--repo",
|
|
7137
|
+
"--title",
|
|
7138
|
+
"--body",
|
|
7139
|
+
"--body-file",
|
|
7140
|
+
"--json",
|
|
7141
|
+
"--search",
|
|
7142
|
+
"--state",
|
|
7143
|
+
"--limit",
|
|
7144
|
+
"--method",
|
|
7145
|
+
"--jq",
|
|
7146
|
+
"--template",
|
|
7147
|
+
"--hostname",
|
|
7148
|
+
]);
|
|
7149
|
+
|
|
7150
|
+
function getFlag(name) {
|
|
7151
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7152
|
+
const value = args[index];
|
|
7153
|
+
if (value === name) {
|
|
7154
|
+
return args[index + 1];
|
|
7155
|
+
}
|
|
7156
|
+
if (value.startsWith(name + "=")) {
|
|
7157
|
+
return value.slice(name.length + 1);
|
|
7158
|
+
}
|
|
7310
7159
|
}
|
|
7311
|
-
|
|
7312
|
-
function makeAssistantStatus(kind, context, options) {
|
|
7313
|
-
return {
|
|
7314
|
-
kind,
|
|
7315
|
-
...context ? { context } : {},
|
|
7316
|
-
...options?.source ? { source: options.source } : {}
|
|
7317
|
-
};
|
|
7160
|
+
return undefined;
|
|
7318
7161
|
}
|
|
7319
|
-
|
|
7320
|
-
|
|
7321
|
-
const
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
|
|
7325
|
-
|
|
7326
|
-
|
|
7162
|
+
|
|
7163
|
+
function getPositionals() {
|
|
7164
|
+
const values = [];
|
|
7165
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
7166
|
+
const value = args[index];
|
|
7167
|
+
if (flagsWithValues.has(value)) {
|
|
7168
|
+
index += 1;
|
|
7169
|
+
continue;
|
|
7170
|
+
}
|
|
7171
|
+
if (value.startsWith("--") && value.includes("=")) {
|
|
7172
|
+
continue;
|
|
7173
|
+
}
|
|
7174
|
+
if (value.startsWith("-")) {
|
|
7175
|
+
continue;
|
|
7176
|
+
}
|
|
7177
|
+
values.push(value);
|
|
7178
|
+
}
|
|
7179
|
+
return values;
|
|
7180
|
+
}
|
|
7181
|
+
|
|
7182
|
+
function loadState() {
|
|
7183
|
+
try {
|
|
7184
|
+
return JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
7185
|
+
} catch {
|
|
7186
|
+
return { nextIssueNumber: 101, issues: {} };
|
|
7187
|
+
}
|
|
7188
|
+
}
|
|
7189
|
+
|
|
7190
|
+
function saveState(state) {
|
|
7191
|
+
fs.mkdirSync(path.dirname(statePath), { recursive: true });
|
|
7192
|
+
fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
7193
|
+
}
|
|
7194
|
+
|
|
7195
|
+
function issueUrl(repo, number) {
|
|
7196
|
+
return "https://github.com/" + repo + "/issues/" + number;
|
|
7197
|
+
}
|
|
7198
|
+
|
|
7199
|
+
function repoValue() {
|
|
7200
|
+
return getFlag("--repo") || "getsentry/junior";
|
|
7201
|
+
}
|
|
7202
|
+
|
|
7203
|
+
function readBody() {
|
|
7204
|
+
const bodyFile = getFlag("--body-file");
|
|
7205
|
+
if (bodyFile) {
|
|
7206
|
+
try {
|
|
7207
|
+
return fs.readFileSync(bodyFile, "utf8");
|
|
7208
|
+
} catch {
|
|
7209
|
+
return "";
|
|
7210
|
+
}
|
|
7211
|
+
}
|
|
7212
|
+
return getFlag("--body") || "";
|
|
7213
|
+
}
|
|
7214
|
+
|
|
7215
|
+
function defaultIssue(repo, number) {
|
|
7327
7216
|
return {
|
|
7328
|
-
|
|
7329
|
-
|
|
7217
|
+
number,
|
|
7218
|
+
title: "Eval issue",
|
|
7219
|
+
body: "",
|
|
7220
|
+
state: "OPEN",
|
|
7221
|
+
url: issueUrl(repo, number),
|
|
7222
|
+
labels: [],
|
|
7223
|
+
assignees: [],
|
|
7224
|
+
author: { login: "junior-eval" },
|
|
7330
7225
|
};
|
|
7331
7226
|
}
|
|
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;
|
|
7227
|
+
|
|
7228
|
+
function pickFields(record, csv) {
|
|
7229
|
+
if (!csv) {
|
|
7230
|
+
return record;
|
|
7341
7231
|
}
|
|
7342
|
-
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7232
|
+
return Object.fromEntries(
|
|
7233
|
+
csv
|
|
7234
|
+
.split(",")
|
|
7235
|
+
.map((value) => value.trim())
|
|
7236
|
+
.filter(Boolean)
|
|
7237
|
+
.map((key) => [key, key in record ? record[key] : null]),
|
|
7238
|
+
);
|
|
7239
|
+
}
|
|
7240
|
+
|
|
7241
|
+
function outputJson(value) {
|
|
7242
|
+
fs.writeFileSync(process.stdout.fd, JSON.stringify(value, null, 2) + "\\n");
|
|
7243
|
+
}
|
|
7244
|
+
|
|
7245
|
+
function outputText(value) {
|
|
7246
|
+
fs.writeFileSync(process.stdout.fd, value);
|
|
7247
|
+
}
|
|
7248
|
+
|
|
7249
|
+
function fallbackToRealGh() {
|
|
7250
|
+
for (const binary of fallbackBinaries) {
|
|
7251
|
+
if (!fs.existsSync(binary)) {
|
|
7252
|
+
continue;
|
|
7253
|
+
}
|
|
7254
|
+
const result = spawnSync(binary, args, { stdio: "inherit" });
|
|
7255
|
+
process.exit(result.status ?? 1);
|
|
7349
7256
|
}
|
|
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);
|
|
7257
|
+
process.stderr.write("gh stub: unsupported command\\n");
|
|
7258
|
+
process.exit(1);
|
|
7814
7259
|
}
|
|
7815
7260
|
|
|
7816
7261
|
if (args.length === 0 || args[0] === "--version" || args[0] === "version") {
|
|
@@ -8233,12 +7678,6 @@ var SANDBOX_RUNTIME = "node22";
|
|
|
8233
7678
|
var SANDBOX_RUNTIME_BIN_DIR = `${SANDBOX_WORKSPACE_ROOT}/.junior/bin`;
|
|
8234
7679
|
var SNAPSHOT_BOOT_RETRY_COUNT = 3;
|
|
8235
7680
|
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
7681
|
function mergeNetworkPolicyWithHeaderTransforms(networkPolicy, headerTransforms) {
|
|
8243
7682
|
const basePolicy = networkPolicy && typeof networkPolicy === "object" && !Array.isArray(networkPolicy) ? { ...networkPolicy } : {};
|
|
8244
7683
|
const existingAllowRaw = basePolicy.allow;
|
|
@@ -8279,26 +7718,6 @@ function sleep2(ms) {
|
|
|
8279
7718
|
setTimeout(resolve, ms);
|
|
8280
7719
|
});
|
|
8281
7720
|
}
|
|
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
7721
|
function parseKeepAliveMs() {
|
|
8303
7722
|
const parsed = Number.parseInt(
|
|
8304
7723
|
process.env.VERCEL_SANDBOX_KEEPALIVE_MS ?? "0",
|
|
@@ -8316,23 +7735,6 @@ function createSandboxSessionManager(options) {
|
|
|
8316
7735
|
const traceContext = options?.traceContext ?? {};
|
|
8317
7736
|
const dependencyProfileHash = getRuntimeDependencyProfileHash(SANDBOX_RUNTIME);
|
|
8318
7737
|
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
7738
|
const clearSession = () => {
|
|
8337
7739
|
sandbox = null;
|
|
8338
7740
|
sandboxIdHint = void 0;
|
|
@@ -8408,16 +7810,9 @@ function createSandboxSessionManager(options) {
|
|
|
8408
7810
|
});
|
|
8409
7811
|
return replacement;
|
|
8410
7812
|
};
|
|
8411
|
-
const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials
|
|
7813
|
+
const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials) => {
|
|
8412
7814
|
for (let attempt = 0; attempt < SNAPSHOT_BOOT_RETRY_COUNT; attempt += 1) {
|
|
8413
7815
|
try {
|
|
8414
|
-
if (emitStatus) {
|
|
8415
|
-
await emitSandboxStatus(
|
|
8416
|
-
"snapshot_boot",
|
|
8417
|
-
emitStatus,
|
|
8418
|
-
makeAssistantStatus("loading", "sandbox")
|
|
8419
|
-
);
|
|
8420
|
-
}
|
|
8421
7816
|
return await Sandbox.create({
|
|
8422
7817
|
timeout: timeoutMs,
|
|
8423
7818
|
source: {
|
|
@@ -8450,13 +7845,8 @@ function createSandboxSessionManager(options) {
|
|
|
8450
7845
|
});
|
|
8451
7846
|
};
|
|
8452
7847
|
const createSandboxFromResolvedSnapshot = async (params) => {
|
|
8453
|
-
const { runtime, snapshot, sandboxCredentials
|
|
7848
|
+
const { runtime, snapshot, sandboxCredentials } = params;
|
|
8454
7849
|
if (!snapshot.snapshotId) {
|
|
8455
|
-
await emitSandboxStatus(
|
|
8456
|
-
"fresh_runtime_boot",
|
|
8457
|
-
status,
|
|
8458
|
-
makeAssistantStatus("loading", "sandbox")
|
|
8459
|
-
);
|
|
8460
7850
|
return await Sandbox.create({
|
|
8461
7851
|
timeout: timeoutMs,
|
|
8462
7852
|
runtime,
|
|
@@ -8466,8 +7856,7 @@ function createSandboxSessionManager(options) {
|
|
|
8466
7856
|
try {
|
|
8467
7857
|
return await createSandboxFromSnapshot(
|
|
8468
7858
|
snapshot.snapshotId,
|
|
8469
|
-
sandboxCredentials
|
|
8470
|
-
status.emit
|
|
7859
|
+
sandboxCredentials
|
|
8471
7860
|
);
|
|
8472
7861
|
} catch (error) {
|
|
8473
7862
|
if (!isSnapshotMissingError(error)) {
|
|
@@ -8480,23 +7869,20 @@ function createSandboxSessionManager(options) {
|
|
|
8480
7869
|
runtime,
|
|
8481
7870
|
timeoutMs,
|
|
8482
7871
|
forceRebuild: true,
|
|
8483
|
-
staleSnapshotId: snapshot.snapshotId
|
|
8484
|
-
onProgress: status.reportSnapshotPhase
|
|
7872
|
+
staleSnapshotId: snapshot.snapshotId
|
|
8485
7873
|
});
|
|
8486
7874
|
if (!rebuiltSnapshot.snapshotId) {
|
|
8487
7875
|
throw error;
|
|
8488
7876
|
}
|
|
8489
7877
|
return await createSandboxFromSnapshot(
|
|
8490
7878
|
rebuiltSnapshot.snapshotId,
|
|
8491
|
-
sandboxCredentials
|
|
8492
|
-
status.emit
|
|
7879
|
+
sandboxCredentials
|
|
8493
7880
|
);
|
|
8494
7881
|
}
|
|
8495
7882
|
};
|
|
8496
7883
|
const createFreshSandbox = async () => {
|
|
8497
7884
|
const runtime = SANDBOX_RUNTIME;
|
|
8498
7885
|
const sandboxCredentials = getVercelSandboxCredentials();
|
|
8499
|
-
const status = createStatusEmitter(options?.onStatus);
|
|
8500
7886
|
let createdSandbox;
|
|
8501
7887
|
try {
|
|
8502
7888
|
createdSandbox = await withSandboxSpan(
|
|
@@ -8508,22 +7894,15 @@ function createSandboxSessionManager(options) {
|
|
|
8508
7894
|
"app.sandbox.runtime": runtime
|
|
8509
7895
|
},
|
|
8510
7896
|
async () => {
|
|
8511
|
-
await emitSandboxStatus(
|
|
8512
|
-
"runtime_dependency_resolve",
|
|
8513
|
-
status,
|
|
8514
|
-
makeAssistantStatus("loading", "sandbox runtime")
|
|
8515
|
-
);
|
|
8516
7897
|
const snapshot = await resolveRuntimeDependencySnapshot({
|
|
8517
7898
|
runtime,
|
|
8518
|
-
timeoutMs
|
|
8519
|
-
onProgress: status.reportSnapshotPhase
|
|
7899
|
+
timeoutMs
|
|
8520
7900
|
});
|
|
8521
7901
|
setSnapshotAttributes(snapshot);
|
|
8522
7902
|
return await createSandboxFromResolvedSnapshot({
|
|
8523
7903
|
runtime,
|
|
8524
7904
|
snapshot,
|
|
8525
|
-
sandboxCredentials
|
|
8526
|
-
status
|
|
7905
|
+
sandboxCredentials
|
|
8527
7906
|
});
|
|
8528
7907
|
}
|
|
8529
7908
|
);
|
|
@@ -8837,7 +8216,6 @@ function createSandboxExecutor(options) {
|
|
|
8837
8216
|
sandboxDependencyProfileHash: options?.sandboxDependencyProfileHash,
|
|
8838
8217
|
timeoutMs: options?.timeoutMs,
|
|
8839
8218
|
traceContext,
|
|
8840
|
-
onStatus: options?.onStatus,
|
|
8841
8219
|
onSandboxAcquired: options?.onSandboxAcquired
|
|
8842
8220
|
});
|
|
8843
8221
|
const withSandboxSpan = (name, op, attributes, callback) => withSpan(name, op, traceContext, callback, attributes);
|
|
@@ -9195,102 +8573,15 @@ function buildReportedProgressStatus(input) {
|
|
|
9195
8573
|
if (!input || typeof input !== "object") {
|
|
9196
8574
|
return void 0;
|
|
9197
8575
|
}
|
|
9198
|
-
const
|
|
9199
|
-
if (typeof
|
|
8576
|
+
const message = input.message;
|
|
8577
|
+
if (typeof message !== "string") {
|
|
9200
8578
|
return void 0;
|
|
9201
8579
|
}
|
|
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]}`);
|
|
8580
|
+
const text = message.trim();
|
|
8581
|
+
if (!text) {
|
|
8582
|
+
return void 0;
|
|
9291
8583
|
}
|
|
9292
|
-
|
|
9293
|
-
return makeAssistantStatus("running", readable || "tool");
|
|
8584
|
+
return { text };
|
|
9294
8585
|
}
|
|
9295
8586
|
|
|
9296
8587
|
// src/chat/tools/execution/build-sandbox-input.ts
|
|
@@ -9441,7 +8732,12 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
9441
8732
|
turnId: spanContext.turnId,
|
|
9442
8733
|
agentId: spanContext.agentId
|
|
9443
8734
|
};
|
|
9444
|
-
|
|
8735
|
+
if (toolName === "reportProgress") {
|
|
8736
|
+
const status = buildReportedProgressStatus(params);
|
|
8737
|
+
if (status) {
|
|
8738
|
+
await onStatus?.(status);
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
9445
8741
|
return withSpan(
|
|
9446
8742
|
`execute_tool ${toolName}`,
|
|
9447
8743
|
"gen_ai.execute_tool",
|
|
@@ -10344,7 +9640,6 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10344
9640
|
sandboxId: context.sandbox?.sandboxId,
|
|
10345
9641
|
sandboxDependencyProfileHash: context.sandbox?.sandboxDependencyProfileHash,
|
|
10346
9642
|
traceContext: spanContext,
|
|
10347
|
-
onStatus: context.onStatus,
|
|
10348
9643
|
onSandboxAcquired: async (sandbox2) => {
|
|
10349
9644
|
lastKnownSandboxId = sandbox2.sandboxId;
|
|
10350
9645
|
lastKnownSandboxDependencyProfileHash = sandbox2.sandboxDependencyProfileHash;
|
|
@@ -10476,492 +9771,850 @@ async function generateAssistantReply(messageText, context = {}) {
|
|
|
10476
9771
|
const syncResumeState = () => {
|
|
10477
9772
|
loadedSkillNamesForResume = activeSkills.map((skill) => skill.name);
|
|
10478
9773
|
};
|
|
10479
|
-
const enableSkillCredentials = async (skill, reason) => {
|
|
10480
|
-
if (!skill?.pluginProvider) {
|
|
10481
|
-
return;
|
|
9774
|
+
const enableSkillCredentials = async (skill, reason) => {
|
|
9775
|
+
if (!skill?.pluginProvider) {
|
|
9776
|
+
return;
|
|
9777
|
+
}
|
|
9778
|
+
try {
|
|
9779
|
+
await capabilityRuntime.enableCredentialsForTurn({
|
|
9780
|
+
activeSkill: skill,
|
|
9781
|
+
reason
|
|
9782
|
+
});
|
|
9783
|
+
} catch (error) {
|
|
9784
|
+
if (error instanceof CredentialUnavailableError && context.requester?.userId) {
|
|
9785
|
+
await pluginAuth.handleCredentialUnavailable({
|
|
9786
|
+
activeSkill: skill,
|
|
9787
|
+
error
|
|
9788
|
+
});
|
|
9789
|
+
}
|
|
9790
|
+
throw error;
|
|
9791
|
+
}
|
|
9792
|
+
};
|
|
9793
|
+
setTags({
|
|
9794
|
+
conversationId: spanContext.conversationId,
|
|
9795
|
+
turnId: spanContext.turnId,
|
|
9796
|
+
agentId: spanContext.agentId,
|
|
9797
|
+
slackThreadId: context.correlation?.threadId,
|
|
9798
|
+
slackUserId: context.correlation?.requesterId,
|
|
9799
|
+
slackChannelId: context.correlation?.channelId,
|
|
9800
|
+
runId: context.correlation?.runId,
|
|
9801
|
+
assistantUserName: context.assistant?.userName,
|
|
9802
|
+
modelId: botConfig.modelId
|
|
9803
|
+
});
|
|
9804
|
+
const tools = createTools(
|
|
9805
|
+
availableSkills,
|
|
9806
|
+
{
|
|
9807
|
+
getGeneratedFile: (filename) => generatedFiles.find((file) => file.filename === filename),
|
|
9808
|
+
onGeneratedArtifactFiles: (files) => {
|
|
9809
|
+
generatedFiles.push(...files);
|
|
9810
|
+
},
|
|
9811
|
+
onGeneratedFiles: (files) => {
|
|
9812
|
+
replyFiles.push(...files);
|
|
9813
|
+
},
|
|
9814
|
+
onArtifactStatePatch: async (patch) => {
|
|
9815
|
+
Object.assign(artifactStatePatch, patch);
|
|
9816
|
+
await context.onArtifactStateUpdated?.(
|
|
9817
|
+
mergeArtifactsState(
|
|
9818
|
+
context.artifactState ?? {},
|
|
9819
|
+
artifactStatePatch
|
|
9820
|
+
)
|
|
9821
|
+
);
|
|
9822
|
+
},
|
|
9823
|
+
toolOverrides: context.toolOverrides,
|
|
9824
|
+
onSkillLoaded: async (loadedSkill) => {
|
|
9825
|
+
const resolvedSkill = await skillSandbox.loadSkill(loadedSkill.name);
|
|
9826
|
+
const effective = resolvedSkill ?? loadedSkill;
|
|
9827
|
+
upsertActiveSkill(activeSkills, effective);
|
|
9828
|
+
syncResumeState();
|
|
9829
|
+
await turnMcpToolManager.activateForSkill(effective);
|
|
9830
|
+
syncResumeState();
|
|
9831
|
+
if (mcpAuth.getPendingPause()) {
|
|
9832
|
+
return void 0;
|
|
9833
|
+
}
|
|
9834
|
+
await enableSkillCredentials(
|
|
9835
|
+
effective,
|
|
9836
|
+
`skill:${effective.name}:turn:load`
|
|
9837
|
+
);
|
|
9838
|
+
if (!effective.pluginProvider) {
|
|
9839
|
+
return void 0;
|
|
9840
|
+
}
|
|
9841
|
+
syncMcpAgentTools();
|
|
9842
|
+
return {
|
|
9843
|
+
available_tools: turnMcpToolManager.getActiveToolCatalog(activeSkills, {
|
|
9844
|
+
provider: effective.pluginProvider
|
|
9845
|
+
}).map(toExposedToolSummary)
|
|
9846
|
+
};
|
|
9847
|
+
}
|
|
9848
|
+
},
|
|
9849
|
+
{
|
|
9850
|
+
channelId: context.toolChannelId ?? context.correlation?.channelId,
|
|
9851
|
+
channelCapabilities: resolveChannelCapabilities(
|
|
9852
|
+
context.toolChannelId ?? context.correlation?.channelId
|
|
9853
|
+
),
|
|
9854
|
+
messageTs: context.correlation?.messageTs,
|
|
9855
|
+
threadTs: context.correlation?.threadTs,
|
|
9856
|
+
userText: userInput,
|
|
9857
|
+
artifactState: context.artifactState,
|
|
9858
|
+
configuration: configurationValues,
|
|
9859
|
+
getActiveSkills: () => activeSkills,
|
|
9860
|
+
mcpToolManager: turnMcpToolManager,
|
|
9861
|
+
sandbox
|
|
9862
|
+
}
|
|
9863
|
+
);
|
|
9864
|
+
syncResumeState();
|
|
9865
|
+
for (const skill of activeSkills) {
|
|
9866
|
+
await turnMcpToolManager.activateForSkill(skill);
|
|
9867
|
+
syncResumeState();
|
|
9868
|
+
if (mcpAuth.getPendingPause()) {
|
|
9869
|
+
timeoutResumeMessages = existingCheckpoint?.piMessages ?? [];
|
|
9870
|
+
throw mcpAuth.getPendingPause();
|
|
9871
|
+
}
|
|
9872
|
+
await enableSkillCredentials(skill, `skill:${skill.name}:turn:resume`);
|
|
9873
|
+
}
|
|
9874
|
+
syncResumeState();
|
|
9875
|
+
const activeToolSummaries = turnMcpToolManager.getActiveToolCatalog(activeSkills).map(toExposedToolSummary);
|
|
9876
|
+
baseInstructions = buildSystemPrompt({
|
|
9877
|
+
availableSkills,
|
|
9878
|
+
activeSkills,
|
|
9879
|
+
activeTools: activeToolSummaries,
|
|
9880
|
+
invocation: skillInvocation,
|
|
9881
|
+
assistant: context.assistant,
|
|
9882
|
+
requester: context.requester,
|
|
9883
|
+
artifactState: context.artifactState,
|
|
9884
|
+
configuration: configurationValues,
|
|
9885
|
+
relevantConfigurationKeys: collectRelevantConfigurationKeys(
|
|
9886
|
+
activeSkills,
|
|
9887
|
+
invokedSkill
|
|
9888
|
+
),
|
|
9889
|
+
runtimeMetadata: getRuntimeMetadata(),
|
|
9890
|
+
threadParticipants: context.threadParticipants
|
|
9891
|
+
});
|
|
9892
|
+
const userContentParts = [{ type: "text", text: userTurnText }];
|
|
9893
|
+
const omittedImageAttachmentCount = context.omittedImageAttachmentCount ?? 0;
|
|
9894
|
+
if (omittedImageAttachmentCount > 0) {
|
|
9895
|
+
userContentParts.push({
|
|
9896
|
+
type: "text",
|
|
9897
|
+
text: buildOmittedImageAttachmentNotice(omittedImageAttachmentCount)
|
|
9898
|
+
});
|
|
9899
|
+
}
|
|
9900
|
+
for (const attachment of context.userAttachments ?? []) {
|
|
9901
|
+
if (attachment.promptText) {
|
|
9902
|
+
userContentParts.push({
|
|
9903
|
+
type: "text",
|
|
9904
|
+
text: attachment.promptText
|
|
9905
|
+
});
|
|
9906
|
+
} else if (attachment.mediaType.startsWith("image/")) {
|
|
9907
|
+
if (!attachment.data) {
|
|
9908
|
+
throw new Error("Image attachment is missing image data");
|
|
9909
|
+
}
|
|
9910
|
+
userContentParts.push({
|
|
9911
|
+
type: "image",
|
|
9912
|
+
data: attachment.data.toString("base64"),
|
|
9913
|
+
mimeType: attachment.mediaType
|
|
9914
|
+
});
|
|
9915
|
+
} else {
|
|
9916
|
+
if (!attachment.data) {
|
|
9917
|
+
throw new Error("Attachment is missing attachment data");
|
|
9918
|
+
}
|
|
9919
|
+
const promptAttachment = {
|
|
9920
|
+
data: attachment.data,
|
|
9921
|
+
mediaType: attachment.mediaType,
|
|
9922
|
+
filename: attachment.filename
|
|
9923
|
+
};
|
|
9924
|
+
userContentParts.push({
|
|
9925
|
+
type: "text",
|
|
9926
|
+
text: encodeNonImageAttachmentForPrompt(promptAttachment)
|
|
9927
|
+
});
|
|
9928
|
+
}
|
|
9929
|
+
}
|
|
9930
|
+
const inputMessagesAttribute = serializeGenAiAttribute([
|
|
9931
|
+
{
|
|
9932
|
+
role: "system",
|
|
9933
|
+
content: [{ type: "text", text: baseInstructions }]
|
|
9934
|
+
},
|
|
9935
|
+
{
|
|
9936
|
+
role: "user",
|
|
9937
|
+
content: userContentParts.map((part) => toObservablePromptPart(part))
|
|
9938
|
+
}
|
|
9939
|
+
]);
|
|
9940
|
+
const agentToolHooks = {
|
|
9941
|
+
onToolCall: (toolName) => {
|
|
9942
|
+
toolCalls.push(toolName);
|
|
9943
|
+
Promise.resolve(context.onToolCall?.(toolName)).catch((error) => {
|
|
9944
|
+
logWarn(
|
|
9945
|
+
"streaming_tool_call_error",
|
|
9946
|
+
{},
|
|
9947
|
+
{
|
|
9948
|
+
"error.message": error instanceof Error ? error.message : String(error),
|
|
9949
|
+
"gen_ai.tool.name": toolName
|
|
9950
|
+
},
|
|
9951
|
+
"Failed to deliver tool call event to stream coordinator"
|
|
9952
|
+
);
|
|
9953
|
+
});
|
|
9954
|
+
}
|
|
9955
|
+
};
|
|
9956
|
+
const baseAgentTools = createAgentTools(
|
|
9957
|
+
tools,
|
|
9958
|
+
skillSandbox,
|
|
9959
|
+
spanContext,
|
|
9960
|
+
context.onStatus,
|
|
9961
|
+
sandboxExecutor,
|
|
9962
|
+
capabilityRuntime,
|
|
9963
|
+
pluginAuth,
|
|
9964
|
+
agentToolHooks
|
|
9965
|
+
);
|
|
9966
|
+
const agentTools = [...baseAgentTools];
|
|
9967
|
+
const syncMcpAgentTools = () => {
|
|
9968
|
+
const mcpTools = turnMcpToolManager.getResolvedActiveTools(activeSkills);
|
|
9969
|
+
const mcpDefs = mcpToolsToDefinitions(mcpTools);
|
|
9970
|
+
const mcpAgentTools = createAgentTools(
|
|
9971
|
+
mcpDefs,
|
|
9972
|
+
skillSandbox,
|
|
9973
|
+
spanContext,
|
|
9974
|
+
context.onStatus,
|
|
9975
|
+
sandboxExecutor,
|
|
9976
|
+
capabilityRuntime,
|
|
9977
|
+
pluginAuth,
|
|
9978
|
+
agentToolHooks
|
|
9979
|
+
);
|
|
9980
|
+
agentTools.length = 0;
|
|
9981
|
+
agentTools.push(...baseAgentTools, ...mcpAgentTools);
|
|
9982
|
+
};
|
|
9983
|
+
syncMcpAgentTools();
|
|
9984
|
+
agent = new Agent({
|
|
9985
|
+
getApiKey: () => getPiGatewayApiKeyOverride(),
|
|
9986
|
+
initialState: {
|
|
9987
|
+
systemPrompt: baseInstructions,
|
|
9988
|
+
model: resolveGatewayModel(botConfig.modelId),
|
|
9989
|
+
tools: agentTools
|
|
10482
9990
|
}
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
9991
|
+
});
|
|
9992
|
+
let hasEmittedText = false;
|
|
9993
|
+
let needsSeparator = false;
|
|
9994
|
+
const unsubscribe = agent.subscribe((event) => {
|
|
9995
|
+
if (event.type === "message_start") {
|
|
9996
|
+
Promise.resolve(context.onAssistantMessageStart?.()).catch((error) => {
|
|
9997
|
+
logWarn(
|
|
9998
|
+
"streaming_message_start_error",
|
|
9999
|
+
{},
|
|
10000
|
+
{
|
|
10001
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
10002
|
+
},
|
|
10003
|
+
"Failed to deliver assistant message start to stream coordinator"
|
|
10004
|
+
);
|
|
10487
10005
|
});
|
|
10488
|
-
|
|
10489
|
-
|
|
10490
|
-
await pluginAuth.handleCredentialUnavailable({
|
|
10491
|
-
activeSkill: skill,
|
|
10492
|
-
error
|
|
10493
|
-
});
|
|
10006
|
+
if (hasEmittedText) {
|
|
10007
|
+
needsSeparator = true;
|
|
10494
10008
|
}
|
|
10495
|
-
|
|
10009
|
+
return;
|
|
10496
10010
|
}
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
10504
|
-
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10011
|
+
if (event.type !== "message_update") return;
|
|
10012
|
+
if (event.assistantMessageEvent.type !== "text_delta") return;
|
|
10013
|
+
const deltaText = event.assistantMessageEvent.delta;
|
|
10014
|
+
if (!deltaText) return;
|
|
10015
|
+
const text = needsSeparator ? "\n\n" + deltaText : deltaText;
|
|
10016
|
+
needsSeparator = false;
|
|
10017
|
+
hasEmittedText = true;
|
|
10018
|
+
Promise.resolve(context.onTextDelta?.(text)).catch((error) => {
|
|
10019
|
+
logWarn(
|
|
10020
|
+
"streaming_text_delta_error",
|
|
10021
|
+
{},
|
|
10022
|
+
{
|
|
10023
|
+
"error.message": error instanceof Error ? error.message : String(error)
|
|
10024
|
+
},
|
|
10025
|
+
"Failed to deliver text delta to stream"
|
|
10026
|
+
);
|
|
10027
|
+
});
|
|
10508
10028
|
});
|
|
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
|
-
|
|
10029
|
+
let beforeMessageCount = agent.state.messages.length;
|
|
10030
|
+
let newMessages = [];
|
|
10031
|
+
let completedAssistantTurn = false;
|
|
10032
|
+
try {
|
|
10033
|
+
if (resumedFromCheckpoint) {
|
|
10034
|
+
agent.replaceMessages(existingCheckpoint.piMessages);
|
|
10035
|
+
}
|
|
10036
|
+
beforeMessageCount = agent.state.messages.length;
|
|
10037
|
+
await withSpan(
|
|
10038
|
+
"ai.generate_assistant_reply",
|
|
10039
|
+
"gen_ai.invoke_agent",
|
|
10040
|
+
spanContext,
|
|
10041
|
+
async () => {
|
|
10042
|
+
let promptResult;
|
|
10043
|
+
const promptPromise = resumedFromCheckpoint ? (
|
|
10044
|
+
// Checkpoint resumes continue from the persisted Pi message
|
|
10045
|
+
// state. Any reconstructed replyContext only matters when the
|
|
10046
|
+
// turn parked before the initial user prompt was recorded.
|
|
10047
|
+
agent.continue()
|
|
10048
|
+
) : agent.prompt({
|
|
10049
|
+
role: "user",
|
|
10050
|
+
content: userContentParts,
|
|
10051
|
+
timestamp: Date.now()
|
|
10052
|
+
});
|
|
10053
|
+
let timeoutId;
|
|
10054
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
10055
|
+
timeoutId = setTimeout(() => {
|
|
10056
|
+
timedOut = true;
|
|
10057
|
+
agent.abort();
|
|
10058
|
+
reject(
|
|
10059
|
+
new Error(
|
|
10060
|
+
`Agent turn timed out after ${botConfig.turnTimeoutMs}ms`
|
|
10061
|
+
)
|
|
10062
|
+
);
|
|
10063
|
+
}, botConfig.turnTimeoutMs);
|
|
10064
|
+
});
|
|
10065
|
+
try {
|
|
10066
|
+
promptResult = await Promise.race([promptPromise, timeoutPromise]);
|
|
10067
|
+
} catch (error) {
|
|
10068
|
+
if (timedOut) {
|
|
10069
|
+
logWarn(
|
|
10070
|
+
"agent_turn_timeout",
|
|
10071
|
+
{},
|
|
10072
|
+
{
|
|
10073
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10074
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
10075
|
+
"gen_ai.request.model": botConfig.modelId,
|
|
10076
|
+
"app.ai.turn_timeout_ms": botConfig.turnTimeoutMs
|
|
10077
|
+
},
|
|
10078
|
+
"Agent turn timed out and was aborted"
|
|
10079
|
+
);
|
|
10080
|
+
await promptPromise.catch(() => {
|
|
10081
|
+
});
|
|
10082
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10083
|
+
}
|
|
10084
|
+
if (getPendingAuthPause()) {
|
|
10085
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10086
|
+
throw getPendingAuthPause();
|
|
10087
|
+
}
|
|
10088
|
+
throw error;
|
|
10089
|
+
} finally {
|
|
10090
|
+
if (timeoutId) {
|
|
10091
|
+
clearTimeout(timeoutId);
|
|
10092
|
+
}
|
|
10538
10093
|
}
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
10544
|
-
return void 0;
|
|
10094
|
+
newMessages = agent.state.messages.slice(beforeMessageCount);
|
|
10095
|
+
completedAssistantTurn = hasCompletedAssistantTurn(newMessages);
|
|
10096
|
+
if (getPendingAuthPause() && !completedAssistantTurn) {
|
|
10097
|
+
timeoutResumeMessages = [...agent.state.messages];
|
|
10098
|
+
throw getPendingAuthPause();
|
|
10545
10099
|
}
|
|
10546
|
-
|
|
10547
|
-
|
|
10548
|
-
|
|
10549
|
-
|
|
10550
|
-
|
|
10551
|
-
|
|
10100
|
+
const outputMessages = newMessages.filter(isAssistantMessage);
|
|
10101
|
+
const outputMessagesAttribute = serializeGenAiAttribute(outputMessages);
|
|
10102
|
+
const usageSummary = extractGenAiUsageSummary(
|
|
10103
|
+
promptResult,
|
|
10104
|
+
agent.state,
|
|
10105
|
+
...outputMessages
|
|
10106
|
+
);
|
|
10107
|
+
turnUsage = Object.values(usageSummary).some(
|
|
10108
|
+
(value) => value !== void 0
|
|
10109
|
+
) ? usageSummary : void 0;
|
|
10110
|
+
setSpanAttributes({
|
|
10111
|
+
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
10112
|
+
...usageSummary.inputTokens !== void 0 ? { "gen_ai.usage.input_tokens": usageSummary.inputTokens } : {},
|
|
10113
|
+
...usageSummary.outputTokens !== void 0 ? { "gen_ai.usage.output_tokens": usageSummary.outputTokens } : {}
|
|
10114
|
+
});
|
|
10115
|
+
},
|
|
10116
|
+
{
|
|
10117
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
10118
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
10119
|
+
"gen_ai.request.model": botConfig.modelId,
|
|
10120
|
+
...inputMessagesAttribute ? { "gen_ai.input.messages": inputMessagesAttribute } : {}
|
|
10121
|
+
}
|
|
10122
|
+
);
|
|
10123
|
+
} finally {
|
|
10124
|
+
unsubscribe();
|
|
10125
|
+
}
|
|
10126
|
+
if (getPendingAuthPause() && !completedAssistantTurn) {
|
|
10127
|
+
throw getPendingAuthPause();
|
|
10128
|
+
}
|
|
10129
|
+
if (checkpointState.canUseTurnSession && sessionConversationId && sessionId) {
|
|
10130
|
+
await persistCompletedCheckpoint({
|
|
10131
|
+
conversationId: sessionConversationId,
|
|
10132
|
+
sessionId,
|
|
10133
|
+
sliceId: currentSliceId,
|
|
10134
|
+
allMessages: agent.state.messages,
|
|
10135
|
+
loadedSkillNames: activeSkills.map((skill) => skill.name)
|
|
10136
|
+
});
|
|
10137
|
+
}
|
|
10138
|
+
return buildTurnResult({
|
|
10139
|
+
newMessages,
|
|
10140
|
+
userInput,
|
|
10141
|
+
replyFiles,
|
|
10142
|
+
artifactStatePatch,
|
|
10143
|
+
toolCalls,
|
|
10144
|
+
sandboxId: currentSandboxExecutor.getSandboxId(),
|
|
10145
|
+
sandboxDependencyProfileHash: currentSandboxExecutor.getDependencyProfileHash(),
|
|
10146
|
+
durationMs: Date.now() - replyStartedAtMs,
|
|
10147
|
+
generatedFileCount: generatedFiles.length,
|
|
10148
|
+
shouldTrace,
|
|
10149
|
+
spanContext,
|
|
10150
|
+
usage: turnUsage,
|
|
10151
|
+
correlation: context.correlation,
|
|
10152
|
+
assistantUserName: context.assistant?.userName
|
|
10153
|
+
});
|
|
10154
|
+
} catch (error) {
|
|
10155
|
+
if (timedOut && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
10156
|
+
const checkpoint = await persistTimeoutCheckpoint({
|
|
10157
|
+
conversationId: timeoutResumeConversationId,
|
|
10158
|
+
sessionId: timeoutResumeSessionId,
|
|
10159
|
+
currentSliceId: timeoutResumeSliceId,
|
|
10160
|
+
messages: timeoutResumeMessages,
|
|
10161
|
+
loadedSkillNames: loadedSkillNamesForResume,
|
|
10162
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
10163
|
+
logContext: {
|
|
10164
|
+
threadId: context.correlation?.threadId,
|
|
10165
|
+
requesterId: context.correlation?.requesterId,
|
|
10166
|
+
channelId: context.correlation?.channelId,
|
|
10167
|
+
runId: context.correlation?.runId,
|
|
10168
|
+
assistantUserName: context.assistant?.userName,
|
|
10169
|
+
modelId: botConfig.modelId
|
|
10170
|
+
}
|
|
10171
|
+
});
|
|
10172
|
+
if (checkpoint) {
|
|
10173
|
+
throw new RetryableTurnError(
|
|
10174
|
+
"turn_timeout_resume",
|
|
10175
|
+
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${checkpoint.sliceId} version=${checkpoint.checkpointVersion}`,
|
|
10176
|
+
{
|
|
10177
|
+
conversationId: timeoutResumeConversationId,
|
|
10178
|
+
sessionId: timeoutResumeSessionId,
|
|
10179
|
+
sliceId: checkpoint.sliceId,
|
|
10180
|
+
checkpointVersion: checkpoint.checkpointVersion
|
|
10181
|
+
}
|
|
10182
|
+
);
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
10185
|
+
if ((error instanceof McpAuthorizationPauseError || error instanceof PluginAuthorizationPauseError) && timeoutResumeConversationId && timeoutResumeSessionId) {
|
|
10186
|
+
const nextSliceId = await persistAuthPauseCheckpoint({
|
|
10187
|
+
conversationId: timeoutResumeConversationId,
|
|
10188
|
+
sessionId: timeoutResumeSessionId,
|
|
10189
|
+
currentSliceId: timeoutResumeSliceId,
|
|
10190
|
+
messages: timeoutResumeMessages,
|
|
10191
|
+
loadedSkillNames: loadedSkillNamesForResume,
|
|
10192
|
+
errorMessage: error.message,
|
|
10193
|
+
logContext: {
|
|
10194
|
+
threadId: context.correlation?.threadId,
|
|
10195
|
+
requesterId: context.correlation?.requesterId,
|
|
10196
|
+
channelId: context.correlation?.channelId,
|
|
10197
|
+
runId: context.correlation?.runId,
|
|
10198
|
+
assistantUserName: context.assistant?.userName,
|
|
10199
|
+
modelId: botConfig.modelId
|
|
10552
10200
|
}
|
|
10553
|
-
}
|
|
10201
|
+
});
|
|
10202
|
+
throw new RetryableTurnError(
|
|
10203
|
+
error instanceof PluginAuthorizationPauseError ? "plugin_auth_resume" : "mcp_auth_resume",
|
|
10204
|
+
`conversation=${timeoutResumeConversationId} session=${timeoutResumeSessionId} slice=${nextSliceId}`,
|
|
10205
|
+
{
|
|
10206
|
+
conversationId: timeoutResumeConversationId,
|
|
10207
|
+
sessionId: timeoutResumeSessionId,
|
|
10208
|
+
sliceId: nextSliceId
|
|
10209
|
+
}
|
|
10210
|
+
);
|
|
10211
|
+
}
|
|
10212
|
+
if (isRetryableTurnError(error)) {
|
|
10213
|
+
throw error;
|
|
10214
|
+
}
|
|
10215
|
+
logException(
|
|
10216
|
+
error,
|
|
10217
|
+
"assistant_reply_generation_failed",
|
|
10554
10218
|
{
|
|
10555
|
-
|
|
10556
|
-
|
|
10557
|
-
|
|
10558
|
-
|
|
10559
|
-
|
|
10560
|
-
|
|
10561
|
-
|
|
10562
|
-
|
|
10563
|
-
|
|
10564
|
-
getActiveSkills: () => activeSkills,
|
|
10565
|
-
mcpToolManager: turnMcpToolManager,
|
|
10566
|
-
sandbox
|
|
10567
|
-
}
|
|
10219
|
+
slackThreadId: context.correlation?.threadId,
|
|
10220
|
+
slackUserId: context.correlation?.requesterId,
|
|
10221
|
+
slackChannelId: context.correlation?.channelId,
|
|
10222
|
+
runId: context.correlation?.runId,
|
|
10223
|
+
assistantUserName: context.assistant?.userName,
|
|
10224
|
+
modelId: botConfig.modelId
|
|
10225
|
+
},
|
|
10226
|
+
{},
|
|
10227
|
+
"generateAssistantReply failed"
|
|
10568
10228
|
);
|
|
10569
|
-
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10229
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10230
|
+
return {
|
|
10231
|
+
text: `Error: ${message}`,
|
|
10232
|
+
...getSandboxMetadata(),
|
|
10233
|
+
diagnostics: {
|
|
10234
|
+
outcome: "provider_error",
|
|
10235
|
+
modelId: botConfig.modelId,
|
|
10236
|
+
assistantMessageCount: 0,
|
|
10237
|
+
toolCalls: [],
|
|
10238
|
+
toolResultCount: 0,
|
|
10239
|
+
toolErrorCount: 0,
|
|
10240
|
+
usedPrimaryText: false,
|
|
10241
|
+
durationMs: Date.now() - replyStartedAtMs,
|
|
10242
|
+
errorMessage: message,
|
|
10243
|
+
providerError: error
|
|
10576
10244
|
}
|
|
10577
|
-
|
|
10245
|
+
};
|
|
10246
|
+
} finally {
|
|
10247
|
+
try {
|
|
10248
|
+
await mcpToolManager?.close();
|
|
10249
|
+
} catch (closeError) {
|
|
10250
|
+
logWarn(
|
|
10251
|
+
"mcp_tool_manager_close_failed",
|
|
10252
|
+
{},
|
|
10253
|
+
{
|
|
10254
|
+
"error.message": closeError instanceof Error ? closeError.message : String(closeError)
|
|
10255
|
+
},
|
|
10256
|
+
"Failed to close MCP tool manager"
|
|
10257
|
+
);
|
|
10578
10258
|
}
|
|
10579
|
-
|
|
10580
|
-
|
|
10581
|
-
|
|
10582
|
-
|
|
10583
|
-
|
|
10584
|
-
|
|
10585
|
-
|
|
10586
|
-
|
|
10587
|
-
|
|
10588
|
-
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10259
|
+
}
|
|
10260
|
+
}
|
|
10261
|
+
|
|
10262
|
+
// src/chat/slack/assistant-thread/status-render.ts
|
|
10263
|
+
var DEFAULT_STATUS_CONTEXTS = {
|
|
10264
|
+
thinking: "\u2026",
|
|
10265
|
+
searching: "sources",
|
|
10266
|
+
reading: "task",
|
|
10267
|
+
reviewing: "results",
|
|
10268
|
+
drafting: "reply",
|
|
10269
|
+
running: "tasks"
|
|
10270
|
+
};
|
|
10271
|
+
function formatAssistantStatusText(verb, context) {
|
|
10272
|
+
const normalizedVerb = normalizeSlackStatusText(verb).trim().toLowerCase();
|
|
10273
|
+
const normalizedContext = normalizeSlackStatusText(context ?? "") || DEFAULT_STATUS_CONTEXTS[normalizedVerb] || "";
|
|
10274
|
+
if (!normalizedVerb) {
|
|
10275
|
+
return truncateStatusText(normalizedContext || "Working");
|
|
10276
|
+
}
|
|
10277
|
+
const displayVerb = `${normalizedVerb[0]?.toUpperCase() ?? ""}${normalizedVerb.slice(1)}`;
|
|
10278
|
+
return truncateStatusText(
|
|
10279
|
+
normalizedContext ? `${displayVerb} ${normalizedContext}` : displayVerb
|
|
10280
|
+
);
|
|
10281
|
+
}
|
|
10282
|
+
function makeAssistantStatus(verb, context) {
|
|
10283
|
+
return {
|
|
10284
|
+
text: formatAssistantStatusText(verb, context)
|
|
10285
|
+
};
|
|
10286
|
+
}
|
|
10287
|
+
function renderAssistantStatus(args) {
|
|
10288
|
+
const visible = truncateStatusText(
|
|
10289
|
+
normalizeSlackStatusText(args.status.text)
|
|
10290
|
+
);
|
|
10291
|
+
return {
|
|
10292
|
+
key: visible,
|
|
10293
|
+
visible
|
|
10294
|
+
};
|
|
10295
|
+
}
|
|
10296
|
+
function selectAssistantLoadingMessages(args) {
|
|
10297
|
+
const random = args.random ?? Math.random;
|
|
10298
|
+
const normalized = Array.from(
|
|
10299
|
+
new Set(
|
|
10300
|
+
args.messages.map((message) => truncateStatusText(normalizeSlackStatusText(message))).filter((message) => message.length > 0)
|
|
10301
|
+
)
|
|
10302
|
+
);
|
|
10303
|
+
if (normalized.length === 0) {
|
|
10304
|
+
return void 0;
|
|
10305
|
+
}
|
|
10306
|
+
const shuffled = [...normalized];
|
|
10307
|
+
for (let index = shuffled.length - 1; index > 0; index -= 1) {
|
|
10308
|
+
const otherIndex = Math.floor(random() * (index + 1));
|
|
10309
|
+
[shuffled[index], shuffled[otherIndex]] = [
|
|
10310
|
+
shuffled[otherIndex],
|
|
10311
|
+
shuffled[index]
|
|
10312
|
+
];
|
|
10313
|
+
}
|
|
10314
|
+
return shuffled.slice(0, 10);
|
|
10315
|
+
}
|
|
10316
|
+
|
|
10317
|
+
// src/chat/slack/assistant-thread/status-scheduler.ts
|
|
10318
|
+
var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
|
|
10319
|
+
var STATUS_MIN_VISIBLE_MS = 1200;
|
|
10320
|
+
var STATUS_ROTATION_INTERVAL_MS = 3e4;
|
|
10321
|
+
function createAssistantStatusScheduler(args) {
|
|
10322
|
+
const now = args.now ?? (() => Date.now());
|
|
10323
|
+
const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
|
|
10324
|
+
const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
|
|
10325
|
+
const random = args.random ?? Math.random;
|
|
10326
|
+
const loadingMessages = selectAssistantLoadingMessages({
|
|
10327
|
+
messages: args.loadingMessages ?? [],
|
|
10328
|
+
random
|
|
10329
|
+
});
|
|
10330
|
+
const defaultStatus = makeAssistantStatus("thinking");
|
|
10331
|
+
let active = false;
|
|
10332
|
+
let currentKey = "";
|
|
10333
|
+
let currentVisibleStatus = "";
|
|
10334
|
+
let currentLoadingMessages;
|
|
10335
|
+
let lastStatusAt = 0;
|
|
10336
|
+
let pendingStatus = null;
|
|
10337
|
+
let pendingKey = "";
|
|
10338
|
+
let pendingTimer = null;
|
|
10339
|
+
let rotationTimer = null;
|
|
10340
|
+
let inflightStatusUpdate = Promise.resolve();
|
|
10341
|
+
const enqueueStatusUpdate = (task) => {
|
|
10342
|
+
const request = inflightStatusUpdate.catch(() => void 0).then(async () => {
|
|
10343
|
+
await task();
|
|
10596
10344
|
});
|
|
10597
|
-
|
|
10598
|
-
|
|
10599
|
-
|
|
10600
|
-
|
|
10601
|
-
|
|
10602
|
-
|
|
10603
|
-
|
|
10345
|
+
inflightStatusUpdate = request.catch(() => void 0);
|
|
10346
|
+
return request;
|
|
10347
|
+
};
|
|
10348
|
+
const scheduleRotation = () => {
|
|
10349
|
+
if (rotationTimer) {
|
|
10350
|
+
clearTimer(rotationTimer);
|
|
10351
|
+
rotationTimer = null;
|
|
10604
10352
|
}
|
|
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
|
-
});
|
|
10353
|
+
if (!active || !currentVisibleStatus) {
|
|
10354
|
+
return;
|
|
10355
|
+
}
|
|
10356
|
+
rotationTimer = setTimer(() => {
|
|
10357
|
+
rotationTimer = null;
|
|
10358
|
+
if (!active || !currentVisibleStatus) {
|
|
10359
|
+
return;
|
|
10633
10360
|
}
|
|
10361
|
+
void postStatus(currentVisibleStatus, currentLoadingMessages);
|
|
10362
|
+
}, STATUS_ROTATION_INTERVAL_MS);
|
|
10363
|
+
};
|
|
10364
|
+
const getLoadingMessagesForVisibleStatus = (visible) => visible ? [visible] : void 0;
|
|
10365
|
+
const getInitialStatusText = () => {
|
|
10366
|
+
if (loadingMessages?.length) {
|
|
10367
|
+
return loadingMessages[0];
|
|
10368
|
+
}
|
|
10369
|
+
return defaultStatus.text;
|
|
10370
|
+
};
|
|
10371
|
+
const haveSameLoadingMessages = (left, right) => {
|
|
10372
|
+
if (left === right) {
|
|
10373
|
+
return true;
|
|
10374
|
+
}
|
|
10375
|
+
if (!left || !right || left.length !== right.length) {
|
|
10376
|
+
return false;
|
|
10377
|
+
}
|
|
10378
|
+
return left.every((message, index) => message === right[index]);
|
|
10379
|
+
};
|
|
10380
|
+
const postStatus = async (text, nextLoadingMessages) => {
|
|
10381
|
+
if (!text && !currentVisibleStatus) {
|
|
10382
|
+
return;
|
|
10383
|
+
}
|
|
10384
|
+
currentVisibleStatus = text;
|
|
10385
|
+
currentLoadingMessages = nextLoadingMessages;
|
|
10386
|
+
lastStatusAt = now();
|
|
10387
|
+
scheduleRotation();
|
|
10388
|
+
await enqueueStatusUpdate(async () => {
|
|
10389
|
+
await args.sendStatus(text, nextLoadingMessages);
|
|
10390
|
+
});
|
|
10391
|
+
};
|
|
10392
|
+
const postRenderedStatus = async (status) => {
|
|
10393
|
+
const presentation = renderAssistantStatus({
|
|
10394
|
+
status
|
|
10395
|
+
});
|
|
10396
|
+
const nextLoadingMessages = getLoadingMessagesForVisibleStatus(
|
|
10397
|
+
presentation.visible
|
|
10398
|
+
);
|
|
10399
|
+
currentKey = presentation.key;
|
|
10400
|
+
await postStatus(presentation.visible, nextLoadingMessages);
|
|
10401
|
+
};
|
|
10402
|
+
const clearPending = () => {
|
|
10403
|
+
if (pendingTimer) {
|
|
10404
|
+
clearTimer(pendingTimer);
|
|
10405
|
+
pendingTimer = null;
|
|
10634
10406
|
}
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10407
|
+
pendingStatus = null;
|
|
10408
|
+
pendingKey = "";
|
|
10409
|
+
};
|
|
10410
|
+
const flushPending = async () => {
|
|
10411
|
+
if (!active || !pendingStatus) {
|
|
10412
|
+
clearPending();
|
|
10413
|
+
return;
|
|
10414
|
+
}
|
|
10415
|
+
const next = pendingStatus;
|
|
10416
|
+
clearPending();
|
|
10417
|
+
const nextPresentation = renderAssistantStatus({
|
|
10418
|
+
status: next
|
|
10419
|
+
});
|
|
10420
|
+
if (nextPresentation.key !== currentKey) {
|
|
10421
|
+
await postRenderedStatus(next);
|
|
10422
|
+
}
|
|
10423
|
+
};
|
|
10424
|
+
return {
|
|
10425
|
+
start() {
|
|
10426
|
+
active = true;
|
|
10427
|
+
clearPending();
|
|
10428
|
+
currentKey = "initial";
|
|
10429
|
+
void postStatus(getInitialStatusText(), loadingMessages);
|
|
10430
|
+
},
|
|
10431
|
+
async stop() {
|
|
10432
|
+
active = false;
|
|
10433
|
+
clearPending();
|
|
10434
|
+
if (rotationTimer) {
|
|
10435
|
+
clearTimer(rotationTimer);
|
|
10436
|
+
rotationTimer = null;
|
|
10643
10437
|
}
|
|
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
|
-
});
|
|
10438
|
+
currentKey = "";
|
|
10439
|
+
await postStatus("");
|
|
10440
|
+
},
|
|
10441
|
+
update(status) {
|
|
10442
|
+
if (!active) {
|
|
10443
|
+
return;
|
|
10659
10444
|
}
|
|
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
|
|
10445
|
+
const presentation = renderAssistantStatus({
|
|
10446
|
+
status
|
|
10447
|
+
});
|
|
10448
|
+
if (!presentation.visible) {
|
|
10449
|
+
return;
|
|
10695
10450
|
}
|
|
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
|
-
}
|
|
10451
|
+
if (presentation.key === currentKey || presentation.key === pendingKey) {
|
|
10714
10452
|
return;
|
|
10715
10453
|
}
|
|
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"
|
|
10454
|
+
if (presentation.visible === currentVisibleStatus) {
|
|
10455
|
+
clearPending();
|
|
10456
|
+
currentKey = presentation.key;
|
|
10457
|
+
const nextLoadingMessages = getLoadingMessagesForVisibleStatus(
|
|
10458
|
+
presentation.visible
|
|
10731
10459
|
);
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
|
|
10736
|
-
let completedAssistantTurn = false;
|
|
10737
|
-
try {
|
|
10738
|
-
if (resumedFromCheckpoint) {
|
|
10739
|
-
agent.replaceMessages(existingCheckpoint.piMessages);
|
|
10460
|
+
if (!haveSameLoadingMessages(currentLoadingMessages, nextLoadingMessages)) {
|
|
10461
|
+
void postStatus(presentation.visible, nextLoadingMessages);
|
|
10462
|
+
}
|
|
10463
|
+
return;
|
|
10740
10464
|
}
|
|
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
|
-
});
|
|
10465
|
+
const elapsed = now() - lastStatusAt;
|
|
10466
|
+
const waitMs = Math.max(
|
|
10467
|
+
STATUS_UPDATE_DEBOUNCE_MS - elapsed,
|
|
10468
|
+
STATUS_MIN_VISIBLE_MS - elapsed,
|
|
10469
|
+
0
|
|
10470
|
+
);
|
|
10471
|
+
if (waitMs <= 0) {
|
|
10472
|
+
clearPending();
|
|
10473
|
+
void postRenderedStatus(status);
|
|
10474
|
+
return;
|
|
10475
|
+
}
|
|
10476
|
+
pendingStatus = status;
|
|
10477
|
+
pendingKey = presentation.key;
|
|
10478
|
+
if (pendingTimer) {
|
|
10479
|
+
return;
|
|
10480
|
+
}
|
|
10481
|
+
pendingTimer = setTimer(
|
|
10482
|
+
() => {
|
|
10483
|
+
pendingTimer = null;
|
|
10484
|
+
void flushPending();
|
|
10820
10485
|
},
|
|
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
|
-
}
|
|
10486
|
+
Math.max(1, waitMs)
|
|
10827
10487
|
);
|
|
10828
|
-
} finally {
|
|
10829
|
-
unsubscribe();
|
|
10830
10488
|
}
|
|
10831
|
-
|
|
10832
|
-
|
|
10489
|
+
};
|
|
10490
|
+
}
|
|
10491
|
+
|
|
10492
|
+
// src/chat/slack/assistant-thread/status-send.ts
|
|
10493
|
+
var SLACK_ASSISTANT_ACTIVE_STATUS = "is working on your request...";
|
|
10494
|
+
function createSlackAdapterStatusSender(args) {
|
|
10495
|
+
const adapter = args.getSlackAdapter();
|
|
10496
|
+
const boundToken = getSlackAdapterRequestToken(adapter);
|
|
10497
|
+
return async (text, loadingMessages) => {
|
|
10498
|
+
const channelId = args.channelId;
|
|
10499
|
+
const threadTs = args.threadTs;
|
|
10500
|
+
if (!channelId || !threadTs) {
|
|
10501
|
+
return;
|
|
10833
10502
|
}
|
|
10834
|
-
|
|
10835
|
-
|
|
10836
|
-
|
|
10837
|
-
sessionId,
|
|
10838
|
-
sliceId: currentSliceId,
|
|
10839
|
-
allMessages: agent.state.messages,
|
|
10840
|
-
loadedSkillNames: activeSkills.map((skill) => skill.name)
|
|
10841
|
-
});
|
|
10503
|
+
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
10504
|
+
if (!normalizedChannelId) {
|
|
10505
|
+
return;
|
|
10842
10506
|
}
|
|
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
|
-
}
|
|
10507
|
+
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
10508
|
+
try {
|
|
10509
|
+
await runWithBoundSlackToken(
|
|
10510
|
+
adapter,
|
|
10511
|
+
boundToken,
|
|
10512
|
+
() => adapter.setAssistantStatus(
|
|
10513
|
+
normalizedChannelId,
|
|
10514
|
+
threadTs,
|
|
10515
|
+
text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
10516
|
+
nextLoadingMessages
|
|
10517
|
+
)
|
|
10518
|
+
);
|
|
10519
|
+
} catch (error) {
|
|
10520
|
+
logAssistantStatusFailure({
|
|
10521
|
+
status: text,
|
|
10522
|
+
error,
|
|
10523
|
+
channelId,
|
|
10524
|
+
normalizedChannelId,
|
|
10525
|
+
threadTs
|
|
10876
10526
|
});
|
|
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
10527
|
}
|
|
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
|
-
);
|
|
10528
|
+
};
|
|
10529
|
+
}
|
|
10530
|
+
function createSlackWebApiStatusSender(args) {
|
|
10531
|
+
const getClient2 = args.getSlackClient ?? getSlackClient;
|
|
10532
|
+
return async (text, loadingMessages) => {
|
|
10533
|
+
const channelId = args.channelId;
|
|
10534
|
+
const threadTs = args.threadTs;
|
|
10535
|
+
if (!channelId || !threadTs) {
|
|
10536
|
+
return;
|
|
10916
10537
|
}
|
|
10917
|
-
|
|
10918
|
-
|
|
10538
|
+
const normalizedChannelId = normalizeSlackConversationId(channelId);
|
|
10539
|
+
if (!normalizedChannelId) {
|
|
10540
|
+
return;
|
|
10919
10541
|
}
|
|
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 {
|
|
10542
|
+
const nextLoadingMessages = text ? loadingMessages ?? [text] : void 0;
|
|
10952
10543
|
try {
|
|
10953
|
-
await
|
|
10954
|
-
|
|
10955
|
-
|
|
10956
|
-
"
|
|
10957
|
-
{}
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
|
|
10544
|
+
await getClient2().assistant.threads.setStatus({
|
|
10545
|
+
channel_id: normalizedChannelId,
|
|
10546
|
+
thread_ts: threadTs,
|
|
10547
|
+
status: text ? SLACK_ASSISTANT_ACTIVE_STATUS : "",
|
|
10548
|
+
...nextLoadingMessages ? { loading_messages: nextLoadingMessages } : {}
|
|
10549
|
+
});
|
|
10550
|
+
} catch (error) {
|
|
10551
|
+
logAssistantStatusFailure({
|
|
10552
|
+
status: text,
|
|
10553
|
+
error,
|
|
10554
|
+
channelId,
|
|
10555
|
+
normalizedChannelId,
|
|
10556
|
+
threadTs
|
|
10557
|
+
});
|
|
10963
10558
|
}
|
|
10559
|
+
};
|
|
10560
|
+
}
|
|
10561
|
+
function getSlackAdapterRequestToken(adapter) {
|
|
10562
|
+
const token = adapter.requestContext?.getStore()?.token;
|
|
10563
|
+
if (typeof token !== "string") {
|
|
10564
|
+
return void 0;
|
|
10565
|
+
}
|
|
10566
|
+
const trimmed = token.trim();
|
|
10567
|
+
return trimmed || void 0;
|
|
10568
|
+
}
|
|
10569
|
+
async function runWithBoundSlackToken(adapter, token, task) {
|
|
10570
|
+
if (!token) {
|
|
10571
|
+
return await task();
|
|
10964
10572
|
}
|
|
10573
|
+
return await adapter.withBotToken(token, task);
|
|
10574
|
+
}
|
|
10575
|
+
function logAssistantStatusFailure(args) {
|
|
10576
|
+
logWarn(
|
|
10577
|
+
"assistant_status_update_failed",
|
|
10578
|
+
{},
|
|
10579
|
+
{
|
|
10580
|
+
"app.slack.status_text": args.status || "(clear)",
|
|
10581
|
+
"app.slack.channel_id_raw": args.channelId,
|
|
10582
|
+
"app.slack.channel_id": args.normalizedChannelId,
|
|
10583
|
+
"app.slack.thread_ts": args.threadTs,
|
|
10584
|
+
"error.message": args.error instanceof Error ? args.error.message : String(args.error)
|
|
10585
|
+
},
|
|
10586
|
+
`Failed to update assistant status channel=${args.normalizedChannelId} raw=${args.channelId} thread=${args.threadTs}`
|
|
10587
|
+
);
|
|
10588
|
+
}
|
|
10589
|
+
|
|
10590
|
+
// src/chat/slack/assistant-thread/status.ts
|
|
10591
|
+
function createSlackAdapterAssistantStatusSession(args) {
|
|
10592
|
+
return createAssistantStatusScheduler({
|
|
10593
|
+
sendStatus: createSlackAdapterStatusSender({
|
|
10594
|
+
channelId: args.channelId,
|
|
10595
|
+
threadTs: args.threadTs,
|
|
10596
|
+
getSlackAdapter: args.getSlackAdapter
|
|
10597
|
+
}),
|
|
10598
|
+
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
10599
|
+
now: args.now,
|
|
10600
|
+
setTimer: args.setTimer,
|
|
10601
|
+
clearTimer: args.clearTimer,
|
|
10602
|
+
random: args.random
|
|
10603
|
+
});
|
|
10604
|
+
}
|
|
10605
|
+
function createSlackWebApiAssistantStatusSession(args) {
|
|
10606
|
+
return createAssistantStatusScheduler({
|
|
10607
|
+
sendStatus: createSlackWebApiStatusSender({
|
|
10608
|
+
channelId: args.channelId,
|
|
10609
|
+
threadTs: args.threadTs,
|
|
10610
|
+
getSlackClient: args.getSlackClient
|
|
10611
|
+
}),
|
|
10612
|
+
loadingMessages: args.loadingMessages ?? botConfig.loadingMessages,
|
|
10613
|
+
now: args.now,
|
|
10614
|
+
setTimer: args.setTimer,
|
|
10615
|
+
clearTimer: args.clearTimer,
|
|
10616
|
+
random: args.random
|
|
10617
|
+
});
|
|
10965
10618
|
}
|
|
10966
10619
|
|
|
10967
10620
|
// src/chat/slack/footer.ts
|