scream-code 0.19.6 → 0.19.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,9 +18,30 @@
18
18
  <a href="README.zh.md">简体中文</a>
19
19
  </p>
20
20
 
21
- <p align="center">
22
- <img width="2880" height="2880" alt="scream" src="https://github.com/user-attachments/assets/7268e848-b1b8-4540-9627-d21c94332229" />
23
- </p>
21
+ ### ❤️Sponsor
22
+
23
+ <details open>
24
+ <summary>Click to collapse</summary>
25
+
26
+ <table>
27
+ <tr>
28
+ <td width="210" align="center" valign="middle"><a href="https://todapi.work"><img src="assets/sponsor-todapi.png" alt="todapi" width="190"></a></td>
29
+ <td>
30
+
31
+ <p><strong>todapi</strong> — a high-value API gateway.</p>
32
+ <p>DeepSeek and other mainstream models from <strong>as low as 10% of list price</strong>, reached through one unified gateway: pay-as-you-go, one API key for multiple models. Powered by tier-one vendor resources with stable routes and strong value.</p>
33
+ <ul>
34
+ <li><strong>Price</strong>: DeepSeek and more from 10% of list price</li>
35
+ <li><strong>Access</strong>: one gateway, one key — switching models no longer means switching providers</li>
36
+ <li><strong>Get started</strong>: sign up at <a href="https://todapi.work">todapi.work</a></li>
37
+ </ul>
38
+ <p>Thanks to <a href="https://todapi.work">todapi</a> for sponsoring this project ❤️</p>
39
+
40
+ </td>
41
+ </tr>
42
+ </table>
43
+
44
+ </details>
24
45
 
25
46
  ---
26
47
 
