scream-code 0.15.7 → 0.15.9

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
@@ -59,7 +59,7 @@ When Scream wants to modify files or run commands, an approval panel pops up —
59
59
  - **Session Trace** — `/trace` exports any session as a self-contained, offline interactive HTML timeline.
60
60
  - **Context Search** — `/search` (Ctrl+Shift+F) full-screen keyword search over the conversation.
61
61
  - **High Cache Hit Rate** — per-session HitR in the status bar, tuned across compatible protocols to maximize cache hits without compromising quality.
62
- - **MCP / Skills / 130+ providers** — all DIY-configurable; ships with browser (46 tools) and macOS desktop automation.
62
+ - **MCP / Skills / 130+ providers** — all DIY-configurable; ships with browser automation and macOS desktop automation.
63
63
  - **Self-Evolving Plugins** — a built-in `ManagePlugin` tool lets the agent find, make, and install add-on skills/MCP servers itself and hot-apply them to the running session: nothing executes until an approved activate, a circuit breaker reclaims a plugin after repeated failures, immune memory warns before reinstalling a source that already broke, and upgrades keep a rollback-ready backup.
64
64
  - **Remote Control** — drive Scream from WeChat, Feishu, Slack, DingTalk, QQ, Telegram, Discord via cc-connect.
65
65
 
