@sentry/junior 0.18.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/app.js +578 -503
  2. package/package.json +1 -1
package/dist/app.js CHANGED
@@ -1260,7 +1260,7 @@ function hasCompletedAssistantTurn(messages) {
1260
1260
  continue;
1261
1261
  }
1262
1262
  const stopReason = message.stopReason;
1263
- return typeof stopReason === "string" && stopReason !== "error";
1263
+ return typeof stopReason === "string" && stopReason !== "error" && extractAssistantText(message).trim().length > 0;
1264
1264
  }
1265
1265
  return false;
1266
1266
  }
@@ -2999,6 +2999,8 @@ function buildSystemPrompt(params) {
2999
2999
  "- If a loaded skill or `loadSkill` result declares `requires_capabilities`, run `jr-rpc issue-credential <capability> [--repo <owner/repo>]` as a bash command before authenticated bash/API work for that skill.",
3000
3000
  "- Use the minimum declared capability needed for the current operation.",
3001
3001
  "- If `jr-rpc issue-credential` returns `oauth_started`, relay its `message` to the user and stop. The runtime will resume after authorization.",
3002
+ "- For disconnect + reconnect requests, run `jr-rpc delete-token <provider>` first, then `jr-rpc issue-credential` \u2014 the system handles the reconnect without auto-resuming the reconnect message.",
3003
+ "- Use `jr-rpc oauth-start <provider>` only when the user explicitly asks to connect a provider and there is no task to resume after authorization.",
3002
3004
  "- GitHub capabilities need repository context, which can come from `--repo` or a configured `github.repo` default.",
3003
3005
  "- To persist or read conversation defaults (for example `github.repo`), run `jr-rpc config get|set|unset|list ...` as a bash command.",
3004
3006
  "- Capabilities are provider-qualified (for example `github.issues.write`).",
@@ -3539,6 +3541,52 @@ async function handleIssueCredentialCommand(args, deps) {
3539
3541
  });