@@ -5,8 +5,8 @@ const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  import { A as sharedMemoStore } from "./src-Cu3sUIJK.mjs";
8
- import { $n as validateFeishuCredentials, A as langFromPath, An as hasEmbeddingModelCache, Ar as flushDiagnosticLogs, At as createThemeStyles, B as getSharedSpeedTracker, Bn as isSendThrottled, Bt as OSC11_RESPONSE, Cn as BUILTIN_SLASH_COMMANDS, Cr as fetchCatalog, D as renderDiffLines, Dn as embeddingProgressLabel, Dt as isEmptySessionHintDismissed, E as ToolCallComponent, En as setExperimentalFlags, Er as normalizeWorkDir, Et as createMarkdownTheme, F as SkillActivationComponent, Fn as startLineDaemonDetached, Fr as SCREAM_ERROR_INFO, Ft as DISABLE_TERMINAL_FOCUS_REPORTING, G as AgentGroupComponent, Gn as parseAesKey, Gt as TERMINAL_FOCUS_OUT, H as lerpGradient, Ht as OSC11_RESPONSE_PREFIX_NO_ESC, I as AssistantMessageComponent, In as stopLineDaemon, It as DISABLE_TERMINAL_THEME_REPORTING, J as getBreathingFrame, Jn as TuiConfigParseError, Jt as safeUsageRatio, K as WelcomeComponent, Kn as replaceTabs, Kt as TERMINAL_THEME_DARK, L as ActivityGroupComponent, Ln as UPLOAD_MEDIA_TYPE, Lt as ENABLE_TERMINAL_FOCUS_REPORTING, M as ThinkingComponent, Mn as PIXEL_PULSE_FRAMES, Mr as resolveGlobalLogPath, Mt as getColorPalette, N as NoticeMessageComponent, Nn as PULSE_WAVE_FRAMES, Nr as isScreamError, Nt as detectTerminalTheme, O as renderDiffLinesClustered, On as embeddingFailureDetail, Or as isRealUserPrompt, Ot as isTurnElapsedEnabled, P as StatusMessageComponent, Pn as readLineDaemonStatus, Pr as isOrphanedToolCallError, Pt as parseOsc11BackgroundTheme, Qn as feishuFileType, R as charsForTokenBudget, Rn as WeixinApiError, Rt as ENABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as buildSkillSlashCommands, Sr as DEFAULT_CATALOG_URL, T as UserMessageComponent, Tn as isExperimentalFlagEnabled, Tr as ScreamHarness, Tt as createEditorTheme, U as lerpHex, Un as buildCdnDownloadUrl, Ut as QUERY_TERMINAL_THEME, V as GRADIENT_CYCLE_MS, Vn as validateWeixinToken, Vt as OSC11_RESPONSE_PREFIX, W as BackgroundAgentStatusComponent, Wn as decryptAesEcb, Wt as TERMINAL_FOCUS_IN, X as CompactionComponent, Xn as FeishuClient, Y as resetBreathingClock, Yn as loadTuiConfig, Yt as SubagentSlots, Z as clearGoalState, Zn as FeishuCredentialError, _n as TIP_ROTATION_INTERVAL_MS, _r as detectInstallSource, a as writeTextClipboard, an as appendStreamingArgsPreview, ar as getWeixinContextTokensFile, at as getModelCycleLevel, b as readUpdateCache, bn as getLlmNotSetMessage, br as CLI_USER_AGENT_PRODUCT, bt as supportsBalance, cn as isTodoItemShape, cr as loadLineConfig, d as isExpandable, dn as stringValue, dr as resolveTurnTimeoutMs, en as isBusy, er as LineConfigError, f as isPlanExpandable, fn as truncateErrorMessage, fr as resolveWeixinOptions, ft as handlePlanCommand, gn as SESSION_TIPS, gr as getLogDir, hn as MAIN_AGENT_ID, hr as getInputHistoryFile, i as openUrl, in as appendSessionHeaderHint, ir as getLinePidFile, it as changeThinkingLevel, j as CachedContainer, jn as startManualEmbeddingDownload, jr as log, jt as contrastTextHex, k as highlightLines, kn as getKnowledgeStore, kr as resolveScreamHome, kt as toggleEmptySessionHint, l as disposeChildren, ln as parseStreamingArgs, lr as resolveCustomCommands, m as formatMemoryMemoForInjection, mn as EXIT_CONFIRM_WINDOW_MS, mr as getDataDir, n as clearEvalPanelState, nn as printableChar, nr as assertSinglePlatformConfig, o as syncDiagramCapabilityPrompt, on as argsRecord, or as getWeixinCursorFile, p as MoonLoader, pr as detectShellEnvironment, q as BREATHE_CYCLE_MS, qn as sanitizeShellOutput, qt as TERMINAL_THEME_LIGHT, rn as STATUS_BULLET, rr as getLineConfigPath, rt as toTerminalHyperlink, sn as formatErrorMessage, sr as getWeixinStateDir, t as dispatchInput, tn as isStreaming, tr as MINUTE_MS, u as hasDispose, un as serializeToolResultOutput, ur as resolveFeishuOptions, ut as handleFusionPlanCommand, v as selectUpdateTarget, vn as getCtrlCHint, vr as CLI_COMMAND_NAME, wn as sortSlashCommands, wr as saveCatalogCache, wt as resolveThemeSync, x as appendJsonlLine, xn as getNoActiveSessionMessage, xr as PRODUCT_NAME, xt as clearInfoPanelState, y as refreshUpdateCache, yn as getCtrlDHint, yr as CLI_UI_MODE, yt as refreshProviderBalance, z as estimateTokens, zn as WeixinClient, zt as OSC11_QUERY } from "./dispatch-XPKo0cQR.mjs";
9
- import { i as t, n as getLocale, r as setLocale } from "./text-input-dialog-BqMy3ifH.mjs";
8
+ import { $n as validateFeishuCredentials, A as langFromPath, An as hasEmbeddingModelCache, At as createThemeStyles, B as getSharedSpeedTracker, Bn as isSendThrottled, Bt as OSC11_RESPONSE, Cn as BUILTIN_SLASH_COMMANDS, Cr as fetchCatalogWithMeta, D as renderDiffLines, Dn as embeddingProgressLabel, Dr as saveCatalogCacheMeta, Dt as isEmptySessionHintDismissed, E as ToolCallComponent, En as setExperimentalFlags, Er as saveCatalogCache, Et as createMarkdownTheme, F as SkillActivationComponent, Fn as startLineDaemonDetached, Fr as resolveGlobalLogPath, Ft as DISABLE_TERMINAL_FOCUS_REPORTING, G as AgentGroupComponent, Gn as parseAesKey, Gt as TERMINAL_FOCUS_OUT, H as lerpGradient, Ht as OSC11_RESPONSE_PREFIX_NO_ESC, I as AssistantMessageComponent, In as stopLineDaemon, Ir as isScreamError, It as DISABLE_TERMINAL_THEME_REPORTING, J as getBreathingFrame, Jn as TuiConfigParseError, Jt as safeUsageRatio, K as WelcomeComponent, Kn as replaceTabs, Kt as TERMINAL_THEME_DARK, L as ActivityGroupComponent, Ln as UPLOAD_MEDIA_TYPE, Lr as isOrphanedToolCallError, Lt as ENABLE_TERMINAL_FOCUS_REPORTING, M as ThinkingComponent, Mn as PIXEL_PULSE_FRAMES, Mr as resolveScreamHome, Mt as getColorPalette, N as NoticeMessageComponent, Nn as PULSE_WAVE_FRAMES, Nr as flushDiagnosticLogs, Nt as detectTerminalTheme, O as renderDiffLinesClustered, On as embeddingFailureDetail, Or as ScreamHarness, Ot as isTurnElapsedEnabled, P as StatusMessageComponent, Pn as readLineDaemonStatus, Pr as log, Pt as parseOsc11BackgroundTheme, Qn as feishuFileType, R as charsForTokenBudget, Rn as WeixinApiError, Rr as SCREAM_ERROR_INFO, Rt as ENABLE_TERMINAL_THEME_REPORTING, S as readJsonlFile, Sn as buildSkillSlashCommands, Sr as DEFAULT_CATALOG_URL, T as UserMessageComponent, Tn as isExperimentalFlagEnabled, Tr as loadCatalogCacheMeta, Tt as createEditorTheme, U as lerpHex, Un as buildCdnDownloadUrl, Ut as QUERY_TERMINAL_THEME, V as GRADIENT_CYCLE_MS, Vn as validateWeixinToken, Vt as OSC11_RESPONSE_PREFIX, W as BackgroundAgentStatusComponent, Wn as decryptAesEcb, Wt as TERMINAL_FOCUS_IN, X as CompactionComponent, Xn as FeishuClient, Y as resetBreathingClock, Yn as loadTuiConfig, Yt as SubagentSlots, Z as clearGoalState, Zn as FeishuCredentialError, _n as TIP_ROTATION_INTERVAL_MS, _r as detectInstallSource, a as writeTextClipboard, an as appendStreamingArgsPreview, ar as getWeixinContextTokensFile, at as getModelCycleLevel, b as readUpdateCache, bn as getLlmNotSetMessage, br as CLI_USER_AGENT_PRODUCT, bt as supportsBalance, cn as isTodoItemShape, cr as loadLineConfig, d as isExpandable, dn as stringValue, dr as resolveTurnTimeoutMs, en as isBusy, er as LineConfigError, f as isPlanExpandable, fn as truncateErrorMessage, fr as resolveWeixinOptions, ft as handlePlanCommand, gn as SESSION_TIPS, gr as getLogDir, hn as MAIN_AGENT_ID, hr as getInputHistoryFile, i as openUrl, in as appendSessionHeaderHint, ir as getLinePidFile, it as changeThinkingLevel, j as CachedContainer, jn as startManualEmbeddingDownload, jr as isRealUserPrompt, jt as contrastTextHex, k as highlightLines, kn as getKnowledgeStore, kr as normalizeWorkDir, kt as toggleEmptySessionHint, l as disposeChildren, ln as parseStreamingArgs, lr as resolveCustomCommands, m as formatMemoryMemoForInjection, mn as EXIT_CONFIRM_WINDOW_MS, mr as getDataDir, n as clearEvalPanelState, nn as printableChar, nr as assertSinglePlatformConfig, o as syncDiagramCapabilityPrompt, on as argsRecord, or as getWeixinCursorFile, p as MoonLoader, pr as detectShellEnvironment, q as BREATHE_CYCLE_MS, qn as sanitizeShellOutput, qt as TERMINAL_THEME_LIGHT, rn as STATUS_BULLET, rr as getLineConfigPath, rt as toTerminalHyperlink, sn as formatErrorMessage, sr as getWeixinStateDir, t as dispatchInput, tn as isStreaming, tr as MINUTE_MS, u as hasDispose, un as serializeToolResultOutput, ur as resolveFeishuOptions, ut as handleFusionPlanCommand, v as selectUpdateTarget, vn as getCtrlCHint, vr as CLI_COMMAND_NAME, wn as sortSlashCommands, wr as loadCatalogCache, wt as resolveThemeSync, x as appendJsonlLine, xn as getNoActiveSessionMessage, xr as PRODUCT_NAME, xt as clearInfoPanelState, y as refreshUpdateCache, yn as getCtrlDHint, yr as CLI_UI_MODE, yt as refreshProviderBalance, z as estimateTokens, zn as WeixinClient, zt as OSC11_QUERY } from "./dispatch-DMntc47e.mjs";
9
+ import { i as t, n as getLocale, r as setLocale } from "./text-input-dialog-Ce2AEFU9.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { createHash, randomInt, randomUUID } from "node:crypto";
12
12
  import { mkdir, mkdtemp, open, readFile, realpath, rm, stat, writeFile } from "node:fs/promises";