@@ -4,8 +4,8 @@ import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
- import { $t as argsRecord, A as renderDiffLinesClustered, An as detectShellEnvironment, At as DISABLE_TERMINAL_THEME_REPORTING, B as BackgroundAgentStatusComponent, Bn as fetchCatalog, Bt as TERMINAL_THEME_DARK, Cn as startManualEmbeddingDownload, Ct as createMarkdownTheme, D as toggleEmptySessionHint, Dn as TuiLikePreferencesSchema, Dt as detectTerminalTheme, E as isTurnElapsedEnabled, En as TuiConfigParseError, Et as getColorPalette, F as estimateTokens, Fn as CLI_COMMAND_NAME, Ft as OSC11_RESPONSE_PREFIX, G as getBreathingFrame, Gn as MemoryMemoStore, Gt as lerpGradient, H as AgentGroupComponent, Hn as ScreamHarness, Ht as isBusy, I as getSharedSpeedTracker, In as CLI_UI_MODE, It as OSC11_RESPONSE_PREFIX_NO_ESC, J as refineGoal, Jn as resolveGlobalLogPath, K as resetBreathingClock, Kn as flushDiagnosticLogs, L as SkillActivationComponent, Ln as CLI_USER_AGENT_PRODUCT, Lt as QUERY_TERMINAL_THEME, M as langFromPath, Mn as getInputHistoryFile, Mt as ENABLE_TERMINAL_THEME_REPORTING, N as CachedContainer, Nn as getLogDir, Nt as OSC11_QUERY, O as ToolCallComponent, On as loadTuiConfig, Ot as parseOsc11BackgroundTheme, P as ThinkingComponent, Pn as detectInstallSource, Pt as OSC11_RESPONSE, Qn as SCREAM_ERROR_INFO, Qt as appendStreamingArgsPreview, R as ReadGroupComponent, Rn as PRODUCT_NAME, Rt as TERMINAL_FOCUS_IN, S as getDaemonInstructions, Sn as isEmbeddingModelCached, St as createEditorTheme, T as isEmptySessionHintDismissed, Tn as PULSE_WAVE_FRAMES, Tt as contrastTextHex, U as WelcomeComponent, Ut as isStreaming, V as AssistantMessageComponent, Vn as saveCatalogCache, Vt as TERMINAL_THEME_LIGHT, W as BREATHE_CYCLE_MS, Wn as resolveScreamHome, Wt as FooterComponent, Xn as isOrphanedToolCallError, Xt as printableChar, Yn as isScreamError, Zn as ErrorCodes, Zt as STATUS_BULLET, _ as refreshUpdateCache, _n as BUILTIN_SLASH_COMMANDS, _t as supportsBalance, an as truncateErrorMessage, b as readJsonlFile, bn as setExperimentalFlags, c as hasDispose, cn as EXIT_CONFIRM_WINDOW_MS, d as formatMemoryMemoForInjection, dn as TIP_ROTATION_INTERVAL_MS, en as formatErrorMessage, et as toTerminalHyperlink, fn as getCtrlCHint, g as selectUpdateTarget, gn as buildSkillSlashCommands, gt as refreshProviderBalance, hn as getNoActiveSessionMessage, i as buildRoleAdditionalText, in as stringValue, j as highlightLines, jn as getDataDir, jt as ENABLE_TERMINAL_FOCUS_REPORTING, k as renderDiffLines, kn as saveTuiConfig, kt as DISABLE_TERMINAL_FOCUS_REPORTING, l as isPlanExpandable, ln as MAIN_AGENT_ID, lt as handlePlanCommand, mn as getLlmNotSetMessage, n as clearEvalPanelState, nn as parseStreamingArgs, nt as getModelCycleLevel, on as CHARS_PER_TOKEN, pn as getCtrlDHint, q as clearGoalState, qn as log, r as openUrl, rn as serializeToolResultOutput, s as disposeChildren, sn as EMPTY_SESSION_HINT_URL, st as handleFusionPlanCommand, t as dispatchInput, tn as isTodoItemShape, tt as changeThinkingLevel, u as MoonLoader, un as SESSION_TIPS, v as readUpdateCache, vn as sortSlashCommands, vt as clearInfoPanelState, w as UserMessageComponent, wn as PIXEL_PULSE_FRAMES, wt as createThemeStyles, xn as getKnowledgeStore, xt as resolveThemeSync, y as appendJsonlLine, yn as isExperimentalFlagEnabled, z as parseReadGroupOutput, zn as DEFAULT_CATALOG_URL, zt as TERMINAL_FOCUS_OUT } from "./dispatch-BJ6GyqMS.mjs";
8
- import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-CnQM0DYj.mjs";
7
+ import { $n as ErrorCodes, $t as argsRecord, A as renderDiffLinesClustered, An as detectShellEnvironment, At as DISABLE_TERMINAL_THEME_REPORTING, B as BackgroundAgentStatusComponent, Bn as fetchCatalog, Bt as TERMINAL_THEME_DARK, Cn as startManualEmbeddingDownload, Ct as createMarkdownTheme, D as toggleEmptySessionHint, Dn as TuiLikePreferencesSchema, Dt as detectTerminalTheme, E as isTurnElapsedEnabled, En as TuiConfigParseError, Et as getColorPalette, F as estimateTokens, Fn as CLI_COMMAND_NAME, Ft as OSC11_RESPONSE_PREFIX, G as getBreathingFrame, Gt as lerpGradient, H as AgentGroupComponent, Hn as ScreamHarness, Ht as isBusy, I as getSharedSpeedTracker, In as CLI_UI_MODE, It as OSC11_RESPONSE_PREFIX_NO_ESC, J as refineGoal, Jn as flushDiagnosticLogs, K as resetBreathingClock, Kn as resolveScreamHome, L as SkillActivationComponent, Ln as CLI_USER_AGENT_PRODUCT, Lt as QUERY_TERMINAL_THEME, M as langFromPath, Mn as getInputHistoryFile, Mt as ENABLE_TERMINAL_THEME_REPORTING, N as CachedContainer, Nn as getLogDir, Nt as OSC11_QUERY, O as ToolCallComponent, On as loadTuiConfig, Ot as parseOsc11BackgroundTheme, P as ThinkingComponent, Pn as detectInstallSource, Pt as OSC11_RESPONSE, Qn as isOrphanedToolCallError, Qt as appendStreamingArgsPreview, R as ReadGroupComponent, Rn as PRODUCT_NAME, Rt as TERMINAL_FOCUS_IN, S as getDaemonInstructions, Sn as isEmbeddingModelCached, St as createEditorTheme, T as isEmptySessionHintDismissed, Tn as PULSE_WAVE_FRAMES, Tt as contrastTextHex, U as WelcomeComponent, Un as encodeWorkDirKey, Ut as isStreaming, V as AssistantMessageComponent, Vn as saveCatalogCache, Vt as TERMINAL_THEME_LIGHT, W as BREATHE_CYCLE_MS, Wn as appendSessionIndexEntry, Wt as FooterComponent, Xn as resolveGlobalLogPath, Xt as printableChar, Yn as log, Zn as isScreamError, Zt as STATUS_BULLET, _ as refreshUpdateCache, _n as BUILTIN_SLASH_COMMANDS, _t as supportsBalance, an as truncateErrorMessage, b as readJsonlFile, bn as setExperimentalFlags, c as hasDispose, cn as EXIT_CONFIRM_WINDOW_MS, d as formatMemoryMemoForInjection, dn as TIP_ROTATION_INTERVAL_MS, en as formatErrorMessage, er as SCREAM_ERROR_INFO, et as toTerminalHyperlink, fn as getCtrlCHint, g as selectUpdateTarget, gn as buildSkillSlashCommands, gt as refreshProviderBalance, hn as getNoActiveSessionMessage, i as buildRoleAdditionalText, in as stringValue, j as highlightLines, jn as getDataDir, jt as ENABLE_TERMINAL_FOCUS_REPORTING, k as renderDiffLines, kn as saveTuiConfig, kt as DISABLE_TERMINAL_FOCUS_REPORTING, l as isPlanExpandable, ln as MAIN_AGENT_ID, lt as handlePlanCommand, mn as getLlmNotSetMessage, n as clearEvalPanelState, nn as parseStreamingArgs, nt as getModelCycleLevel, on as CHARS_PER_TOKEN, pn as getCtrlDHint, q as clearGoalState, qn as MemoryMemoStore, r as openUrl, rn as serializeToolResultOutput, s as disposeChildren, sn as EMPTY_SESSION_HINT_URL, st as handleFusionPlanCommand, t as dispatchInput, tn as isTodoItemShape, tt as changeThinkingLevel, u as MoonLoader, un as SESSION_TIPS, v as readUpdateCache, vn as sortSlashCommands, vt as clearInfoPanelState, w as UserMessageComponent, wn as PIXEL_PULSE_FRAMES, wt as createThemeStyles, xn as getKnowledgeStore, xt as resolveThemeSync, y as appendJsonlLine, yn as isExperimentalFlagEnabled, z as parseReadGroupOutput, zn as DEFAULT_CATALOG_URL, zt as TERMINAL_FOCUS_OUT } from "./dispatch-D8ot3OUd.mjs";
8
+ import { a as setLocale, i as getLocale, o as t } from "./text-input-dialog-Dx7LJTG6.mjs";
9
9
  import { createRequire } from "node:module";
