@sentry/junior 0.27.0 → 0.27.1

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