scream-code 0.15.2 → 0.15.4

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
@@ -139,7 +139,7 @@ Website: [scream.chat](https://scream.chat)
139
139
 
140
140
  ### Contributing
141
141
 
142
- Free and open — forks, feedback and pull requests are welcome. Scream is a lightweight agent foundation: maximize the model's own capability, don't over-constrain it with framework. Inspired by pi, pi-tui, gork, kimicli, Gemini, ohmypi, zero and others.
142
+ Free and open — forks, feedback and pull requests are welcome. Scream is a lightweight agent foundation: maximize the model's own capability, don't over-constrain it with framework. Inspired by pi, pi-tui, gork, kimicli, Gemini, ohmypi, and others.
143
143
 
144
144
  ---
145
145
 
@@ -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 isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-B_ViYZer.mjs";
8
- import { a as setLocale, o as t } from "./text-input-dialog-BUXAKycx.mjs";
7
+ import { $t as isTodoItemShape, A as renderDiffLinesClustered, An as getInputHistoryFile, At as ENABLE_TERMINAL_FOCUS_REPORTING, B as BackgroundAgentStatusComponent, Bn as ScreamHarness, Bt as TERMINAL_THEME_LIGHT, Cn as PULSE_WAVE_FRAMES, Ct as createThemeStyles, D as toggleEmptySessionHint, Dn as saveTuiConfig, Dt as parseOsc11BackgroundTheme, E as isTurnElapsedEnabled, En as loadTuiConfig, Et as detectTerminalTheme, F as estimateTokens, Fn as CLI_USER_AGENT_PRODUCT, Ft as OSC11_RESPONSE_PREFIX_NO_ESC, G as getBreathingFrame, Gn as log, H as AgentGroupComponent, Hn as resolveScreamHome, Ht as isStreaming, I as getSharedSpeedTracker, In as PRODUCT_NAME, It as QUERY_TERMINAL_THEME, J as refineGoal, Jn as isOrphanedToolCallError, Jt as printableChar, K as resetBreathingClock, Kn as resolveGlobalLogPath, L as SkillActivationComponent, Ln as DEFAULT_CATALOG_URL, Lt as TERMINAL_FOCUS_IN, M as langFromPath, Mn as detectInstallSource, Mt as OSC11_QUERY, N as CachedContainer, Nn as CLI_COMMAND_NAME, Nt as OSC11_RESPONSE, O as ToolCallComponent, On as detectShellEnvironment, Ot as DISABLE_TERMINAL_FOCUS_REPORTING, P as ThinkingComponent, Pn as CLI_UI_MODE, Pt as OSC11_RESPONSE_PREFIX, Qt as formatErrorMessage, R as ReadGroupComponent, Rn as fetchCatalog, Rt as TERMINAL_FOCUS_OUT, S as getDaemonInstructions, Sn as PIXEL_PULSE_FRAMES, St as createMarkdownTheme, T as isEmptySessionHintDismissed, Tn as TuiLikePreferencesSchema, Tt as getColorPalette, U as WelcomeComponent, Un as MemoryMemoStore, Ut as FooterComponent, V as AssistantMessageComponent, Vt as isBusy, W as BREATHE_CYCLE_MS, Wn as flushDiagnosticLogs, Xn as SCREAM_ERROR_INFO, Xt as appendStreamingArgsPreview, Yn as ErrorCodes, Yt as STATUS_BULLET, Zt as argsRecord, _ as refreshUpdateCache, _n as isExperimentalFlagEnabled, _t as clearInfoPanelState, an as EMPTY_SESSION_HINT_URL, b as readJsonlFile, bn as isEmbeddingModelCached, bt as resolveThemeSync, c as hasDispose, cn as SESSION_TIPS, ct as handlePlanCommand, d as formatMemoryMemoForInjection, dn as getCtrlDHint, en as parseStreamingArgs, et as toTerminalHyperlink, fn as getLlmNotSetMessage, g as selectUpdateTarget, gn as sortSlashCommands, gt as supportsBalance, hn as BUILTIN_SLASH_COMMANDS, ht as refreshProviderBalance, i as buildRoleAdditionalText, in as CHARS_PER_TOKEN, j as highlightLines, jn as getLogDir, jt as ENABLE_TERMINAL_THEME_REPORTING, k as renderDiffLines, kn as getDataDir, kt as DISABLE_TERMINAL_THEME_REPORTING, l as isPlanExpandable, ln as TIP_ROTATION_INTERVAL_MS, mn as buildSkillSlashCommands, n as clearEvalPanelState, nn as stringValue, nt as getModelCycleLevel, on as EXIT_CONFIRM_WINDOW_MS, ot as handleFusionPlanCommand, pn as getNoActiveSessionMessage, q as clearGoalState, qn as isScreamError, r as openUrl, rn as truncateErrorMessage, s as disposeChildren, sn as MAIN_AGENT_ID, t as dispatchInput, tn as serializeToolResultOutput, tt as changeThinkingLevel, u as MoonLoader, un as getCtrlCHint, v as readUpdateCache, vn as setExperimentalFlags, w as UserMessageComponent, wn as TuiConfigParseError, wt as contrastTextHex, xn as startManualEmbeddingDownload, xt as createEditorTheme, y as appendJsonlLine, yn as getKnowledgeStore, z as parseReadGroupOutput, zn as saveCatalogCache, zt as TERMINAL_THEME_DARK } from "./dispatch-m6mYTI1M.mjs";
8
+ import { a as setLocale, o as t } from "./text-input-dialog-B2a6Ks8f.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.2"),
35
+ version: optionalBuildString("0.15.4"),
36
36
  channel: optionalBuildString(""),