3540
3542
  } catch (error) {
3541
3543
  if (error instanceof CredentialUnavailableError && getPluginOAuthConfig(error.provider) && deps.requesterId) {
3544
+ const authAction = deps.providerAuthActions?.get(error.provider);
3545
+ if (authAction?.kind === "oauth_started") {
3546
+ const providerLabel = formatProviderLabel(error.provider);
3547
+ return commandResult({
3548
+ stdout: {
3549
+ credential_unavailable: true,
3550
+ oauth_started: true,
3551
+ provider: error.provider,
3552
+ private_delivery_sent: authAction.delivered,
3553
+ message: authAction.delivered ? `I've already sent you a private authorization link to connect your ${providerLabel} account. Finish that flow, then return to Slack.` : `I still need to connect your ${providerLabel} account, but I wasn't able to send you a private authorization link. Please send me a direct message and try again.`
3554
+ },
3555
+ exitCode: 0
3556
+ });
3557
+ }
3558
+ if (authAction?.kind === "token_deleted") {
3559
+ const reconnectResult = await startOAuthFlow(error.provider, {
3560
+ requesterId: deps.requesterId,
3561
+ channelId: deps.channelId,
3562
+ threadTs: deps.threadTs,
3563
+ activeSkillName: deps.activeSkill?.name ?? void 0
3564
+ // Intentionally no userMessage — reconnect flows must not auto-resume.
3565
+ });
3566
+ if (!reconnectResult.ok) {
3567
+ return commandResult({
3568
+ stderr: `${reconnectResult.error}
3569
+ `,
3570
+ exitCode: 1
3571
+ });
3572
+ }
3573
+ const delivered = !!reconnectResult.delivery;
3574
+ deps.providerAuthActions?.set(error.provider, {
3575
+ kind: "oauth_started",
3576
+ delivered
3577
+ });
3578
+ const providerLabel = formatProviderLabel(error.provider);
3579
+ return commandResult({
3580
+ stdout: {
3581
+ credential_unavailable: true,
3582
+ oauth_started: true,
3583
+ provider: error.provider,
3584
+ private_delivery_sent: delivered,
3585
+ message: delivered ? `I need to connect your ${providerLabel} account first. I've sent you a private authorization link.` : `I need to connect your ${providerLabel} account first, but I wasn't able to send you a private authorization link. Please send me a direct message and try your command again.`
3586
+ },
3587
+ exitCode: 0
3588
+ });
3589
+ }
3542
3590
  const oauthResult = await startOAuthFlow(error.provider, {
3543
3591
  requesterId: deps.requesterId,
3544
3592
  channelId: deps.channelId,
@@ -3808,6 +3856,10 @@ async function handleOAuthStartCommand(args, deps) {
3808
3856
  return commandResult({ stderr: `${result.error}
3809
3857
  `, exitCode: 1 });
3810
3858
  }
3859
+ deps.providerAuthActions?.set(provider, {
3860
+ kind: "oauth_started",
3861
+ delivered: !!result.delivery
3862
+ });
3811
3863
  if (!result.delivery) {
3812
3864
  return commandResult({
3813
3865
  stdout: {
@@ -3854,6 +3906,7 @@ async function handleDeleteTokenCommand(args, deps) {
3854
3906
  });
3855
3907
  }
3856
3908
  await unlinkProvider(deps.requesterId, provider, deps.userTokenStore);
3909
+ deps.providerAuthActions?.set(provider, { kind: "token_deleted" });
3857
3910
  logInfo(
3858
3911
  "jr_rpc_delete_token",
3859
3912
  {},
@@ -6754,103 +6807,50 @@ function createToolState(hooks, context) {
6754
6807
  }
6755
6808
  };
6756
6809
  }
6757
- function wrapToolExecution(toolName, toolDef, hooks) {
6758
- const maybeExecutable = toolDef;
6759
- if (!maybeExecutable.execute) {
6760
- return toolDef;
6761
- }
6762
- const originalExecute = maybeExecutable.execute.bind(toolDef);
6763
- maybeExecutable.execute = async (...args) => {
6764
- const input = args[0];
6765
- await hooks.onToolCallStart?.(toolName, input);
6766
- return originalExecute(...args);
6767
- };
6768
- return toolDef;
6769
- }
6770
6810
  function createTools(availableSkills, hooks = {}, context) {
6771
6811
  const state = createToolState(hooks, context);
6772
6812
  const tools = {
6773
- loadSkill: wrapToolExecution(
6774
- "loadSkill",
6775
- createLoadSkillTool(context.sandbox, availableSkills, {
6776
- onSkillLoaded: hooks.onSkillLoaded
6777
- }),
6778
- hooks
6779
- ),
6780
- systemTime: wrapToolExecution("systemTime", createSystemTimeTool(), hooks),
6781
- bash: wrapToolExecution("bash", createBashTool(), hooks),
6782
- attachFile: wrapToolExecution(
6783
- "attachFile",
6784
- createAttachFileTool(context.sandbox, hooks),
6785
- hooks
6786
- ),
6787
- readFile: wrapToolExecution("readFile", createReadFileTool(), hooks),
6788
- writeFile: wrapToolExecution("writeFile", createWriteFileTool(), hooks),
6789
- webSearch: wrapToolExecution("webSearch", createWebSearchTool(), hooks),
6790
- webFetch: wrapToolExecution("webFetch", createWebFetchTool(hooks), hooks),
6791
- imageGenerate: wrapToolExecution(
6792
- "imageGenerate",
6793
- createImageGenerateTool(hooks, hooks.toolOverrides?.imageGenerate),
6794
- hooks
6795
- ),
6796
- slackCanvasUpdate: wrapToolExecution(
6797
- "slackCanvasUpdate",
6798
- createSlackCanvasUpdateTool(state, context),
6799
- hooks
6800
- ),
6801
- slackListCreate: wrapToolExecution(
6802
- "slackListCreate",
6803
- createSlackListCreateTool(state),
6804
- hooks
6805
- ),
6806
- slackListAddItems: wrapToolExecution(
6807
- "slackListAddItems",
6808
- createSlackListAddItemsTool(state),
6809
- hooks
6810
- ),
6811
- slackListGetItems: wrapToolExecution(
6812
- "slackListGetItems",
6813
- createSlackListGetItemsTool(state),
6814
- hooks
6813
+ loadSkill: createLoadSkillTool(context.sandbox, availableSkills, {
6814
+ onSkillLoaded: hooks.onSkillLoaded
6815
+ }),
6816
+ systemTime: createSystemTimeTool(),
6817
+ bash: createBashTool(),
6818
+ attachFile: createAttachFileTool(context.sandbox, hooks),
6819
+ readFile: createReadFileTool(),
6820
+ writeFile: createWriteFileTool(),
6821
+ webSearch: createWebSearchTool(),
6822
+ webFetch: createWebFetchTool(hooks),
6823
+ imageGenerate: createImageGenerateTool(
6824
+ hooks,
6825
+ hooks.toolOverrides?.imageGenerate
6815
6826
  ),
6816
- slackListUpdateItem: wrapToolExecution(
6817
- "slackListUpdateItem",
6818
- createSlackListUpdateItemTool(state),
6819
- hooks
6820
- )
6827
+ slackCanvasUpdate: createSlackCanvasUpdateTool(state, context),
6828
+ slackListCreate: createSlackListCreateTool(state),
6829
+ slackListAddItems: createSlackListAddItemsTool(state),
6830
+ slackListGetItems: createSlackListGetItemsTool(state),
6831
+ slackListUpdateItem: createSlackListUpdateItemTool(state)
6821
6832
  };
6822
6833
  if (context.mcpToolManager && context.getActiveSkills) {
6823
- tools.searchTools = wrapToolExecution(
6824
- "searchTools",
6825
- createSearchToolsTool(context.mcpToolManager, context.getActiveSkills),
6826
- hooks
6834
+ tools.searchTools = createSearchToolsTool(
6835
+ context.mcpToolManager,
6836
+ context.getActiveSkills
6827
6837
  );
6828
6838
  }
6829
6839
  const { channelCapabilities } = context;
6830
6840
  if (channelCapabilities.canCreateCanvas) {
6831
- tools.slackCanvasCreate = wrapToolExecution(
6832
- "slackCanvasCreate",
6833
- createSlackCanvasCreateTool(context, state),
6834
- hooks
6835
- );
6841
+ tools.slackCanvasCreate = createSlackCanvasCreateTool(context, state);
6836
6842
  }
6837
6843
  if (channelCapabilities.canPostToChannel) {
6838
- tools.slackChannelPostMessage = wrapToolExecution(
6839
- "slackChannelPostMessage",
6840
- createSlackChannelPostMessageTool(context, state),
6841
- hooks
6842
- );
6843
- tools.slackChannelListMessages = wrapToolExecution(
6844
- "slackChannelListMessages",
6845
- createSlackChannelListMessagesTool(context),
6846
- hooks
6844
+ tools.slackChannelPostMessage = createSlackChannelPostMessageTool(
6845
+ context,
6846
+ state
6847
6847
  );
6848
+ tools.slackChannelListMessages = createSlackChannelListMessagesTool(context);
6848
6849
  }
6849
6850
  if (channelCapabilities.canAddReactions) {
6850
- tools.slackMessageAddReaction = wrapToolExecution(
6851
- "slackMessageAddReaction",
6852
- createSlackMessageAddReactionTool(context, state),
6853
- hooks
6851
+ tools.slackMessageAddReaction = createSlackMessageAddReactionTool(
6852
+ context,
6853
+ state
6854
6854
  );
6855
6855
  }
6856
6856
  return tools;
@@ -6973,6 +6973,267 @@ function extractHttpErrorDetails(error, options = {}) {
6973
6973
  };
6974
6974
  }
6975
6975
 
6976
+ // src/chat/runtime/status-format.ts
6977
+ var SLACK_STATUS_MAX_LENGTH = 50;
6978
+ function truncateWithEllipsis(text, maxLength) {
6979
+ if (text.length <= maxLength) {
6980
+ return text;
6981
+ }
6982
+ return `${text.slice(0, Math.max(1, maxLength - 3)).trimEnd()}...`;
6983
+ }
6984
+ function truncateStatusText(text) {
6985
+ const trimmed = text.trim();
6986
+ if (!trimmed) {
6987
+ return "";
6988
+ }
6989
+ return truncateWithEllipsis(trimmed, SLACK_STATUS_MAX_LENGTH);
6990
+ }
6991
+ function compactStatusPath(value) {
6992
+ if (typeof value !== "string") {
6993
+ return void 0;
6994
+ }
6995
+ const trimmed = value.trim();
6996
+ if (!trimmed) {
6997
+ return void 0;
6998
+ }
6999
+ if (trimmed.length <= 80) {
7000
+ return trimmed;
7001
+ }
7002
+ return `...${trimmed.slice(-77)}`;
7003
+ }
7004
+ function compactStatusText(value, maxLength = 80) {
7005
+ if (typeof value !== "string") {
7006
+ return void 0;
7007
+ }
7008
+ const trimmed = value.trim();
7009
+ if (!trimmed) {
7010
+ return void 0;
7011
+ }
7012
+ return truncateWithEllipsis(trimmed, maxLength);
7013
+ }
7014
+ function readShellToken(command, startIndex) {
7015
+ let index = startIndex;
7016
+ while (index < command.length && /\s/.test(command[index] ?? "")) {
7017
+ index += 1;
7018
+ }
7019
+ if (index >= command.length) {
7020
+ return void 0;
7021
+ }
7022
+ let token = "";
7023
+ let quote;
7024
+ while (index < command.length) {
7025
+ const char = command[index];
7026
+ if (!char) {
7027
+ break;
7028
+ }
7029
+ if (quote) {
7030
+ if (char === quote) {
7031
+ quote = void 0;
7032
+ index += 1;
7033
+ continue;
7034
+ }
7035
+ if (char === "\\" && quote === '"' && index + 1 < command.length) {
7036
+ token += command[index + 1];
7037
+ index += 2;
7038
+ continue;
7039
+ }
7040
+ token += char;
7041
+ index += 1;
7042
+ continue;
7043
+ }
7044
+ if (/\s/.test(char)) {
7045
+ break;
7046
+ }
7047
+ if (char === '"' || char === "'") {
7048
+ quote = char;
7049
+ index += 1;
7050
+ continue;
7051
+ }
7052
+ if (char === "\\" && index + 1 < command.length) {
7053
+ token += command[index + 1];
7054
+ index += 2;
7055
+ continue;
7056
+ }
7057
+ token += char;
7058
+ index += 1;
7059
+ }
7060
+ return { token, nextIndex: index };
7061
+ }
7062
+ function compactStatusCommand(value) {
7063
+ if (typeof value !== "string") {
7064
+ return void 0;
7065
+ }
7066
+ const trimmed = value.trim();
7067
+ if (!trimmed) {
7068
+ return void 0;
7069
+ }
7070
+ let index = 0;
7071
+ while (index < trimmed.length) {
7072
+ const parsed = readShellToken(trimmed, index);
7073
+ if (!parsed) {
7074
+ return void 0;
7075
+ }
7076
+ index = parsed.nextIndex;
7077
+ if (!parsed.token) {
7078
+ continue;
7079
+ }
7080
+ if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(parsed.token)) {
7081
+ continue;
7082
+ }
7083
+ const normalized = parsed.token.replace(/[\\/]+$/g, "");
7084
+ if (!normalized) {
7085
+ return void 0;
7086
+ }
7087
+ const parts = normalized.split(/[\\/]/).filter((part) => part.length > 0);
7088
+ const command = parts.length > 0 ? parts[parts.length - 1] : normalized;
7089
+ return compactStatusText(command, 40);
7090
+ }
7091
+ return void 0;
7092
+ }
7093
+ function compactStatusFilename(value) {
7094
+ if (typeof value !== "string") {
7095
+ return void 0;
7096
+ }
7097
+ const trimmed = value.trim().replace(/[\\/]+$/g, "");
7098
+ if (!trimmed) {
7099
+ return void 0;
7100
+ }
7101
+ const parts = trimmed.split(/[\\/]/).filter((part) => part.length > 0);
7102
+ const filename = parts.length > 0 ? parts[parts.length - 1] : trimmed;
7103
+ return compactStatusText(filename, 80);
7104
+ }
7105
+ function extractStatusUrlDomain(value) {
7106
+ if (typeof value !== "string") {
7107
+ return void 0;
7108
+ }
7109
+ const trimmed = value.trim();
7110
+ if (!trimmed) {
7111
+ return void 0;
7112
+ }
7113
+ try {
7114
+ const parsed = new URL(trimmed);
7115
+ return parsed.hostname || void 0;
7116
+ } catch {
7117
+ return void 0;
7118
+ }
7119
+ }
7120
+
7121
+ // src/chat/runtime/assistant-status.ts
7122
+ var STATUS_PATTERNS = {
7123
+ thinking: {
7124
+ defaultContext: "\u2026",
7125
+ variants: ["Thinking", "Reasoning", "Considering", "Working through"]
7126
+ },
7127
+ searching: {
7128
+ defaultContext: "sources",
7129
+ variants: ["Searching", "Scanning", "Probing", "Trawling"]
7130
+ },
7131
+ reading: {
7132
+ defaultContext: "task",
7133
+ variants: ["Reading", "Inspecting", "Parsing", "Skimming"]
7134
+ },
7135
+ reviewing: {
7136
+ defaultContext: "results",
7137
+ variants: ["Reviewing", "Checking", "Inspecting", "Auditing"]
7138
+ },
7139
+ loading: {
7140
+ defaultContext: "task",
7141
+ variants: ["Loading", "Priming", "Booting", "Spinning up"]
7142
+ },
7143
+ updating: {
7144
+ defaultContext: "state",
7145
+ variants: ["Updating", "Patching", "Refreshing", "Adjusting"]
7146
+ },
7147
+ fetching: {
7148
+ defaultContext: "sources",
7149
+ variants: ["Fetching", "Pulling", "Retrieving", "Loading"]
7150
+ },
7151
+ creating: {
7152
+ defaultContext: "draft",
7153
+ variants: ["Creating", "Building", "Assembling", "Generating"]
7154
+ },
7155
+ listing: {
7156
+ defaultContext: "items",
7157
+ variants: ["Listing", "Gathering", "Collecting", "Enumerating"]
7158
+ },
7159
+ posting: {
7160
+ defaultContext: "reply",
7161
+ variants: ["Posting", "Sending", "Delivering", "Dispatching"]
7162
+ },
7163
+ adding: {
7164
+ defaultContext: "details",
7165
+ variants: ["Adding", "Applying", "Attaching", "Dropping in"]
7166
+ },
7167
+ running: {
7168
+ defaultContext: "tasks",
7169
+ variants: ["Running", "Executing", "Launching", "Processing"]
7170
+ }
7171
+ };
7172
+ function makeAssistantStatus(kind, context) {
7173
+ return { kind, ...context ? { context } : {} };
7174
+ }
7175
+ function normalizeAssistantStatusText(text) {
7176
+ const trimmed = text.trim();
7177
+ if (!trimmed) {
7178
+ return "";
7179
+ }
7180
+ return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
7181
+ }
7182
+ function buildAssistantStatusPresentation(args) {
7183
+ const random = args.random ?? Math.random;
7184
+ const pattern = STATUS_PATTERNS[args.status.kind];
7185
+ const context = normalizeAssistantStatusText(args.status.context ?? "") || pattern.defaultContext;
7186
+ const index = Math.floor(random() * pattern.variants.length);
7187
+ const verb = pattern.variants[index] ?? pattern.variants[0];
7188
+ const visible = truncateStatusText(`${verb} ${context}`);
7189
+ const hint = truncateStatusText(`${pattern.variants[0]} ${context}`);
7190
+ return {
7191
+ key: `${args.status.kind}:${context}`,
7192
+ hint,
7193
+ visible,
7194
+ suggestions: Array.from(/* @__PURE__ */ new Set([visible, hint]))
7195
+ };
7196
+ }
7197
+ function createSlackAdapterAssistantStatusTransport(args) {
7198
+ return {
7199
+ async setStatus(channelId, threadTs, status, suggestions) {
7200
+ try {
7201
+ await args.getSlackAdapter().setAssistantStatus(channelId, threadTs, status, suggestions);
7202
+ } catch (error) {
7203
+ logAssistantStatusFailure(status, error);
7204
+ }
7205
+ }
7206
+ };
7207
+ }
7208
+ function createSlackWebApiAssistantStatusTransport(args) {
7209
+ const getClient2 = args?.getSlackClient ?? getSlackClient;
7210
+ return {
7211
+ async setStatus(channelId, threadTs, status, suggestions) {
7212
+ try {
7213
+ await getClient2().assistant.threads.setStatus({
7214
+ channel_id: channelId,
7215
+ thread_ts: threadTs,
7216
+ status,
7217
+ ...suggestions ? { loading_messages: suggestions } : {}
7218
+ });
7219
+ } catch (error) {
7220
+ logAssistantStatusFailure(status, error);
7221
+ }
7222
+ }
7223
+ };
7224
+ }
7225
+ function logAssistantStatusFailure(status, error) {
7226
+ logWarn(
7227
+ "assistant_status_update_failed",
7228
+ {},
7229
+ {
7230
+ "app.slack.status_text": status || "(clear)",
7231
+ "error.message": error instanceof Error ? error.message : String(error)
7232
+ },
7233
+ "Failed to update assistant status"
7234
+ );
7235
+ }
7236
+
6976
7237
  // src/chat/sandbox/sandbox.ts
6977
7238
  var SANDBOX_TOOL_NAMES = /* @__PURE__ */ new Set(["bash", "readFile", "writeFile"]);
6978
7239
  var DEFAULT_MAX_OUTPUT_LENGTH = 3e4;
@@ -7465,7 +7726,7 @@ function createSandboxExecutor(options) {
7465
7726
  const createSandboxFromSnapshot = async (snapshotId, sandboxCredentials, onStatus) => {
7466
7727
  for (let attempt = 0; attempt < SNAPSHOT_BOOT_RETRY_COUNT; attempt += 1) {
7467
7728
  try {
7468
- await onStatus?.("Booting up...");
7729
+ await onStatus?.(makeAssistantStatus("loading", "sandbox"));
7469
7730
  return await Sandbox.create({
7470
7731
  timeout: timeoutMs,
7471
7732
  source: {
@@ -7585,28 +7846,37 @@ function createSandboxExecutor(options) {
7585
7846
  let statusCount = 0;
7586
7847
  const sentStatuses = /* @__PURE__ */ new Set();
7587
7848
  const emitSandboxStatus = async (status) => {
7588
- if (!emitStatus || statusCount >= 4 || sentStatuses.has(status)) {
7849
+ const statusKey = `${status.kind}:${status.context ?? ""}`;
7850
+ if (!emitStatus || statusCount >= 4 || sentStatuses.has(statusKey)) {
7589
7851
  return;
7590
7852
  }
7591
- sentStatuses.add(status);
7853
+ sentStatuses.add(statusKey);
7592
7854
  statusCount += 1;
7593
7855
  await emitStatus(status);
7594
7856
  };
7595
7857
  const reportSnapshotPhase = async (phase) => {
7596
7858
  if (phase === "resolve_start") {
7597
- await emitSandboxStatus("Checking sandbox snapshot cache...");
7859
+ await emitSandboxStatus(
7860
+ makeAssistantStatus("loading", "sandbox snapshot cache")
7861
+ );
7598
7862
  return;
7599
7863
  }
7600
7864
  if (phase === "waiting_for_lock") {
7601
- await emitSandboxStatus("Waiting for sandbox snapshot build...");
7865
+ await emitSandboxStatus(
7866
+ makeAssistantStatus("loading", "sandbox snapshot build")
7867
+ );
7602
7868
  return;
7603
7869
  }
7604
7870
  if (phase === "building_snapshot") {
7605
- await emitSandboxStatus("Building sandbox snapshot...");
7871
+ await emitSandboxStatus(
7872
+ makeAssistantStatus("creating", "sandbox snapshot")
7873
+ );
7606
7874
  return;
7607
7875
  }
7608
7876
  if (phase === "cache_hit") {
7609
- await emitSandboxStatus("Using cached sandbox snapshot...");
7877
+ await emitSandboxStatus(
7878
+ makeAssistantStatus("loading", "sandbox snapshot")
7879
+ );
7610
7880
  }
7611
7881
  };
7612
7882
  let createdSandbox;
@@ -7620,7 +7890,9 @@ function createSandboxExecutor(options) {
7620
7890
  "app.sandbox.runtime": runtime
7621
7891
  },
7622
7892
  async () => {
7623
- await emitSandboxStatus("Preparing sandbox runtime...");
7893
+ await emitSandboxStatus(
7894
+ makeAssistantStatus("loading", "sandbox runtime")
7895
+ );
7624
7896
  const snapshot = await resolveRuntimeDependencySnapshot({
7625
7897
  runtime,
7626
7898
  timeoutMs,
@@ -7639,7 +7911,9 @@ function createSandboxExecutor(options) {
7639
7911
  } : {}
7640
7912
  });
7641
7913
  if (!snapshot.snapshotId) {
7642
- await emitSandboxStatus("Booting up...");
7914
+ await emitSandboxStatus(
7915
+ makeAssistantStatus("loading", "sandbox")
7916
+ );
7643
7917
  return await Sandbox.create({
7644
7918
  timeout: timeoutMs,
7645
7919
  runtime,
@@ -8069,219 +8343,74 @@ function shouldEmitDevAgentTrace() {
8069
8343
  return process.env.NODE_ENV === "development";
8070
8344
  }
8071
8345
 
8072
- // src/chat/runtime/status-format.ts
8073
- var SLACK_STATUS_MAX_LENGTH = 50;
8074
- function truncateWithEllipsis(text, maxLength) {
8075
- if (text.length <= maxLength) {
8076
- return text;
8346
+ // src/chat/runtime/tool-status.ts
8347
+ function buildToolStatus(toolName, input) {
8348
+ const obj = input && typeof input === "object" ? input : void 0;
8349
+ const command = obj ? compactStatusCommand(obj.command) : void 0;
8350
+ const path6 = obj ? compactStatusPath(obj.path) : void 0;
8351
+ const filename = obj ? compactStatusFilename(obj.path) : void 0;
8352
+ const query = obj ? compactStatusText(obj.query, 70) : void 0;
8353
+ const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
8354
+ const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
8355
+ const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
8356
+ if (command && toolName === "bash") {
8357
+ return makeAssistantStatus("running", command);
8077
8358
  }
8078
- return `${text.slice(0, Math.max(1, maxLength - 3)).trimEnd()}...`;
8079
- }
8080
- function truncateStatusText(text) {
8081
- const trimmed = text.trim();
8082
- if (!trimmed) {
8083
- return "";
8084
- }
8085
- return truncateWithEllipsis(trimmed, SLACK_STATUS_MAX_LENGTH);
8086
- }
8087
- function compactStatusPath(value) {
8088
- if (typeof value !== "string") {
8089
- return void 0;
8090
- }
8091
- const trimmed = value.trim();
8092
- if (!trimmed) {
8093
- return void 0;
8094
- }
8095
- if (trimmed.length <= 80) {
8096
- return trimmed;
8097
- }
8098
- return `...${trimmed.slice(-77)}`;
8099
- }
8100
- function compactStatusText(value, maxLength = 80) {
8101
- if (typeof value !== "string") {
8102
- return void 0;
8103
- }
8104
- const trimmed = value.trim();
8105
- if (!trimmed) {
8106
- return void 0;
8107
- }
8108
- return truncateWithEllipsis(trimmed, maxLength);
8109
- }
8110
- function readShellToken(command, startIndex) {
8111
- let index = startIndex;
8112
- while (index < command.length && /\s/.test(command[index] ?? "")) {
8113
- index += 1;
8114
- }
8115
- if (index >= command.length) {
8116
- return void 0;
8117
- }
8118
- let token = "";
8119
- let quote;
8120
- while (index < command.length) {
8121
- const char = command[index];
8122
- if (!char) {
8123
- break;
8124
- }
8125
- if (quote) {
8126
- if (char === quote) {
8127
- quote = void 0;
8128
- index += 1;
8129
- continue;
8130
- }
8131
- if (char === "\\" && quote === '"' && index + 1 < command.length) {
8132
- token += command[index + 1];
8133
- index += 2;
8134
- continue;
8135
- }
8136
- token += char;
8137
- index += 1;
8138
- continue;
8139
- }
8140
- if (/\s/.test(char)) {
8141
- break;
8142
- }
8143
- if (char === '"' || char === "'") {
8144
- quote = char;
8145
- index += 1;
8146
- continue;
8147
- }
8148
- if (char === "\\" && index + 1 < command.length) {
8149
- token += command[index + 1];
8150
- index += 2;
8151
- continue;
8152
- }
8153
- token += char;
8154
- index += 1;
8155
- }
8156
- return { token, nextIndex: index };
8157
- }
8158
- function compactStatusCommand(value) {
8159
- if (typeof value !== "string") {
8160
- return void 0;
8359
+ if (filename && toolName === "readFile") {
8360
+ return makeAssistantStatus("reading", filename);
8161
8361
  }
8162
- const trimmed = value.trim();
8163
- if (!trimmed) {
8164
- return void 0;
8362
+ if (filename && toolName === "writeFile") {
8363
+ return makeAssistantStatus("updating", filename);
8165
8364
  }
8166
- let index = 0;
8167
- while (index < trimmed.length) {
8168
- const parsed = readShellToken(trimmed, index);
8169
- if (!parsed) {
8170
- return void 0;
8171
- }
8172
- index = parsed.nextIndex;
8173
- if (!parsed.token) {
8174
- continue;
8175
- }
8176
- if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(parsed.token)) {
8177
- continue;
8178
- }
8179
- const normalized = parsed.token.replace(/[\\/]+$/g, "");
8180
- if (!normalized) {
8181
- return void 0;
8182
- }
8183
- const parts = normalized.split(/[\\/]/).filter((part) => part.length > 0);
8184
- const command = parts.length > 0 ? parts[parts.length - 1] : normalized;
8185
- return compactStatusText(command, 40);
8365
+ if (path6 && toolName === "writeFile") {
8366
+ return makeAssistantStatus("updating", path6);
8186
8367
  }
8187
- return void 0;
8188
- }
8189
- function compactStatusFilename(value) {
8190
- if (typeof value !== "string") {
8191
- return void 0;
8368
+ if (skillName && toolName === "loadSkill") {
8369
+ return makeAssistantStatus("loading", skillName);
8192
8370
  }
8193
- const trimmed = value.trim().replace(/[\\/]+$/g, "");
8194
- if (!trimmed) {
8195
- return void 0;
8371
+ if (query && toolName === "webSearch") {
8372
+ return makeAssistantStatus("searching", `"${query}"`);
8196
8373
  }
8197
- const parts = trimmed.split(/[\\/]/).filter((part) => part.length > 0);
8198
- const filename = parts.length > 0 ? parts[parts.length - 1] : trimmed;
8199
- return compactStatusText(filename, 80);
8200
- }
8201
- function extractStatusUrlDomain(value) {
8202
- if (typeof value !== "string") {
8203
- return void 0;
8374
+ if (query && provider && toolName === "searchTools") {
8375
+ return makeAssistantStatus("searching", `${provider} "${query}"`);
8204
8376
  }
8205
- const trimmed = value.trim();
8206
- if (!trimmed) {
8207
- return void 0;
8377
+ if (query && toolName === "searchTools") {
8378
+ return makeAssistantStatus("searching", `"${query}"`);
8208
8379
  }
8209
- try {
8210
- const parsed = new URL(trimmed);
8211
- return parsed.hostname || void 0;
8212
- } catch {
8213
- return void 0;
8380
+ if (domain && toolName === "webFetch") {
8381
+ return makeAssistantStatus("fetching", domain);
8214
8382
  }
8215
- }
8216
-
8217
- // src/chat/runtime/tool-status.ts
8218
- function formatToolStatus(toolName) {
8219
8383
  const known = {
8220
- loadSkill: "Loading skill instructions",
8221
- systemTime: "Reading current system time",
8222
- bash: "Working in the shell",
8223
- readFile: "Reading a file",
8224
- writeFile: "Updating a file",
8225
- webSearch: "Searching public sources",
8226
- webFetch: "Reading source pages",
8227
- slackChannelPostMessage: "Posting message to channel",
8228
- slackMessageAddReaction: "Adding emoji reaction",
8229
- slackChannelListMessages: "Listing channel messages",
8230
- slackCanvasCreate: "Creating detailed brief",
8231
- slackCanvasUpdate: "Updating detailed brief",
8232
- slackListCreate: "Creating tracking list",
8233
- slackListAddItems: "Updating tracking list",
8234
- slackListUpdateItem: "Updating tracking list",
8235
- imageGenerate: "Generating image",
8236
- searchTools: "Searching active tools"
8384
+ loadSkill: makeAssistantStatus("loading", "skill instructions"),
8385
+ systemTime: makeAssistantStatus("reading", "system time"),
8386
+ bash: makeAssistantStatus("running", "shell"),
8387
+ readFile: makeAssistantStatus("reading", "file"),
8388
+ writeFile: makeAssistantStatus("updating", "file"),
8389
+ webSearch: makeAssistantStatus("searching", "sources"),
8390
+ webFetch: makeAssistantStatus("fetching", "pages"),
8391
+ slackChannelPostMessage: makeAssistantStatus("posting", "channel"),
8392
+ slackMessageAddReaction: makeAssistantStatus("adding", "reaction"),
8393
+ slackChannelListMessages: makeAssistantStatus("listing", "messages"),
8394
+ slackCanvasCreate: makeAssistantStatus("creating", "brief"),
8395
+ slackCanvasUpdate: makeAssistantStatus("updating", "brief"),
8396
+ slackListCreate: makeAssistantStatus("creating", "tracking list"),
8397
+ slackListAddItems: makeAssistantStatus("updating", "tracking list"),
8398
+ slackListUpdateItem: makeAssistantStatus("updating", "tracking list"),
8399
+ imageGenerate: makeAssistantStatus("creating", "image"),
8400
+ searchTools: makeAssistantStatus(
8401
+ "searching",
8402
+ provider ? `${provider} tools` : "tools"
8403
+ )
8237
8404
  };
8238
8405
  if (known[toolName]) {
8239
8406
  return known[toolName];
8240
8407
  }
8241
8408
  const mcpMatch = /^mcp__([^_]+)__(.+)$/.exec(toolName);
8242
8409
  if (mcpMatch) {
8243
- return `Running ${mcpMatch[1]}/${mcpMatch[2]}`;
8410
+ return makeAssistantStatus("running", `${mcpMatch[1]}/${mcpMatch[2]}`);
8244
8411
  }
8245
8412
  const readable = toolName.replaceAll("_", " ").trim();
8246
- return readable.length > 0 ? `Running ${readable}` : "Running tool";
8247
- }
8248
- function formatToolStatusWithInput(toolName, input) {
8249
- const obj = input && typeof input === "object" ? input : void 0;
8250
- const command = obj ? compactStatusCommand(obj.command) : void 0;
8251
- const path6 = obj ? compactStatusPath(obj.path) : void 0;
8252
- const filename = obj ? compactStatusFilename(obj.path) : void 0;
8253
- const query = obj ? compactStatusText(obj.query, 70) : void 0;
8254
- const domain = obj ? extractStatusUrlDomain(obj.url) : void 0;
8255
- const skillName = obj ? compactStatusText(obj.skill_name ?? obj.skillName, 40) : void 0;
8256
- const provider = obj ? compactStatusText(obj.provider, 20) : void 0;
8257
- if (command && toolName === "bash") {
8258
- return `Running ${command}`;
8259
- }
8260
- if (filename && toolName === "readFile") {
8261
- return `Reading file ${filename}`;
8262
- }
8263
- if (filename && toolName === "writeFile") {
8264
- return `Updating file ${filename}`;
8265
- }
8266
- if (path6 && toolName === "writeFile") {
8267
- return `Updating file ${path6}`;
8268
- }
8269
- if (skillName && toolName === "loadSkill") {
8270
- return `Loading skill ${skillName}`;
8271
- }
8272
- if (query && toolName === "webSearch") {
8273
- return `Searching web for "${query}"`;
8274
- }
8275
- if (query && provider && toolName === "searchTools") {
8276
- return `Searching ${provider} tools for "${query}"`;
8277
- }
8278
- if (query && toolName === "searchTools") {
8279
- return `Searching tools for "${query}"`;
8280
- }
8281
- if (domain && toolName === "webFetch") {
8282
- return `Fetching page from ${domain}`;
8283
- }
8284
- return formatToolStatus(toolName);
8413
+ return makeAssistantStatus("running", readable || "tool");
8285
8414
  }
8286
8415
 
8287
8416
  // src/chat/tools/execution/build-sandbox-input.ts
@@ -8432,7 +8561,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
8432
8561
  turnId: spanContext.turnId,
8433
8562
  agentId: spanContext.agentId
8434
8563
  };
8435
- await onStatus?.(`${formatToolStatusWithInput(toolName, params)}...`);
8564
+ await onStatus?.(buildToolStatus(toolName, params));
8436
8565
  return withSpan(
8437
8566
  `execute_tool ${toolName}`,
8438
8567
  "gen_ai.execute_tool",
@@ -9098,6 +9227,7 @@ async function generateAssistantReply(messageText, context = {}) {
9098
9227
  requesterId: context.requester?.userId,
9099
9228
  resolveConfiguration: async (key) => configurationValues[key]
9100
9229
  });
9230
+ const providerAuthActions = /* @__PURE__ */ new Map();
9101
9231
  const sandboxExecutor = createSandboxExecutor({
9102
9232
  sandboxId: context.sandbox?.sandboxId,
9103
9233
  sandboxDependencyProfileHash: context.sandbox?.sandboxDependencyProfileHash,
@@ -9113,6 +9243,7 @@ async function generateAssistantReply(messageText, context = {}) {
9113
9243
  threadTs: context.correlation?.threadTs,
9114
9244
  userMessage: userInput,
9115
9245
  userTokenStore: createUserTokenStore(),
9246
+ providerAuthActions,
9116
9247
  onConfigurationValueChanged: (key, value) => {
9117
9248
  if (value === void 0) {
9118
9249
  delete configurationValues[key];
@@ -9201,11 +9332,6 @@ async function generateAssistantReply(messageText, context = {}) {
9201
9332
  onArtifactStatePatch: (patch) => {
9202
9333
  Object.assign(artifactStatePatch, patch);
9203
9334
  },
9204
- onToolCallStart: async (toolName, input) => {
9205
- await context.onStatus?.(
9206
- `${formatToolStatusWithInput(toolName, input)}...`
9207
- );
9208
- },
9209
9335
  toolOverrides: context.toolOverrides,
9210
9336
  onSkillLoaded: async (loadedSkill) => {
9211
9337
  const resolvedSkill = await skillSandbox.loadSkill(loadedSkill.name);
@@ -9559,6 +9685,152 @@ async function generateAssistantReply(messageText, context = {}) {
9559
9685
  }
9560
9686
  }
9561
9687
 
9688
+ // src/chat/runtime/progress-reporter.ts
9689
+ var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
9690
+ var STATUS_MIN_VISIBLE_MS = 1200;
9691
+ var STATUS_ROTATION_INTERVAL_MS = 3e4;
9692
+ function createProgressReporter(args) {
9693
+ const now = args.now ?? (() => Date.now());
9694
+ const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
9695
+ const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
9696
+ const random = args.random ?? Math.random;
9697
+ let active = false;
9698
+ let currentKey = "";
9699
+ let currentStatus = makeAssistantStatus("thinking");
9700
+ let currentVisibleStatus = "";
9701
+ let lastStatusAt = 0;
9702
+ let pendingStatus = null;
9703
+ let pendingKey = "";
9704
+ let pendingTimer = null;
9705
+ let rotationTimer = null;
9706
+ let inflightStatusUpdate = Promise.resolve();
9707
+ const scheduleRotation = () => {
9708
+ if (rotationTimer) {
9709
+ clearTimer(rotationTimer);
9710
+ rotationTimer = null;
9711
+ }
9712
+ if (!active || !currentVisibleStatus) {
9713
+ return;
9714
+ }
9715
+ rotationTimer = setTimer(() => {
9716
+ rotationTimer = null;
9717
+ if (!active || !currentVisibleStatus) {
9718
+ return;
9719
+ }
9720
+ void postRenderedStatus(currentStatus);
9721
+ }, STATUS_ROTATION_INTERVAL_MS);
9722
+ };
9723
+ const postStatus = async (text, suggestions) => {
9724
+ const channelId = args.channelId;
9725
+ const threadTs = args.threadTs;
9726
+ if (!channelId || !threadTs) {
9727
+ return;
9728
+ }
9729
+ if (!text && !currentVisibleStatus) {
9730
+ return;
9731
+ }
9732
+ currentVisibleStatus = text;
9733
+ lastStatusAt = now();
9734
+ scheduleRotation();
9735
+ const previous = inflightStatusUpdate;
9736
+ const request = (async () => {
9737
+ await previous;
9738
+ await args.transport.setStatus(channelId, threadTs, text, suggestions);
9739
+ })();
9740
+ inflightStatusUpdate = request;
9741
+ await request;
9742
+ };
9743
+ const postRenderedStatus = async (status) => {
9744
+ const presentation = buildAssistantStatusPresentation({
9745
+ status,
9746
+ random
9747
+ });
9748
+ currentStatus = status;
9749
+ currentKey = presentation.key;
9750
+ await postStatus(presentation.visible, presentation.suggestions);
9751
+ };
9752
+ const clearPending = () => {
9753
+ if (pendingTimer) {
9754
+ clearTimer(pendingTimer);
9755
+ pendingTimer = null;
9756
+ }
9757
+ pendingStatus = null;
9758
+ pendingKey = "";
9759
+ };
9760
+ const flushPending = async () => {
9761
+ if (!active || !pendingStatus) {
9762
+ clearPending();
9763
+ return;
9764
+ }
9765
+ const next = pendingStatus;
9766
+ clearPending();
9767
+ const nextPresentation = buildAssistantStatusPresentation({
9768
+ status: next,
9769
+ random
9770
+ });
9771
+ if (nextPresentation.key !== currentKey) {
9772
+ await postRenderedStatus(next);
9773
+ }
9774
+ };
9775
+ return {
9776
+ async start() {
9777
+ active = true;
9778
+ clearPending();
9779
+ currentStatus = makeAssistantStatus("thinking");
9780
+ currentKey = "";
9781
+ void postRenderedStatus(currentStatus);
9782
+ },
9783
+ async stop() {
9784
+ active = false;
9785
+ clearPending();
9786
+ if (rotationTimer) {
9787
+ clearTimer(rotationTimer);
9788
+ rotationTimer = null;
9789
+ }
9790
+ currentKey = "";
9791
+ await postStatus("");
9792
+ },
9793
+ async setStatus(status) {
9794
+ if (!active) {
9795
+ return;
9796
+ }
9797
+ const presentation = buildAssistantStatusPresentation({
9798
+ status,
9799
+ random
9800
+ });
9801
+ if (!presentation.visible) {
9802
+ return;
9803
+ }
9804
+ if (presentation.key === currentKey || presentation.key === pendingKey) {
9805
+ return;
9806
+ }
9807
+ const elapsed = now() - lastStatusAt;
9808
+ const waitMs = Math.max(
9809
+ STATUS_UPDATE_DEBOUNCE_MS - elapsed,
9810
+ STATUS_MIN_VISIBLE_MS - elapsed,
9811
+ 0
9812
+ );
9813
+ if (waitMs <= 0) {
9814
+ clearPending();
9815
+ void postRenderedStatus(status);
9816
+ return;
9817
+ }
9818
+ pendingStatus = status;
9819
+ pendingKey = presentation.key;
9820
+ if (pendingTimer) {
9821
+ return;
9822
+ }
9823
+ pendingTimer = setTimer(
9824
+ () => {
9825
+ pendingTimer = null;
9826
+ void flushPending();
9827
+ },
9828
+ Math.max(1, waitMs)
9829
+ );
9830
+ }
9831
+ };
9832
+ }
9833
+
9562
9834
  // src/handlers/oauth-resume.ts
9563
9835
  function resolveReplyTimeoutMs(explicitTimeoutMs) {
9564
9836
  if (typeof explicitTimeoutMs === "number" && explicitTimeoutMs > 0) {
@@ -9581,69 +9853,6 @@ async function postSlackMessage(channelId, threadTs, text) {
9581
9853
  } catch {
9582
9854
  }
9583
9855
  }
9584
- async function setAssistantStatus(channelId, threadTs, status) {
9585
- try {
9586
- await getSlackClient().assistant.threads.setStatus({
9587
- channel_id: channelId,
9588
- thread_ts: threadTs,
9589
- status
9590
- });
9591
- } catch {
9592
- }
9593
- }
9594
- var STATUS_DEBOUNCE_MS = 1e3;
9595
- function createDebouncedStatusPoster(channelId, threadTs) {
9596
- let lastPostAt = 0;
9597
- let currentStatus = "";
9598
- let pendingStatus = null;
9599
- let pendingTimer = null;
9600
- let stopped = false;
9601
- const flush = async () => {
9602
- if (stopped || !pendingStatus) return;
9603
- const status = pendingStatus;
9604
- pendingStatus = null;
9605
- pendingTimer = null;
9606
- lastPostAt = Date.now();
9607
- currentStatus = status;
9608
- await setAssistantStatus(channelId, threadTs, status);
9609
- };
9610
- const post = async (status) => {
9611
- if (stopped) return;
9612
- const truncated = truncateStatusText(status);
9613
- if (!truncated || truncated === currentStatus) return;
9614
- const now = Date.now();
9615
- const elapsed = now - lastPostAt;
9616
- if (elapsed >= STATUS_DEBOUNCE_MS) {
9617
- if (pendingTimer) {
9618
- clearTimeout(pendingTimer);
9619
- pendingTimer = null;
9620
- }
9621
- pendingStatus = null;
9622
- lastPostAt = now;
9623
- currentStatus = truncated;
9624
- await setAssistantStatus(channelId, threadTs, truncated);
9625
- return;
9626
- }
9627
- pendingStatus = truncated;
9628
- if (!pendingTimer) {
9629
- pendingTimer = setTimeout(
9630
- () => {
9631
- void flush();
9632
- },
9633
- Math.max(1, STATUS_DEBOUNCE_MS - elapsed)
9634
- );
9635
- }
9636
- };
9637
- post.stop = () => {
9638
- stopped = true;
9639
- if (pendingTimer) {
9640
- clearTimeout(pendingTimer);
9641
- pendingTimer = null;
9642
- }
9643
- pendingStatus = null;
9644
- };
9645
- return post;
9646
- }
9647
9856
  function createReadOnlyConfigService(values) {
9648
9857
  const entries = Object.entries(values).map(([key, value]) => ({
9649
9858
  key,
@@ -9671,9 +9880,13 @@ function createReadOnlyConfigService(values) {
9671
9880
  };
9672
9881
  }
9673
9882
  async function resumeAuthorizedRequest(args) {
9674
- const postStatus = createDebouncedStatusPoster(args.channelId, args.threadTs);
9883
+ const progress = createProgressReporter({
9884
+ channelId: args.channelId,
9885
+ threadTs: args.threadTs,
9886
+ transport: createSlackWebApiAssistantStatusTransport()
9887
+ });
9675
9888
  await postSlackMessage(args.channelId, args.threadTs, args.connectedText);
9676
- await setAssistantStatus(args.channelId, args.threadTs, "Thinking...");
9889
+ await progress.start();
9677
9890
  try {
9678
9891
  const generateReply = args.generateReply ?? generateAssistantReply;
9679
9892
  const replyPromise = generateReply(args.messageText, {
@@ -9691,7 +9904,7 @@ async function resumeAuthorizedRequest(args) {
9691
9904
  artifactState: args.artifactState,
9692
9905
  configuration: args.configuration,
9693
9906
  channelConfiguration: args.configuration ? createReadOnlyConfigService(args.configuration) : void 0,
9694
- onStatus: postStatus
9907
+ onStatus: (status) => progress.setStatus(status)
9695
9908
  });
9696
9909
  const replyTimeoutMs = resolveReplyTimeoutMs(args.replyTimeoutMs);
9697
9910
  const reply = typeof replyTimeoutMs === "number" ? await Promise.race([
@@ -9707,8 +9920,7 @@ async function resumeAuthorizedRequest(args) {
9707
9920
  )
9708
9921
  )
9709
9922
  ]) : await replyPromise;
9710
- postStatus.stop();
9711
- await setAssistantStatus(args.channelId, args.threadTs, "");
9923
+ await progress.stop();
9712
9924
  if (args.onReply) {
9713
9925
  await args.onReply(reply);
9714
9926
  } else if (reply.text) {
@@ -9716,8 +9928,7 @@ async function resumeAuthorizedRequest(args) {
9716
9928
  }
9717
9929
  await args.onSuccess?.(reply);
9718
9930
  } catch (error) {
9719
- postStatus.stop();
9720
- await setAssistantStatus(args.channelId, args.threadTs, "");
9931
+ await progress.stop();
9721
9932
  if (isRetryableTurnError(error, "mcp_auth_resume") && args.onAuthPause) {
9722
9933
  await args.onAuthPause(error);
9723
9934
  return;
@@ -11593,111 +11804,6 @@ function createJuniorRuntimeServices(overrides = {}) {
11593
11804
  };
11594
11805
  }
11595
11806
 
11596
- // src/chat/runtime/progress-reporter.ts
11597
- var STATUS_UPDATE_DEBOUNCE_MS = 1e3;
11598
- var STATUS_MIN_VISIBLE_MS = 1200;
11599
- function createProgressReporter(args) {
11600
- const now = args.now ?? (() => Date.now());
11601
- const setTimer = args.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
11602
- const clearTimer = args.clearTimer ?? ((timer) => clearTimeout(timer));
11603
- let active = false;
11604
- let currentStatus = "";
11605
- let lastStatusAt = 0;
11606
- let pendingStatus = null;
11607
- let pendingTimer = null;
11608
- let inflightStatusUpdate = Promise.resolve();
11609
- const postStatus = async (text) => {
11610
- const channelId = args.channelId;
11611
- const threadTs = args.threadTs;
11612
- if (!channelId || !threadTs) {
11613
- return;
11614
- }
11615
- if (!text && !currentStatus) {
11616
- return;
11617
- }
11618
- currentStatus = text;
11619
- lastStatusAt = now();
11620
- const suggestions = text ? [text] : void 0;
11621
- const previous = inflightStatusUpdate;
11622
- const request = (async () => {
11623
- await previous;
11624
- try {
11625
- await args.setAssistantStatus(channelId, threadTs, text, suggestions);
11626
- } catch (error) {
11627
- logWarn(
11628
- "assistant_status_update_failed",
11629
- {},
11630
- {
11631
- "app.slack.status_text": text || "(clear)",
11632
- "error.message": error instanceof Error ? error.message : String(error)
11633
- },
11634
- "Failed to update assistant status"
11635
- );
11636
- }
11637
- })();
11638
- inflightStatusUpdate = request;
11639
- await request;
11640
- };
11641
- const clearPending = () => {
11642
- if (pendingTimer) {
11643
- clearTimer(pendingTimer);
11644
- pendingTimer = null;
11645
- }
11646
- pendingStatus = null;
11647
- };
11648
- const flushPending = async () => {
11649
- if (!active || !pendingStatus) {
11650
- clearPending();
11651
- return;
11652
- }
11653
- const next = pendingStatus;
11654
- clearPending();
11655
- if (next !== currentStatus) {
11656
- await postStatus(next);
11657
- }
11658
- };
11659
- return {
11660
- async start() {
11661
- active = true;
11662
- clearPending();
11663
- void postStatus("Thinking...");
11664
- },
11665
- async stop() {
11666
- active = false;
11667
- clearPending();
11668
- await postStatus("");
11669
- },
11670
- async setStatus(text) {
11671
- const truncated = truncateStatusText(text);
11672
- if (!active || !truncated || truncated === currentStatus || truncated === pendingStatus) {
11673
- return;
11674
- }
11675
- const elapsed = now() - lastStatusAt;
11676
- const waitMs = Math.max(
11677
- STATUS_UPDATE_DEBOUNCE_MS - elapsed,
11678
- STATUS_MIN_VISIBLE_MS - elapsed,
11679
- 0
11680
- );
11681
- if (waitMs <= 0) {
11682
- clearPending();
11683
- void postStatus(truncated);
11684
- return;
11685
- }
11686
- pendingStatus = truncated;
11687
- if (pendingTimer) {
11688
- return;
11689
- }
11690
- pendingTimer = setTimer(
11691
- () => {
11692
- pendingTimer = null;
11693
- void flushPending();
11694
- },
11695
- Math.max(1, waitMs)
11696
- );
11697
- }
11698
- };
11699
- }
11700
-
11701
11807
  // src/chat/runtime/streaming.ts
11702
11808
  function createTextStreamBridge() {
11703
11809
  const queue = [];
@@ -11738,36 +11844,6 @@ function createTextStreamBridge() {
11738
11844
  }
11739
11845
  };
11740
11846
  }
11741
- function createNormalizingStream(inner, normalize) {
11742
- return {
11743
- async *[Symbol.asyncIterator]() {
11744
- let accumulated = "";
11745
- let emitted = 0;
11746
- for await (const chunk of inner) {
11747
- accumulated += chunk;
11748
- const lastNewline = accumulated.lastIndexOf("\n");
11749
- if (lastNewline === -1) {
11750
- const delta2 = accumulated.slice(emitted);
11751
- if (delta2) {
11752
- yield delta2;
11753
- emitted = accumulated.length;
11754
- }
11755
- continue;
11756
- }
11757
- const stable = accumulated.slice(0, lastNewline + 1);
11758
- const normalized = normalize(stable);
11759
- const delta = normalized.slice(emitted);
11760
- emitted = normalized.length;
11761
- if (delta) yield delta;
11762
- }
11763
- if (accumulated) {
11764
- const normalized = normalize(accumulated);
11765
- const delta = normalized.slice(emitted);
11766
- if (delta) yield delta;
11767
- }
11768
- }
11769
- };
11770
- }
11771
11847
 
11772
11848
  // src/chat/runtime/reply-executor.ts
11773
11849
  function getExecutionFailureReason(reply) {
@@ -11881,7 +11957,9 @@ function createReplyToThread(deps) {
11881
11957
  const progress = createProgressReporter({
11882
11958
  channelId,
11883
11959
  threadTs,
11884
- setAssistantStatus: (channel, thread2, text, suggestions) => deps.getSlackAdapter().setAssistantStatus(channel, thread2, text, suggestions)
11960
+ transport: createSlackAdapterAssistantStatusTransport({
11961
+ getSlackAdapter: deps.getSlackAdapter
11962
+ })
11885
11963
  });
11886
11964
  const textStream = createTextStreamBridge();
11887
11965
  let streamedReplyPromise;
@@ -11898,10 +11976,7 @@ function createReplyToThread(deps) {
11898
11976
  if (!streamedReplyPromise) {
11899
11977
  const streamingReply = (async () => {
11900
11978
  return await postThreadReply(
11901
- createNormalizingStream(
11902
- textStream.iterable,
11903
- ensureBlockSpacing
11904
- ),
11979
+ textStream.iterable,
11905
11980
  "streaming_initial_post"
11906
11981
  );
11907
11982
  })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"