@@ -28,7 +28,7 @@ function optionalBuildString(value) {
28
28
  return typeof value === "string" && value.length > 0 ? value : void 0;
29
29
  }
30
30
  const SCREAM_BUILD_INFO = {
31
- version: optionalBuildString("0.19.6"),
31
+ version: optionalBuildString("0.19.8"),
32
32
  channel: optionalBuildString(""),
33
33
  commit: optionalBuildString(""),
34
34
  buildTarget: optionalBuildString("darwin-arm64")
@@ -5146,6 +5146,8 @@ async function autoStartLineDaemonIfConfigured() {
5146
5146
  }
5147
5147
  //#endregion
5148
5148
  //#region src/tui/controllers/auth-flow.ts
5149
+ /** Skip the startup refresh when the cached snapshot is younger than this. */
5150
+ const CATALOG_REFRESH_INTERVAL_MS = 1440 * 60 * 1e3;
5149
5151
  var AuthFlowController = class {
5150
5152
  host;
5151
5153
  constructor(host) {
@@ -5165,8 +5167,23 @@ var AuthFlowController = class {
5165
5167
  * existing cache (or built-in) keeps working. Never throws.
5166
5168
  */
5167
5169
  async prefetchCatalogCache() {
5170
+ const home = resolveScreamHome();
5171
+ const meta = loadCatalogCacheMeta(home);
5172
+ if (meta.fetchedAt !== void 0 && Date.now() - Date.parse(meta.fetchedAt) < CATALOG_REFRESH_INTERVAL_MS && loadCatalogCache(home) !== void 0) return;
5168
5173
  try {
5169
- saveCatalogCache(await fetchCatalog(DEFAULT_CATALOG_URL), resolveScreamHome());
5174
+ const result = await fetchCatalogWithMeta(DEFAULT_CATALOG_URL, meta.etag === void 0 ? {} : { etag: meta.etag });
5175
+ const fetchedAt = (/* @__PURE__ */ new Date()).toISOString();
5176
+ if (result.notModified) {
5177
+ saveCatalogCacheMeta(home, meta.etag === void 0 ? { fetchedAt } : {
5178
+ fetchedAt,
5179
+ etag: meta.etag
5180
+ });
5181
+ return;
5182
+ }
5183
+ if (result.catalog !== void 0) saveCatalogCache(result.catalog, home, result.etag === void 0 ? { fetchedAt } : {
5184
+ fetchedAt,
5185
+ etag: result.etag
5186
+ });
5170
5187
  } catch {}
5171
5188
  }
5172
5189
  async activateModelAfterLogin(model, thinkingLevel) {
@@ -7112,7 +7129,6 @@ var SessionEventHandler = class {
7112
7129
  providerLatencyWindow = 3;
7113
7130
  renderedSkillActivationIds = /* @__PURE__ */ new Set();
7114
7131
  renderedMcpServerStatusKeys = /* @__PURE__ */ new Map();
7115
- mcpServerStatusSpinners = /* @__PURE__ */ new Map();
7116
7132
  /**
7117
7133
  * `SendSubagentMessage` tool call → target agent id. Needed at result time
7118
7134
  * because `SubagentSlots.onMessagingEnd()` deletes its own mapping, and a
@@ -7140,7 +7156,6 @@ var SessionEventHandler = class {
7140
7156
  this.recentSubagentInstances.length = 0;
7141
7157
  this.renderedSkillActivationIds.clear();
7142
7158
  this.renderedMcpServerStatusKeys.clear();
7143
- this.stopAllMcpServerStatusSpinners();
7144
7159
  this.pendingSkillCandidates = [];
7145
7160
  this.promptedSkillCandidates.clear();
7146
7161
  this.goalJudgeCallId = void 0;
@@ -7198,7 +7213,11 @@ var SessionEventHandler = class {
7198
7213
  this.renderedMcpServerStatusKeys.set(server.name, mcpServerStatusKey(server));
7199
7214
  hidden.push(server);
7200
7215
  }
7201
- if (hidden.length > 0) host.showStatus(formatMcpStartupStatusSummary(hidden, visible.length), host.state.theme.colors.textMuted);
7216
+ if (hidden.length > 0) {
7217
+ const summary = formatMcpStartupStatusSummary(hidden, visible.length);
7218
+ if (hidden.some((server) => server.status === "failed" || server.status === "needs-auth")) host.showStatus(summary, host.state.theme.colors.textMuted);
7219
+ else host.showMcpActivity?.(summary, host.state.theme.colors.textMuted);
7220
+ }
7202
7221
  }
7203
7222
  handleEvent(event, sendQueued) {
7204
7223
  if (stampsActivity(event)) this.host.state.lastActivityAt = Date.now();
@@ -7308,17 +7327,6 @@ var SessionEventHandler = class {
7308
7327
  default: break;
7309
7328
  }
7310
7329
  }
7311
- stopAllMcpServerStatusSpinners() {
7312
- const { state } = this.host;
7313
- const children = state.transcriptContainer.children;
7314
- for (const spinner of this.mcpServerStatusSpinners.values()) {
7315
- spinner.stop();
7316
- const idx = children.indexOf(spinner);
7317
- if (idx >= 0) children.splice(idx, 1);
7318
- }
7319
- if (this.mcpServerStatusSpinners.size > 0) state.transcriptContainer.invalidate();
7320
- this.mcpServerStatusSpinners.clear();
7321
- }
7322
7330
  /** Read-only snapshot of the sidebar subagent slots (8 default types +
7323
7331
  * extras in spawn order). Consumed by the sidebar data provider. */
7324
7332
  getSubagentSlots() {
@@ -7860,6 +7868,12 @@ var SessionEventHandler = class {
7860
7868
  handleSessionWarning(event) {
7861
7869
  this.host.showStatus(t("handler.warning_prefix", { message: event.message }), this.host.state.theme.colors.warning);
7862
7870
  }
7871
+ /**
7872
+ * MCP server status goes to the status row above the editor — a transient
7873
+ * notice with its own TTL (same design as the collaboration ticker), not a
7874
+ * permanent transcript row. Failures / auth requests stay in the transcript
7875
+ * because they need user action and must not vanish after a few seconds.
7876
+ */
7863
7877
  renderMcpServerStatus(server) {
7864
7878
  const { state } = this.host;
7865
7879
  const key = mcpServerStatusKey(server);
@@ -7869,63 +7883,29 @@ var SessionEventHandler = class {
7869
7883
  switch (server.status) {
7870
7884
  case "connected": {
7871
7885
  const toolStr = `${server.toolCount} tool${server.toolCount === 1 ? "" : "s"}`;
7872
- const message = `MCP server "${server.name}" connected · ${toolStr} (${server.transport})`;
7873
- this.finalizeMcpServerStatusRow(server.name, message, colors.success);
7886
+ this.host.showMcpActivity?.(`MCP ${server.name} · ${toolStr}`, colors.success);
7874
7887
  return;
7875
7888
  }
7889
+ case "pending":
7890
+ this.host.showMcpActivity?.(t("handler.mcp_server_connecting", { name: server.name }), colors.textMuted);
7891
+ return;
7892
+ case "disabled":
7893
+ this.host.showMcpActivity?.(t("handler.mcp_server_disabled", { name: server.name }), colors.textMuted);
7894
+ return;
7876
7895
  case "failed": {
7877
7896
  const message = t("handler.mcp_server_failed", {
7878
7897
  name: server.name,
7879
7898
  error: server.error !== void 0 ? `: ${server.error}` : ""
7880
7899
  });
7881
- this.finalizeMcpServerStatusRow(server.name, message, colors.error);
7900
+ this.host.showStatus(message, colors.error);
7882
7901
  return;
7883
7902
  }
7884
7903
  case "needs-auth": {
7885
7904
  const message = t("handler.mcp_server_needs_auth", { name: server.name });
7886
- this.finalizeMcpServerStatusRow(server.name, message, colors.warning);
7905
+ this.host.showStatus(message, colors.warning);
7887
7906
  return;
7888
7907
  }
7889
- case "disabled":
7890
- this.finalizeMcpServerStatusRow(server.name, t("handler.mcp_server_disabled", { name: server.name }), colors.textMuted);
7891
- return;
7892
- case "pending":
7893
- this.showMcpServerStatusSpinner(server.name);
7894
- return;
7895
- }
7896
- }
7897
- showMcpServerStatusSpinner(name) {
7898
- const { state } = this.host;
7899
- const label = t("handler.mcp_server_connecting", { name });
7900
- const existing = this.mcpServerStatusSpinners.get(name);
7901
- if (existing !== void 0) {
7902
- existing.setLabel(label);
7903
- return;
7904
- }
7905
- const tint = (s) => chalk.hex(state.theme.colors.textMuted)(s);
7906
- const spinner = new MoonLoader(state.ui, tint, label);
7907
- state.transcriptContainer.addChild(spinner);
7908
- this.mcpServerStatusSpinners.set(name, spinner);
7909
- state.ui.requestRender();
7910
- }
7911
- finalizeMcpServerStatusRow(name, message, color) {
7912
- const { state } = this.host;
7913
- const spinner = this.mcpServerStatusSpinners.get(name);
7914
- if (spinner === void 0) {
7915
- this.host.showStatus(message, color);
7916
- return;
7917
7908
  }
7918
- spinner.stop();
7919
- const container = state.transcriptContainer;
7920
- const children = container.children;
7921
- const idx = children.indexOf(spinner);
7922
- if (idx >= 0) {
7923
- children.splice(idx, 1);
7924
- container.addChild(new StatusMessageComponent(message, state.theme.colors, color));
7925
- container.invalidate();
7926
- } else container.addChild(new StatusMessageComponent(message, state.theme.colors, color));
7927
- this.mcpServerStatusSpinners.delete(name);
7928
- state.ui.requestRender();
7929
7909
  }
7930
7910
  handleSkillActivated(event) {
7931
7911
  if (this.renderedSkillActivationIds.has(event.activationId)) return;
@@ -8068,7 +8048,7 @@ var SessionEventHandler = class {
8068
8048
  * candidate was detected. */
8069
8049
  promptSkillCandidate(session, name, purpose) {
8070
8050
  if (this.host.state.appState.model.trim().length === 0) return;
8071
- const request = `检测到可复用过程「${name}」${purpose ? `(${purpose})` : ""}。请用 AskUserQuestion 工具询问用户是否将其保存为可复用技能(选项:生成 / 忽略)。用户选择「生成」后,用 MakeSkillPlanTool 生成该技能;选择「忽略」则直接说明已跳过。`;
8051
+ const request = `检测到可复用过程「${name}」${purpose ? `(${purpose})` : ""}。请用 AskUserQuestion 工具询问用户是否将其保存为可复用技能(选项:生成 / 忽略)。用户选择「生成」后,用 MakeSkillPlanTool 生成该技能,再用 MakeSkillApply 落盘;落盘后把 plan.verification.instructions 原样告诉用户,并说明这是装完后要跑的触发检查。选择「忽略」则直接说明已跳过。`;
8072
8052
  session.prompt(request).catch(() => {
8073
8053
  this.host.showStatus(`检测到可复用过程「${name}」,但询问失败。可稍后手动处理。`, this.host.state.theme.colors.warning);
8074
8054
  });
@@ -11687,7 +11667,6 @@ var TranscriptController = class TranscriptController {
11687
11667
  const { state } = this.host;
11688
11668
  this.welcomeComponent?.stopBreathing();
11689
11669
  const welcome = new WelcomeComponent(state.appState, state.theme.colors, state.ui);
11690
- welcome.borderTitle = "Scream Code";
11691
11670
  this.welcomeComponent = welcome;
11692
11671
  if (state.editor.hasFirstInputFired()) welcome.stopBreathing();
11693
11672
  state.transcriptContainer.addChild(welcome);
@@ -11877,14 +11856,14 @@ var GutterContainer = class extends Container {
11877
11856
  * string and truncated to the available width.
11878
11857
  */
11879
11858
  /**
11880
- * Single-line row: `<wave> <label> <reminder> <activity>`, empty parts
11881
- * omitted.
11859
+ * Single-line row: `<wave> <label> <reminder> <activity> <notice>`,
11860
+ * empty parts omitted.
11882
11861
  *
11883
11862
  * HStack cannot produce this layout: pi-tui's Text.render pads every line to
11884
11863
  * the full row width, so each child's intrinsic width equals the full width
11885
11864
  * and HStack's shrink distribution pushes later segments far to the right.
11886
11865
  * Composing the line manually from the loader's unpadded frame text keeps the
11887
- * segments adjacent, and it re-reads the frame (and both chips) on every
11866
+ * segments adjacent, and it re-reads the frame (and every chip) on every
11888
11867
  * render so the wave keeps animating and the tickers update in place.
11889
11868
  */
11890
11869
  var StatusLine = class {
@@ -11893,27 +11872,50 @@ var StatusLine = class {
11893
11872
  labelColor;
11894
11873
  reminder;
11895
11874
  activity;
11875
+ notice;
11896
11876
  labelCollapsesOnChip;
11897
- constructor(wave, label, labelColor, reminder, activity, labelCollapsesOnChip = false) {
11877
+ constructor(wave, label, labelColor, reminder, activity, notice, labelCollapsesOnChip = false) {
11898
11878
  this.wave = wave;
11899
11879
  this.label = label;
11900
11880
  this.labelColor = labelColor;
11901
11881
  this.reminder = reminder;
11902
11882
  this.activity = activity;
11883
+ this.notice = notice;
11903
11884
  this.labelCollapsesOnChip = labelCollapsesOnChip;
11904
11885
  }
11905
11886
  render(width) {
11906
11887
  const minWidth = Math.max(1, width);
11907
11888
  const reminder = this.reminder?.() ?? null;
11908
11889
  const activity = this.activity?.() ?? null;
11890
+ const notice = this.notice?.() ?? null;
11909
11891
  const showLabel = !(this.labelCollapsesOnChip && (reminder !== null || activity !== null));
11910
- const segments = [];
11911
- if (this.wave !== void 0) segments.push(this.wave.getFrameText());
11912
- if (showLabel && this.label.length > 0) segments.push(this.labelColor === void 0 ? this.label : chalk.hex(this.labelColor)(this.label));
11913
- for (const chip of [reminder, activity]) {
11914
- if (chip === null) continue;
11915
- segments.push(chip.color === void 0 ? chip.text : chalk.hex(chip.color)(chip.text));
11892
+ const waveText = this.wave !== void 0 ? this.wave.getFrameText() : null;
11893
+ const paintChip = (chip) => {
11894
+ if (chip === null) return null;
11895
+ return chip.color === void 0 ? chip.text : chalk.hex(chip.color)(chip.text);
11896
+ };
11897
+ const reminderText = paintChip(reminder);
11898
+ const activityText = paintChip(activity);
11899
+ const noticeText = paintChip(notice);
11900
+ let labelText = showLabel && this.label.length > 0 ? this.labelColor === void 0 ? this.label : chalk.hex(this.labelColor)(this.label) : null;
11901
+ if (labelText !== null && noticeText !== null) {
11902
+ const fixedSegments = [
11903
+ waveText,
11904
+ reminderText,
11905
+ activityText,
11906
+ noticeText
11907
+ ].filter((segment) => segment !== null);
11908
+ const room = minWidth - (1 + fixedSegments.reduce((total, segment) => total + visibleWidth(segment), 0) + fixedSegments.length * 2);
11909
+ labelText = room >= 2 ? truncateToWidth(labelText, room, "…") : null;
11916
11910
  }
11911
+ const segments = [];
11912
+ if (waveText !== null) segments.push(waveText);
11913
+ if (labelText !== null && labelText.length > 0) segments.push(labelText);
11914
+ for (const chipText of [
11915
+ reminderText,
11916
+ activityText,
11917
+ noticeText
11918
+ ]) if (chipText !== null) segments.push(chipText);
11917
11919
  if (segments.length === 0) return [];
11918
11920
  if (this.wave !== void 0 && segments.length === 1) return [truncateToWidth(segments[0], minWidth)];
11919
11921
  return [truncateToWidth(` ${segments.join(" ")}`, minWidth)];
@@ -11942,11 +11944,11 @@ var StatusBarPaneComponent = class extends Container {
11942
11944
  update(options) {
11943
11945
  this.clear();
11944
11946
  if (options.mode === "idle") {
11945
- if (options.label.length > 0 || options.reminder !== void 0 || options.activity !== void 0) this.addChild(new StatusLine(void 0, options.label, options.labelColor, options.reminder, options.activity, true));
11947
+ if (options.label.length > 0 || options.reminder !== void 0 || options.activity !== void 0 || options.notice !== void 0) this.addChild(new StatusLine(void 0, options.label, options.labelColor, options.reminder, options.activity, options.notice, true));
11946
11948
  return;
11947
11949
  }
11948
11950
  if (options.pulseWave !== void 0) {
11949
- this.addChild(new StatusLine(options.pulseWave, options.label, options.labelColor, options.reminder, options.activity));
11951
+ this.addChild(new StatusLine(options.pulseWave, options.label, options.labelColor, options.reminder, options.activity, options.notice));
11950
11952
  return;
11951
11953
  }
11952
11954
  if (options.label.length > 0) {
@@ -13070,6 +13072,11 @@ const COLLAB_ACTIVITY_TTL_MS = 8e3;
13070
13072
  /** How long a scheduled-task reminder stays: longer than the collaboration
13071
13073
  * chip because a cron fire is time-sensitive and must not slip by. */
13072
13074
  const REMINDER_ACTIVITY_TTL_MS = 1e4;
13075
+ /** How long an MCP server notice stays on the status bar (own slot, transient
13076
+ * by design — startup connect chatter must not occupy the transcript).
13077
+ * Short on purpose: the notice appears once loading finishes and clears
13078
+ * quickly instead of parking on the row. */
13079
+ const MCP_ACTIVITY_TTL_MS = 3e3;
13073
13080
  /** Footer git badge freshness window; the deferred read refreshes past it. */
13074
13081
  const GIT_DIFF_TTL_MS = 15e3;
13075
13082
  /** Window after a command-driven line write during which the poll stands down. */
@@ -13106,6 +13113,11 @@ var LifecycleController = class LifecycleController {
13106
13113
  * a collaboration request can never evict a fired reminder. */
13107
13114
  reminderActivity = null;
13108
13115
  reminderTimer;
13116
+ /** Single-slot MCP server notice (connect / connecting / disabled). Own slot
13117
+ * for the same reason: a burst of server statuses must never evict a
13118
+ * collaboration request or a fired reminder. */
13119
+ mcpActivity = null;
13120
+ mcpTimer;
13109
13121
  /** Sum all four token buckets into the session's total token consumption. */
13110
13122
  static sumTokens(usage) {
13111
13123
  return usage.inputOther + usage.inputCacheRead + usage.inputCacheCreation + usage.output;
@@ -13574,7 +13586,8 @@ var LifecycleController = class LifecycleController {
13574
13586
  labelColor: state.theme.colors.warning,
13575
13587
  pulseWave: loader,
13576
13588
  reminder: () => this.getReminderActivity(),
13577
- activity: () => this.getCollabActivity()
13589
+ activity: () => this.getCollabActivity(),
13590
+ notice: () => this.getMcpActivity()
13578
13591
  }));
13579
13592
  }
13580
13593
  break;
@@ -13587,7 +13600,8 @@ var LifecycleController = class LifecycleController {
13587
13600
  label: tip === void 0 ? "" : composeTipLabel(t(tip.i18nKey), tip, state.theme.colors.primary),
13588
13601
  labelColor: state.theme.colors.textDim,
13589
13602
  reminder: () => this.getReminderActivity(),
13590
- activity: () => this.getCollabActivity()
13603
+ activity: () => this.getCollabActivity(),
13604
+ notice: () => this.getMcpActivity()
13591
13605
  }));
13592
13606
  if (tip !== void 0) this.startTipRotation();
13593
13607
  break;
@@ -13673,6 +13687,29 @@ var LifecycleController = class LifecycleController {
13673
13687
  getReminderActivity() {
13674
13688
  return this.reminderActivity;
13675
13689
  }
13690
+ /**
13691
+ * Shows an MCP server notice on the status row (connected / connecting /
13692
+ * disabled). Single slot, newest wins, expires after `MCP_ACTIVITY_TTL_MS`;
13693
+ * the row reads it live, so no rebuild (and no wave reset) is involved.
13694
+ * The colour is chosen by the caller (success / muted).
13695
+ */
13696
+ showMcpActivity(text, color) {
13697
+ this.mcpActivity = {
13698
+ text,
13699
+ color
13700
+ };
13701
+ if (this.mcpTimer !== void 0) clearTimeout(this.mcpTimer);
13702
+ this.mcpTimer = setTimeout(() => {
13703
+ this.mcpTimer = void 0;
13704
+ this.mcpActivity = null;
13705
+ this.host.state.ui?.requestRender();
13706
+ }, MCP_ACTIVITY_TTL_MS);
13707
+ this.host.state.ui?.requestRender();
13708
+ }
13709
+ /** Live getter consumed by the status row on every render. */
13710
+ getMcpActivity() {
13711
+ return this.mcpActivity;
13712
+ }
13676
13713
  /** Drops every transient chip (session switch, shutdown). */
13677
13714
  clearStatusActivity() {
13678
13715
  if (this.collabTimer !== void 0) {
@@ -13685,6 +13722,11 @@ var LifecycleController = class LifecycleController {
13685
13722
  this.reminderTimer = void 0;
13686
13723
  }
13687
13724
  this.reminderActivity = null;
13725
+ if (this.mcpTimer !== void 0) {
13726
+ clearTimeout(this.mcpTimer);
13727
+ this.mcpTimer = void 0;
13728
+ }
13729
+ this.mcpActivity = null;
13688
13730
  }
13689
13731
  /**
13690
13732
  * Stops every animation this controller owns. Called from the shutdown path:
@@ -19872,7 +19914,6 @@ var ScreamTUI = class {
19872
19914
  clearRuntimeMarker(getDataDir());
19873
19915
  await this.closeSession();
19874
19916
  await this.harness.close();
19875
- this.sessionEventHandler.stopAllMcpServerStatusSpinners();
19876
19917
  this.state.ui.stop();
19877
19918
  if (this.onExit) await this.onExit(exitCode);
19878
19919
  }
@@ -20049,6 +20090,10 @@ var ScreamTUI = class {
20049
20090
  showReminderActivity(text, tone) {
20050
20091
  this.lifecycleController.showReminderActivity(text, tone);
20051
20092
  }
20093
+ /** MCP server notice above the editor (connecting / connected / disabled). */
20094
+ showMcpActivity(text, color) {
20095
+ this.lifecycleController.showMcpActivity(text, color);
20096
+ }
20052
20097
  /** Drops every transient chip on the status row (session switch). */
20053
20098
  clearStatusActivity() {
20054
20099
  this.lifecycleController.clearStatusActivity();
@@ -20202,7 +20247,7 @@ var ScreamTUI = class {
20202
20247
  const card = new OnboardingCardComponent({
20203
20248
  colors: this.state.theme.colors,
20204
20249
  onConfigure: () => {
20205
- import("./dispatch-DNYdiEo4.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
20250
+ import("./dispatch-DD8Vu4Qc.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
20206
20251
  this.restoreEditor();
20207
20252
  onDone?.();
20208
20253
  });
@@ -20264,7 +20309,6 @@ var ScreamTUI = class {
20264
20309
  this.mountEditorReplacement(card);
20265
20310
  }
20266
20311
  clearTranscriptAndRedraw() {
20267
- this.sessionEventHandler.stopAllMcpServerStatusSpinners();
20268
20312
  clearGoalState();
20269
20313
  clearInfoPanelState(this.state);
20270
20314
  clearEvalPanelState(this.state);
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
3
3
  import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
- import { Qt as handleConnectCommand } from "./dispatch-XPKo0cQR.mjs";
6
+ import { Qt as handleConnectCommand } from "./dispatch-DMntc47e.mjs";
7
7
  export { handleConnectCommand };