37
37
  commit: optionalBuildString(""),
38
38
  buildTarget: optionalBuildString("darwin-arm64")
@@ -2338,6 +2338,10 @@ var SessionEventHandler = class {
2338
2338
  */
2339
2339
  lastCompactionTrigger;
2340
2340
  backgroundTaskTranscriptedTerminal = /* @__PURE__ */ new Set();
2341
+ /** Active foreground (non-background) subagents. Footer badge counts them
2342
+ * separately from background agents: foreground subagents are transient
2343
+ * (spawned by the current turn's Agent tool), background agents persist. */
2344
+ foregroundSubagentCount = 0;
2341
2345
  subagentInfo = /* @__PURE__ */ new Map();
2342
2346
  renderedSkillActivationIds = /* @__PURE__ */ new Set();
2343
2347
  renderedMcpServerStatusKeys = /* @__PURE__ */ new Map();
@@ -3096,6 +3100,8 @@ var SessionEventHandler = class {
3096
3100
  this.syncBackgroundAgentBadge();
3097
3101
  return;
3098
3102
  }
3103
+ this.foregroundSubagentCount += 1;
3104
+ this.syncBackgroundAgentBadge();
3099
3105
  let tc = streamingUI.getToolComponent(event.parentToolCallId);
3100
3106
  if (tc === void 0) {
3101
3107
  const toolCall = streamingUI.getActiveToolCall(event.parentToolCallId);
@@ -3137,6 +3143,8 @@ var SessionEventHandler = class {
3137
3143
  this.appendBackgroundAgentEntry("completed", backgroundMeta, extras);
3138
3144
  return;
3139
3145
  }
3146
+ this.foregroundSubagentCount = Math.max(0, this.foregroundSubagentCount - 1);
3147
+ this.syncBackgroundAgentBadge();
3140
3148
  const routingId = this.subagentInfo.get(event.subagentId)?.parentToolCallId ?? event.parentToolCallId;
3141
3149
  const tc = streamingUI.getToolComponent(routingId);
3142
3150
  if (tc === void 0) return;
@@ -3166,6 +3174,8 @@ var SessionEventHandler = class {
3166
3174
  this.appendBackgroundAgentEntry("failed", backgroundMeta, { error: event.error });
3167
3175
  return;
3168
3176
  }
3177
+ this.foregroundSubagentCount = Math.max(0, this.foregroundSubagentCount - 1);
3178
+ this.syncBackgroundAgentBadge();
3169
3179
  const tc = streamingUI.getToolComponent(this.subagentInfo.get(event.subagentId)?.parentToolCallId ?? event.parentToolCallId);
3170
3180
  if (tc === void 0) return;
3171
3181
  tc.onSubagentFailed({ error: event.error });
@@ -3295,11 +3305,12 @@ var SessionEventHandler = class {
3295
3305
  if (info.taskId.startsWith("agent-")) agentTasks += 1;
3296
3306
  else bashTasks += 1;
3297
3307
  }
3298
- state.footer.setBackgroundCounts({
3308
+ state.footer?.setBackgroundCounts({
3299
3309
  bashTasks,
3300
- agentTasks
3310
+ agentTasks,
3311
+ foregroundSubagents: this.foregroundSubagentCount
3301
3312
  });
3302
- state.ui.requestRender();
3313
+ state.ui?.requestRender();
3303
3314
  }
3304
3315
  };
3305
3316
  //#endregion
@@ -3605,7 +3616,10 @@ var SessionReplayRenderer = class {
3605
3616
  sessionEventHandler.backgroundTasks = new Map(agent.background.map((info) => [info.taskId, info]));
3606
3617
  sessionEventHandler.backgroundTaskTranscriptedTerminal.clear();
3607
3618
  for (const info of agent.background) if (isTerminalBackgroundTask(info)) sessionEventHandler.backgroundTaskTranscriptedTerminal.add(info.taskId);
3608
- state.footer.setBackgroundCounts(countActiveBackgroundTasks(sessionEventHandler.backgroundTasks));
3619
+ state.footer.setBackgroundCounts({
3620
+ ...countActiveBackgroundTasks(sessionEventHandler.backgroundTasks),
3621
+ foregroundSubagents: 0
3622
+ });
3609
3623
  state.ui.requestRender();
3610
3624
  }
3611
3625
  renderRecords(agent) {
@@ -6553,27 +6567,34 @@ function installTerminalThemeTracking(state, onTheme) {
6553
6567
  }
6554
6568
  //#endregion
6555
6569
  //#region src/tui/components/chrome/pulse-wave-loader.ts
6556
- const FULL_BOX = "■";
6557
- const DIM_DOT = "⬝";
6570
+ /** Leading/trailing glyph: U+2588 (Block Elements — guaranteed single-column
6571
+ * width). The remaining stage cells render muted middle dots (U+00B7). */
6572
+ const LEADING_BOX = "■";
6558
6573
  /**
6559
- * 3-box pulse-wave loading indicator.
6574
+ * 8-box flowing pulse-wave loading indicator.
6560
6575
  *
6561
- * Cycles through a breathing wave pattern:
6562
- * ⬝ → ■ ⬝ → ⬝ ■ → (bounce back)
6576
+ * A leading box (full primary colour) sweeps across an 8-cell stage, trailed
6577
+ * by a dim box, over a row of muted middle dots:
6578
+ * ■ 淡 ■ · · · · · · → ■ ■ 淡 ■ · · · · → … → (bounce back)
6579
+ * (淡 shown here only to mark the trailing box; it renders as primary+dim.)
6563
6580
  *
6564
- * Colouring mirrors Grok's PromptLoadingBoxes:
6565
- * - active box (distance 0) → full primary colour
6566
- * - trailing box (distance 1) → ~72 % opacity via chalk dim
6567
- * - dim dot (distance 2) → muted
6581
+ * 14-frame ping-pong loop @ 80 ms — the leading edge never teleports, it
6582
+ * walks cell by cell.
6583
+ *
6584
+ * All glyphs are width-safe in terminal fonts (U+2588 Block Elements and
6585
+ * U+00B7 Latin-1) — the earlier ■/⬝ mix reflowed the whole line on every
6586
+ * frame because U+2B1D is not single-column guaranteed.
6568
6587
  *
6569
6588
  * The component auto-starts on construction. Call `stop()` to tear
6570
- * down the interval timer.
6589
+ * down the timer chain.
6571
6590
  */
6572
6591
  var PulseWaveLoader = class extends Text {
6573
6592
  currentFrame = 0;
6574
6593
  intervalId = null;
6575
6594
  ui;
6576
6595
  colorHex;
6596
+ /** Wall-clock time of the last frame advance, for drift-free scheduling. */
6597
+ lastTickAt = 0;
6577
6598
  /** Raw frame text without Text's full-width render padding, exposed so
6578
6599
  * callers can compose the wave inline with trailing content (e.g. the
6579
6600
  * reconnect label in the status bar). */
@@ -6589,39 +6610,60 @@ var PulseWaveLoader = class extends Text {
6589
6610
  this.start();
6590
6611
  }
6591
6612
  start() {
6613
+ this.lastTickAt = performance.now();
6592
6614
  this.updateDisplay();
6593
- this.intervalId = setInterval(() => {
6594
- this.currentFrame = (this.currentFrame + 1) % PULSE_WAVE_FRAMES.length;
6595
- this.updateDisplay();
6596
- }, 120);
6615
+ this.scheduleTick(80, 80);
6597
6616
  }
6598
6617
  stop() {
6599
6618
  if (this.intervalId) {
6600
- clearInterval(this.intervalId);
6619
+ clearTimeout(this.intervalId);
6601
6620
  this.intervalId = null;
6602
6621
  }
6603
6622
  }
6623
+ dispose() {
6624
+ this.stop();
6625
+ }
6626
+ /**
6627
+ * setTimeout self-rescheduling chain with drift-free frame advancement and
6628
+ * paint-cost backpressure (same pattern as the pi-tui Loader): a slow tick
6629
+ * defers the next one by `max(0, interval - cost, cost × 9)` instead of
6630
+ * stacking intervals, keeping the wave ≤ ~10% CPU under slow writes.
6631
+ */
6632
+ scheduleTick(intervalMs, delayMs) {
6633
+ const timer = setTimeout(() => {
6634
+ if (this.intervalId !== timer) return;
6635
+ const startedAt = performance.now();
6636
+ const elapsed = startedAt - this.lastTickAt;
6637
+ if (elapsed >= intervalMs) {
6638
+ const steps = Math.floor(elapsed / intervalMs);
6639
+ this.currentFrame = (this.currentFrame + steps) % PULSE_WAVE_FRAMES.length;
6640
+ this.lastTickAt += steps * intervalMs;
6641
+ this.updateDisplay();
6642
+ }
6643
+ const frameCostMs = performance.now() - startedAt;
6644
+ if (this.intervalId !== timer) return;
6645
+ const cadenceDelayMs = Math.max(0, intervalMs - frameCostMs);
6646
+ const backpressureDelayMs = frameCostMs * 9;
6647
+ this.scheduleTick(intervalMs, Math.max(cadenceDelayMs, backpressureDelayMs));
6648
+ }, delayMs);
6649
+ this.intervalId = timer;
6650
+ }
6604
6651
  setColorHex(colorHex) {
6605
6652
  this.colorHex = colorHex;
6606
6653
  this.updateDisplay();
6607
6654
  }
6608
6655
  updateDisplay() {
6609
6656
  const step = PULSE_WAVE_FRAMES[this.currentFrame] ?? PULSE_WAVE_FRAMES[0];
6610
- const cells = [
6611
- 0,
6612
- 1,
6613
- 2
6614
- ].map((idx) => this.renderCell(idx, step.active, step.forward));
6615
- this.frameText = cells.join(" ");
6657
+ const cells = Array.from({ length: 8 }, (_, idx) => this.renderCell(idx, step.active, step.forward));
6658
+ this.frameText = cells.join("");
6616
6659
  this.setText(this.frameText);
6617
6660
  this.ui.requestRender();
6618
6661
  }
6619
6662
  renderCell(index, active, forward) {
6620
6663
  const distance = forward ? active - index : index - active;
6621
- const glyph = distance >= 0 && distance < 2 ? FULL_BOX : DIM_DOT;
6622
- if (distance === 0) return chalk.hex(this.colorHex)(glyph);
6623
- if (distance === 1) return chalk.hex(this.colorHex).dim(glyph);
6624
- return chalk.dim(glyph);
6664
+ if (distance === 0) return chalk.hex(this.colorHex)(LEADING_BOX);
6665
+ if (distance === 1) return chalk.hex(this.colorHex).dim(LEADING_BOX);
6666
+ return chalk.dim("·");
6625
6667
  }
6626
6668
  };
6627
6669
  //#endregion
@@ -9713,7 +9755,8 @@ var SessionManager$1 = class {
9713
9755
  this.host.tasksBrowserController.close();
9714
9756
  this.host.state.footer.setBackgroundCounts({
9715
9757
  bashTasks: 0,
9716
- agentTasks: 0
9758
+ agentTasks: 0,
9759
+ foregroundSubagents: 0
9717
9760
  });
9718
9761
  this.host.streamingUI.setTodoList([]);
9719
9762
  this.host.streamingUI.setTurnId(void 0);
@@ -12397,7 +12440,7 @@ var ScreamTUI = class {
12397
12440
  const card = new OnboardingCardComponent({
12398
12441
  colors: this.state.theme.colors,
12399
12442
  onConfigure: () => {
12400
- import("./dispatch-Dur5jHVw.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12443
+ import("./dispatch-CULU8xVW.mjs").then(({ handleConnectCommand }) => handleConnectCommand(this, "")).finally(() => {
12401
12444
  this.restoreEditor();
12402
12445
  onDone?.();
12403
12446
  });
@@ -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 { Kt as handleConnectCommand } from "./dispatch-B_ViYZer.mjs";
6
+ import { Kt as handleConnectCommand } from "./dispatch-m6mYTI1M.mjs";
7
7
  export { handleConnectCommand };