10
10
  import { randomBytes, randomUUID, scryptSync, timingSafeEqual } from "node:crypto";
11
11
  import { access, chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
@@ -32,7 +32,7 @@ function optionalBuildString(value) {
32
32
  return typeof value === "string" && value.length > 0 ? value : void 0;
33
33
  }
34
34
  const SCREAM_BUILD_INFO = {
35
- version: optionalBuildString("0.15.7"),
35
+ version: optionalBuildString("0.15.9"),
36
36
  channel: optionalBuildString(""),
37
37
  commit: optionalBuildString(""),
38
38
  buildTarget: optionalBuildString("darwin-arm64")
@@ -2913,10 +2913,12 @@ var SessionEventHandler = class {
2913
2913
  this.host.streamingUI.flushNow();
2914
2914
  this.maybeShowDebugTiming(event);
2915
2915
  this.drainQueuedMessagesIntoSteer();
2916
- if (event.usage !== void 0) {
2917
- const prev = this.host.state.appState.sessionUsage;
2918
- this.host.setAppState({ sessionUsage: prev === void 0 ? event.usage : addTokenUsage(prev, event.usage) });
2919
- }
2916
+ const prev = this.host.state.appState.sessionUsage;
2917
+ const apiCalls = this.host.state.appState.sessionApiCalls + 1;
2918
+ this.host.setAppState(event.usage === void 0 ? { sessionApiCalls: apiCalls } : {
2919
+ sessionUsage: prev === void 0 ? event.usage : addTokenUsage(prev, event.usage),
2920
+ sessionApiCalls: apiCalls
2921
+ });
2920
2922
  if (event.finishReason !== "max_tokens") return;
2921
2923
  const title = this.host.streamingUI.markStepTruncated(String(event.turnId), event.step) > 0 ? t("handler.max_tokens_truncated") : t("handler.max_tokens_no_tool");
2922
2924
  const detail = this.isAnthropicSessionActive() ? t("handler.max_tokens_hint") : void 0;
@@ -2930,7 +2932,8 @@ var SessionEventHandler = class {
2930
2932
  reconnectMaxAttempts: event.maxAttempts,
2931
2933
  reconnectDelayMs: event.delayMs,
2932
2934
  reconnectStatusCode: event.statusCode,
2933
- reconnectErrorName: event.errorName
2935
+ reconnectErrorName: event.errorName,
2936
+ sessionApiCalls: this.host.state.appState.sessionApiCalls + 1
2934
2937
  });
2935
2938
  }
2936
2939
  maybeShowDebugTiming(event) {
@@ -6709,6 +6712,24 @@ function displayWidth(text) {
6709
6712
  function padLabel(label, target) {
6710
6713
  return label + " ".repeat(Math.max(0, target - displayWidth(label)));
6711
6714
  }
6715
+ /** Pad `value` on the LEFT to `target` display columns (CJK-aware), which is
6716
+ * what right-aligning a numeric column means in terminal space. */
6717
+ function padValue(value, target) {
6718
+ return " ".repeat(Math.max(0, target - displayWidth(value))) + value;
6719
+ }
6720
+ function alignMetricRows(rows, opts = {}) {
6721
+ const gap = opts.gap ?? 2;
6722
+ const labels = [...rows.map(([label]) => label), ...opts.extraLabels ?? []];
6723
+ const labelCols = labels.length === 0 ? 0 : Math.max(...labels.map((label) => displayWidth(label)));
6724
+ const measured = rows.length === 0 ? 0 : Math.max(...rows.map(([, value]) => displayWidth(value)));
6725
+ const valueCols = opts.maxValueCols === void 0 ? measured : Math.min(measured, Math.max(0, opts.maxValueCols));
6726
+ const separator = " ".repeat(Math.max(0, gap));
6727
+ return {
6728
+ labelCols,
6729
+ valueCols,
6730
+ rows: rows.map(([label, value]) => [padLabel(label, labelCols) + separator, padValue(truncateToWidth(value, valueCols), valueCols)])
6731
+ };
6732
+ }
6712
6733
  //#endregion
6713
6734
  //#region src/tui/components/sidebar/panels/session-panel.ts
6714
6735
  function formatDuration(ms) {
@@ -6730,10 +6751,31 @@ function formatCount$1(n) {
6730
6751
  return n.toLocaleString("en-US");
6731
6752
  }
6732
6753
  /**
6733
- * Session summary, live-rendered each frame: total token consumption and
6734
- * total working time as the primary rows, turn/tool/message/compaction
6735
- * counters as the secondary row. Uptime derives from `startedAt` so it ticks
6736
- * between data updates; counts come from the snapshot in getData().
6754
+ * Compact token form for narrow sidebars: 2,267,680,102 `22.7亿` (zh) or
6755
+ * `2.3B` (en). Used only when the thousands-separated form cannot fit, so a
6756
+ * narrow sidebar keeps every value readable instead of cutting digits.
6757
+ */
6758
+ function formatCompact(n) {
6759
+ if (getLocale() === "zh") {
6760
+ if (n >= 9995e4) return `${(n / 1e8).toFixed(1)}亿`;
6761
+ if (n >= 9500) return `${Math.round(n / 1e4)}万`;
6762
+ return String(n);
6763
+ }
6764
+ if (n >= 9995e5) return `${(n / 1e9).toFixed(1)}B`;
6765
+ if (n >= 999500) return `${(n / 1e6).toFixed(1)}M`;
6766
+ if (n >= 1e3) return `${Math.round(n / 1e3)}K`;
6767
+ return String(n);
6768
+ }
6769
+ /** True when a two-column grid fits `width` display columns. */
6770
+ function gridFits(rows, width) {
6771
+ const grid = alignMetricRows(rows);
6772
+ return grid.labelCols + 2 + grid.valueCols <= width;
6773
+ }
6774
+ /**
6775
+ * Session summary, live-rendered each frame as a strict two-column table:
6776
+ * one field per row, labels sharing a left column, values sharing a right
6777
+ * edge. Uptime derives from `startedAt` so it ticks between data updates;
6778
+ * every other value comes from the snapshot in getData().
6737
6779
  */
6738
6780
  var SessionPanelContent = class {
6739
6781
  ctx;
@@ -6749,19 +6791,25 @@ var SessionPanelContent = class {
6749
6791
  const label = (s) => chalk.hex(this.colors.textDim)(s);
6750
6792
  const value = (s) => chalk.hex(this.colors.text)(s);
6751
6793
  const now = Date.now();
6752
- const rows = [
6753
- [t("sidebar.tokens"), formatCount$1(stats.tokensTotal)],
6754
- [t("sidebar.tokens_cache_hit"), formatCount$1(stats.tokensInputCacheHit)],
6755
- [t("sidebar.tokens_cache_miss"), formatCount$1(stats.tokensInputCacheMiss)],
6756
- [t("sidebar.tokens_output"), formatCount$1(stats.tokensOutput)],
6757
- [t("sidebar.uptime"), formatDuration(now - stats.startedAt)]
6794
+ const tokenRows = [
6795
+ [t("sidebar.tokens"), stats.tokensTotal],
6796
+ [t("sidebar.tokens_cache_hit"), stats.tokensInputCacheHit],
6797
+ [t("sidebar.tokens_cache_miss"), stats.tokensInputCacheMiss],
6798
+ [t("sidebar.tokens_output"), stats.tokensOutput]
6758
6799
  ];
6759
- const maxCols = Math.max(...rows.map(([k]) => displayWidth(k)));
6760
- const lines = [];
6761
- for (const [k, v] of rows) lines.push(label(padLabel(k, maxCols)) + value(v));
6762
- lines.push(truncateToWidth(chalk.hex(this.colors.textDim)(`${t("sidebar.turns")} ${stats.turns} · ${t("sidebar.tools")} ${stats.toolCalls}`), width));
6763
- lines.push(truncateToWidth(chalk.hex(this.colors.textDim)(`${t("sidebar.messages")} ${formatCount$1(stats.messages)} · ${t("sidebar.compacts")} ${stats.compactions}`), width));
6764
- return lines;
6800
+ const countRows = [
6801
+ [t("sidebar.uptime"), formatDuration(now - stats.startedAt)],
6802
+ [t("sidebar.turns"), formatCount$1(stats.turns)],
6803
+ [t("sidebar.tools"), formatCount$1(stats.toolCalls)],
6804
+ [t("sidebar.calls"), formatCount$1(stats.apiCalls)],
6805
+ [t("sidebar.compacts"), formatCount$1(stats.compactions)]
6806
+ ];
6807
+ const withTokens = (compact) => [...tokenRows.map(([name, n]) => [name, compact ? formatCompact(n) : formatCount$1(n)]), ...countRows];
6808
+ const full = withTokens(false);
6809
+ const rows = gridFits(full, width) ? full : withTokens(true);
6810
+ const probe = alignMetricRows(rows);
6811
+ const budget = Math.max(1, width - probe.labelCols - 2);
6812
+ return (probe.valueCols <= budget ? probe : alignMetricRows(rows, { maxValueCols: budget })).rows.map(([k, v]) => label(k) + value(v));
6765
6813
  }
6766
6814
  };
6767
6815
  const sessionPanel = {
@@ -6890,9 +6938,17 @@ var GoalPanelContent = class {
6890
6938
  [t("sidebar.goal_wall"), formatWallClock(wallMs)],
6891
6939
  [t("sidebar.goal_judge"), judgeText(goal.judge)]
6892
6940
  ];
6893
- const maxCols = Math.max(...rows.map(([k]) => displayWidth(k)));
6894
- for (const [label, value] of rows) lines.push(dim(padLabel(label, maxCols)) + text(value));
6895
- if (goal.completionCriterion !== null) lines.push(dim(padLabel(t("sidebar.goal_criterion"), maxCols)) + truncateToWidth(text(goal.completionCriterion), Math.max(4, width - maxCols)));
6941
+ const hasCriterion = goal.completionCriterion !== null;
6942
+ const criterionLabel = t("sidebar.goal_criterion");
6943
+ const extraLabels = hasCriterion ? [criterionLabel] : [];
6944
+ const probe = alignMetricRows(rows, { extraLabels });
6945
+ const budget = Math.max(4, width - probe.labelCols - 2);
6946
+ const grid = probe.valueCols <= budget ? probe : alignMetricRows(rows, {
6947
+ extraLabels,
6948
+ maxValueCols: budget
6949
+ });
6950
+ for (const [label, value] of grid.rows) lines.push(dim(label) + text(value));
6951
+ if (hasCriterion) lines.push(dim(padLabel(criterionLabel, grid.labelCols) + " ".repeat(2)) + truncateToWidth(text(goal.completionCriterion), budget));
6896
6952
  return lines;
6897
6953
  }
6898
6954
  /** State badge: judging wins over everything; otherwise map the snapshot
@@ -7740,7 +7796,7 @@ var LifecycleController = class LifecycleController {
7740
7796
  const usage = appState.sessionUsage;
7741
7797
  const subagentUsage = appState.subagentUsage;
7742
7798
  const memo = this.sidebarStatsMemo;
7743
- if (memo !== void 0 && memo.length === entries.length && memo.compactions === appState.autoCompactionCount && memo.usage === usage && memo.subagentUsage === subagentUsage) return memo.stats;
7799
+ if (memo !== void 0 && memo.length === entries.length && memo.compactions === appState.autoCompactionCount && memo.usage === usage && memo.subagentUsage === subagentUsage && memo.apiCalls === appState.sessionApiCalls) return memo.stats;
7744
7800
  let turns = 0;
7745
7801
  let toolCalls = 0;
7746
7802
  for (const entry of entries) {
@@ -7752,8 +7808,8 @@ var LifecycleController = class LifecycleController {
7752
7808
  const stats = {
7753
7809
  turns,
7754
7810
  toolCalls,
7755
- messages: entries.length,
7756
7811
  compactions: appState.autoCompactionCount,
7812
+ apiCalls: appState.sessionApiCalls,
7757
7813
  tokensTotal: LifecycleController.sumTokens(usage) + subagentOutput,
7758
7814
  tokensInputCacheHit: usage.inputCacheRead,
7759
7815
  tokensInputCacheMiss: usage.inputOther + usage.inputCacheCreation,
@@ -7765,6 +7821,7 @@ var LifecycleController = class LifecycleController {
7765
7821
  compactions: appState.autoCompactionCount,
7766
7822
  usage,
7767
7823
  subagentUsage,
7824
+ apiCalls: appState.sessionApiCalls,
7768
7825
  stats
7769
7826
  };
7770
7827
  return stats;
@@ -10593,10 +10650,11 @@ const PRUNE_EMPTY_SESSION_GRACE_MS = 300 * 1e3;
10593
10650
  function isPrunableEmptySession(summary, now, graceMs = PRUNE_EMPTY_SESSION_GRACE_MS) {
10594
10651
  if (summary.archived) return false;
10595
10652
  if (summary.lastPrompt !== void 0) return false;
10596
- if (isUntitledTitle(summary.title)) return false;
10653
+ if (hasRealTitle(summary.title)) return false;
10597
10654
  return now - summary.updatedAt > graceMs;
10598
10655
  }
10599
- function isUntitledTitle(title) {
10656
+ /** True when the title is a user-meaningful title (not missing / placeholder). */
10657
+ function hasRealTitle(title) {
10600
10658
  return typeof title === "string" && title.trim().length > 0 && title !== "New Session";
10601
10659
  }
10602
10660
  /**
@@ -13046,6 +13104,7 @@ function createInitialAppState(input) {
13046
13104
  inputCacheRead: 0,
13047
13105
  inputCacheCreation: 0
13048
13106
  },
13107
+ sessionApiCalls: 0,
13049
13108
  providerBalance: null,
13050
13109
  balanceUpdatedAt: 0,
13051
13110
  isCompacting: false,
@@ -13465,6 +13524,7 @@ var ScreamTUI = class {
13465
13524
  inputCacheRead: 0,
13466
13525
  inputCacheCreation: 0
13467
13526
  };
13527
+ this.state.appState.sessionApiCalls = 0;
13468
13528
  }
13469
13529
  if (planModeChanged || sessionChanged) {
13470
13530
  this.updateEditorBorderHighlight();
@@ -13572,7 +13632,7 @@ var ScreamTUI = class {
13572
13632
  const card = new OnboardingCardComponent({
13573
13633
  colors: this.state.theme.colors,
13574
13634
  onConfigure: () => {
13575
- import("./dispatch-CPK1Lkxu.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
13635
+ import("./dispatch-CxGKRLcZ.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
13576
13636
  this.restoreEditor();
13577
13637
  onDone?.();
13578
13638
  });
@@ -15583,6 +15643,14 @@ const contentTypes = {
15583
15643
  function cloneSnapshot(value) {
15584
15644
  return structuredClone(value);
15585
15645
  }
15646
+ /**
15647
+ * Duck-typed SESSION_NOT_FOUND check: covers both same-realm ScreamError
15648
+ * instances and errors that crossed a serialization boundary carrying `code`.
15649
+ */
15650
+ function isSessionNotFoundError(error) {
15651
+ if (isScreamError(error)) return error.code === ErrorCodes.SESSION_NOT_FOUND;
15652
+ return typeof error === "object" && error !== null && error.code === ErrorCodes.SESSION_NOT_FOUND;
15653
+ }
15586
15654
  function errorMessage(error) {
15587
15655
  return error instanceof Error ? error.message : String(error);
15588
15656
  }
@@ -17328,7 +17396,20 @@ var SessionManager = class {
17328
17396
  if (!existing) return null;
17329
17397
  if (existing.isActive) return existing;
17330
17398
  const meta = existing.getMetadata();
17331
- const session = await this.harness.resumeSession({ id: meta.coreSessionId ?? meta.sessionId });
17399
+ const coreId = meta.coreSessionId ?? meta.sessionId;
17400
+ let session;
17401
+ try {
17402
+ session = await this.harness.resumeSession({ id: coreId });
17403
+ } catch (error) {
17404
+ if (!isSessionNotFoundError(error)) throw error;
17405
+ if (!await this.reindexCoreSession(coreId, meta.workDir)) return null;
17406
+ try {
17407
+ session = await this.harness.resumeSession({ id: coreId });
17408
+ } catch (retryError) {
17409
+ if (isSessionNotFoundError(retryError)) return null;
17410
+ throw retryError;
17411
+ }
17412
+ }
17332
17413
  const reactivated = new WebSession(session, {
17333
17414
  sessionId,
17334
17415
  workDir: meta.workDir,
@@ -17354,6 +17435,30 @@ var SessionManager = class {
17354
17435
  log.info("web: session reactivated", { sessionId });
17355
17436
  return reactivated;
17356
17437
  }
17438
+ /**
17439
+ * Self-heal for "meta exists but core session missing from the global
17440
+ * index": re-append the index entry when the session directory is still on
17441
+ * disk. Returns false when the directory is truly gone.
17442
+ */
17443
+ async reindexCoreSession(coreId, workDir) {
17444
+ const sessionDir = join(this.homeDir, "sessions", encodeWorkDirKey(workDir), coreId);
17445
+ if (!existsSync(sessionDir)) return false;
17446
+ try {
17447
+ await appendSessionIndexEntry(this.homeDir, {
17448
+ sessionId: coreId,
17449
+ sessionDir,
17450
+ workDir
17451
+ });
17452
+ log.info("web: re-indexed core session from web metadata", { sessionId: coreId });
17453
+ return true;
17454
+ } catch (error) {
17455
+ log.warn("web: core session reindex failed", {
17456
+ sessionId: coreId,
17457
+ error: errorMessage(error)
17458
+ });
17459
+ return false;
17460
+ }
17461
+ }
17357
17462
  get(sessionId) {
17358
17463
  return this.sessions.get(sessionId);
17359
17464
  }
@@ -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 { Jt as handleConnectCommand } from "./dispatch-BJ6GyqMS.mjs";
6
+ import { Jt as handleConnectCommand } from "./dispatch-D8ot3OUd.mjs";
7
7
  export { handleConnectCommand };