scream-code 0.12.5 → 0.12.7

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.
@@ -7,7 +7,7 @@ import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } fr
7
7
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
8
8
  import { C as join$1, D as resolve$1, E as relative$1, S as isAbsolute$1, T as parse$7, a as isSupportedFile, b as basename$1, i as ingestFile, r as ingestDirectory, t as multiSearch, w as normalize, x as dirname$2, y as KnowledgeStore } from "./src-BH9W5k24.mjs";
9
9
  import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
10
- import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-BQeV1dTM.mjs";
10
+ import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-9ywV88Lh.mjs";
11
11
  import { createRequire } from "node:module";
12
12
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
13
13
  import * as fs$1 from "node:fs/promises";
@@ -54,7 +54,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
54
54
  import { Command, Option } from "commander";
55
55
  import { createInterface } from "node:readline/promises";
56
56
  import chalk, { chalkStderr } from "chalk";
57
- import { CombinedAutocompleteProvider, Container, Editor, FlexSpacer, Image, Input, Key, Markdown, ProcessTerminal, Spacer, TUI, Text, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, getImageDimensions, isKeyRelease, matchesKey, setTightMode, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@liutod-scream/pi-tui";
57
+ import { CombinedAutocompleteProvider, Container, Editor, HStack, Image, Input, Key, Markdown, ProcessTerminal, ScrollView, Spacer, Text, TuiAltScreen, VStack, decodeKittyPrintable, deleteAllKittyImages, fuzzyFilter, fuzzyMatch, getCapabilities, getImageDimensions, isKeyRelease, matchesKey, sliceByColumn, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@liutod-scream/pi-tui";
58
58
  import { highlight, supportsLanguage } from "cli-highlight";
59
59
  import { diffWords } from "diff";
60
60
  import { gt, valid } from "semver";
@@ -5600,7 +5600,7 @@ var _BetaToolRunner_instances, _BetaToolRunner_consumed, _BetaToolRunner_mutated
5600
5600
  /**
5601
5601
  * Just Promise.withResolvers(), which is not available in all environments.
5602
5602
  */
5603
- function promiseWithResolvers() {
5603
+ function promiseWithResolvers$1() {
5604
5604
  let resolve;
5605
5605
  let reject;
5606
5606
  return {
@@ -5646,7 +5646,7 @@ var BetaToolRunner = class {
5646
5646
  ...options,
5647
5647
  headers: buildHeaders$2([{ "x-stainless-helper": helperValue }, options?.headers])
5648
5648
  }, "f");
5649
- __classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers(), "f");
5649
+ __classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers$1(), "f");
5650
5650
  if (params.compactionControl?.enabled) console.warn("Anthropic: The `compactionControl` parameter is deprecated and will be removed in a future version. Use server-side compaction instead by passing `edits: [{ type: \"compact_20260112\" }]` in the params passed to `toolRunner()`. See https://platform.claude.com/docs/en/build-with-claude/compaction");
5651
5651
  }
5652
5652
  async *[(_BetaToolRunner_consumed = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_mutated = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_state = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_options = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_message = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_toolResponse = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_completion = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_iterationCount = /* @__PURE__ */ new WeakMap(), _BetaToolRunner_instances = /* @__PURE__ */ new WeakSet(), _BetaToolRunner_checkAndCompact = async function _BetaToolRunner_checkAndCompact() {
@@ -5742,7 +5742,7 @@ var BetaToolRunner = class {
5742
5742
  __classPrivateFieldSet$1(this, _BetaToolRunner_consumed, false, "f");
5743
5743
  __classPrivateFieldGet$1(this, _BetaToolRunner_completion, "f").promise.catch(() => {});
5744
5744
  __classPrivateFieldGet$1(this, _BetaToolRunner_completion, "f").reject(error);
5745
- __classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers(), "f");
5745
+ __classPrivateFieldSet$1(this, _BetaToolRunner_completion, promiseWithResolvers$1(), "f");
5746
5746
  throw error;
5747
5747
  }
5748
5748
  }
@@ -76344,14 +76344,84 @@ function maybeStatusCode(error) {
76344
76344
  }
76345
76345
  //#endregion
76346
76346
  //#region ../../packages/agent-core/src/agent/context/projector.ts
76347
- function project(history) {
76348
- const usable = history.filter((message) => {
76347
+ /** Synthetic error text used when a tool result is missing and must be
76348
+ * filled in so the provider accepts the message sequence. */
76349
+ const SYNTHETIC_TOOL_RESULT_TEXT = "<system>ERROR: The tool call did not complete because of an interruption. Do not assume the tool executed successfully, and do not invent its result.</system>";
76350
+ function project(history, options) {
76351
+ return repairToolExchangeAdjacency(mergeAdjacentUserMessages(history.filter((message) => {
76349
76352
  return message.partial !== true && !(message.role === "assistant" && message.content.length === 0 && message.toolCalls.length === 0);
76350
- });
76351
- const last = usable.at(-1);
76352
- return mergeAdjacentUserMessages(last?.role === "assistant" && last.toolCalls.length > 0 ? usable.slice(0, -1) : usable);
76353
+ }), options?.onAnomaly), options);
76354
+ }
76355
+ /**
76356
+ * Closes every tool exchange whose assistant `tool_use` is not fully answered
76357
+ * by a matching `tool_result`. A mid-history orphan (a later user/assistant
76358
+ * message follows) can never be in-flight, so it is always closed by
76359
+ * synthesizing an error result. A trailing orphan is closed only when
76360
+ * `synthesizeMissing` is set — otherwise it is left for the trim / replay
76361
+ * synthesis. This prevents the provider error "must be followed by tool
76362
+ * messages responding to each tool_call_id" after an interruption (e.g. a
76363
+ * network drop mid-batch).
76364
+ */
76365
+ function repairToolExchangeAdjacency(messages, options) {
76366
+ let lastNonToolIndex = messages.length - 1;
76367
+ while (lastNonToolIndex >= 0 && messages[lastNonToolIndex]?.role === "tool") lastNonToolIndex -= 1;
76368
+ const out = [];
76369
+ const consumed = /* @__PURE__ */ new Set();
76370
+ for (let i = 0; i < messages.length; i++) {
76371
+ if (consumed.has(i)) continue;
76372
+ const message = messages[i];
76373
+ if (message.role !== "assistant" || message.toolCalls.length === 0) {
76374
+ out.push(message);
76375
+ continue;
76376
+ }
76377
+ out.push(message);
76378
+ const pending = new Set(message.toolCalls.map((toolCall) => toolCall.id));
76379
+ let foreignBetween = false;
76380
+ for (let j = i + 1; j < messages.length && pending.size > 0; j++) {
76381
+ if (consumed.has(j)) continue;
76382
+ const next = messages[j];
76383
+ const toolCallId = next.toolCallId;
76384
+ if (next.role === "tool" && toolCallId !== void 0 && pending.has(toolCallId)) {
76385
+ out.push(next);
76386
+ consumed.add(j);
76387
+ pending.delete(toolCallId);
76388
+ if (foreignBetween) options?.onAnomaly?.({
76389
+ kind: "tool_result_reordered",
76390
+ toolCallId
76391
+ });
76392
+ } else foreignBetween = true;
76393
+ }
76394
+ const isMidHistory = i < lastNonToolIndex;
76395
+ if (options?.synthesizeMissing === true || isMidHistory) for (const missingId of pending) {
76396
+ out.push(makeSyntheticToolResult(missingId));
76397
+ options?.onAnomaly?.({
76398
+ kind: "tool_result_synthesized",
76399
+ toolCallId: missingId,
76400
+ trailing: !isMidHistory
76401
+ });
76402
+ }
76403
+ }
76404
+ return out;
76405
+ }
76406
+ function makeSyntheticToolResult(toolCallId) {
76407
+ return {
76408
+ role: "tool",
76409
+ content: [{
76410
+ type: "text",
76411
+ text: SYNTHETIC_TOOL_RESULT_TEXT
76412
+ }],
76413
+ toolCalls: [],
76414
+ toolCallId
76415
+ };
76353
76416
  }
76354
- function mergeAdjacentUserMessages(history) {
76417
+ /**
76418
+ * Drops a trailing open tool exchange from a projected message list: when the
76419
+ * last assistant message carries tool calls whose results never arrived and
76420
+ * nothing follows, truncate from that batch so the compacted history does not
76421
+ * carry an unterminated exchange (which would be rejected or, if synthesized,
76422
+ * pollute the compaction prompt).
76423
+ */
76424
+ function mergeAdjacentUserMessages(history, _onAnomaly) {
76355
76425
  const out = [];
76356
76426
  for (const message of history) {
76357
76427
  const previous = out.at(-1);
@@ -76621,12 +76691,23 @@ function extractFileOpsFromMessage(message, ops) {
76621
76691
  }
76622
76692
  }
76623
76693
  }
76694
+ /** Projects a `FileOperations` accumulator into the persistent, sorted
76695
+ * file lists stored on `CompactionResult`. Unlike `formatFileOperations`
76696
+ * this keeps the full list (no `FILE_LIMIT` elision) so later compactions
76697
+ * can merge the previous round's file context without losing entries. */
76698
+ function computeFileLists(ops) {
76699
+ const modified = new Set([...ops.edited, ...ops.written]);
76700
+ return {
76701
+ readFiles: [...ops.read].toSorted(),
76702
+ modifiedFiles: [...modified].toSorted()
76703
+ };
76704
+ }
76624
76705
  const FILE_LIMIT = 20;
76625
76706
  function formatFileOperations(ops) {
76626
76707
  const modified = new Set([...ops.edited, ...ops.written]);
76627
- const readOnly = [...ops.read].filter((f) => !modified.has(f)).sort();
76628
- const modifiedFiles = [...modified].sort();
76629
- const all = [...new Set([...readOnly, ...modifiedFiles])].sort();
76708
+ const readOnly = [...ops.read].filter((f) => !modified.has(f)).toSorted();
76709
+ const modifiedFiles = [...modified].toSorted();
76710
+ const all = [...new Set([...readOnly, ...modifiedFiles])].toSorted();
76630
76711
  if (all.length === 0) return "";
76631
76712
  const mode = /* @__PURE__ */ new Map();
76632
76713
  for (const f of readOnly) mode.set(f, "Read");
@@ -76769,6 +76850,10 @@ var FullCompaction = class {
76769
76850
  * limit → overflow → compact cycle from consuming the entire
76770
76851
  * maxCompactionPerTurn budget with marginal savings. */
76771
76852
  reactiveAttempted = false;
76853
+ /** File lists from the most recent successful compaction. Merged into the
76854
+ * next compaction's file operations so file context accumulates across
76855
+ * repeated compactions instead of being reset each round. */
76856
+ lastCompactionFiles;
76772
76857
  compacting = null;
76773
76858
  _compactedHistory = [];
76774
76859
  strategy;
@@ -77000,15 +77085,25 @@ var FullCompaction = class {
77000
77085
  const recent = originalHistory.slice(compactedCount);
77001
77086
  const messagesToCompactForOps = originalHistory.slice(0, compactedCount);
77002
77087
  const fileOps = createFileOps();
77088
+ if (this.lastCompactionFiles !== void 0 && extractPreviousSummary(originalHistory) !== null) {
77089
+ for (const f of this.lastCompactionFiles.readFiles) fileOps.read.add(f);
77090
+ for (const f of this.lastCompactionFiles.modifiedFiles) fileOps.edited.add(f);
77091
+ } else this.lastCompactionFiles = void 0;
77003
77092
  for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
77004
77093
  const toolCallHistory = formatToolCallHistory(messagesToCompactForOps);
77005
77094
  const processedSummary = this.postProcessSummary(summary, fileOps, toolCallHistory);
77006
77095
  const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
77096
+ const fileLists = computeFileLists(fileOps);
77097
+ const MAX_PERSISTED_FILES = 100;
77098
+ const readFiles = fileLists.readFiles.slice(0, MAX_PERSISTED_FILES);
77099
+ const modifiedFiles = fileLists.modifiedFiles.slice(0, MAX_PERSISTED_FILES);
77007
77100
  const result = {
77008
77101
  summary: processedSummary,
77009
77102
  compactedCount,
77010
77103
  tokensBefore,
77011
77104
  tokensAfter,
77105
+ ...readFiles.length > 0 ? { readFiles } : {},
77106
+ ...modifiedFiles.length > 0 ? { modifiedFiles } : {},
77012
77107
  ...isUpdate ? { isUpdate: true } : {}
77013
77108
  };
77014
77109
  this.markCompleted();
@@ -77017,6 +77112,7 @@ var FullCompaction = class {
77017
77112
  result
77018
77113
  });
77019
77114
  this.agent.context.applyCompaction(result);
77115
+ this.lastCompactionFiles = fileLists;
77020
77116
  this.lowWaterMark = Math.floor(this.effectiveTokenCount * 1.1);
77021
77117
  await this.extractAndStoreMemos(processedSummary);
77022
77118
  this.triggerPostCompactHook(data, result);
@@ -77145,7 +77241,9 @@ var FullCompaction = class {
77145
77241
  */
77146
77242
  postProcessSummary(summary, fileOps, toolCallHistory) {
77147
77243
  const todos = this.agent.tools.storeData()["todo"] ?? [];
77148
- const sections = [summary.trim()];
77244
+ const base = summary.trim().replaceAll(/<files>[\s\S]*?<\/files>\s*/g, "").trimEnd();
77245
+ const sections = [];
77246
+ if (base.length > 0) sections.push(base);
77149
77247
  if (todos.length > 0) {
77150
77248
  const lines = todos.map((t) => {
77151
77249
  return `- [${t.status === "done" ? "x" : t.status === "in_progress" ? "-" : " "}] ${t.title}`;
@@ -78804,12 +78902,15 @@ var ContextMemory = class {
78804
78902
  * message mutated (compaction summary, micro-compaction truncation, or a
78805
78903
  * projection repair) and the cache broke from that index.
78806
78904
  *
78807
- * Behavior is otherwise identical to the getter - this is observation
78808
- * only, it does not alter the messages returned.
78905
+ * Unlike the read-only `messages` getter, this path closes any trailing
78906
+ * in-flight tool call by synthesizing an error result (synthesizeMissing):
78907
+ * these messages go straight to the provider, which rejects an assistant
78908
+ * tool_calls message with no matching tool result (e.g. after a network
78909
+ * drop mid-batch).
78809
78910
  */
78810
78911
  messagesForLLM() {
78811
78912
  this.agent.microCompaction.detect();
78812
- const messages = project(this.agent.microCompaction.compact(this.history));
78913
+ const messages = project(this.agent.microCompaction.compact(this.history), { synthesizeMissing: true });
78813
78914
  this.observePrefixStability(messages);
78814
78915
  return messages;
78815
78916
  }
@@ -78911,6 +79012,31 @@ var ContextMemory = class {
78911
79012
  hasOpenToolExchange() {
78912
79013
  return this.pendingToolResultIds.size > 0;
78913
79014
  }
79015
+ /**
79016
+ * Defensive teardown for a live turn that ended — normally, cancelled, or
79017
+ * failed — while recorded tool calls were still awaiting results (e.g. the
79018
+ * batch's result dispatch died after a `tool.call` was already recorded,
79019
+ * like a network drop mid-execution). Synthesizes an error result for each
79020
+ * dangling call so the exchange closes: left open, the assistant tool_calls
79021
+ * message would have no matching tool message and the next request would be
79022
+ * rejected by the provider ("must be followed by tool messages responding
79023
+ * to each tool_call_id"). No-op when the exchange is already closed.
79024
+ */
79025
+ closeAbandonedToolExchange(output) {
79026
+ if (this.pendingToolResultIds.size === 0) return 0;
79027
+ const interruptedToolCallIds = [...this.pendingToolResultIds];
79028
+ for (const toolCallId of interruptedToolCallIds) this.appendLoopEvent({
79029
+ type: "tool.result",
79030
+ parentUuid: toolCallId,
79031
+ toolCallId,
79032
+ result: {
79033
+ output,
79034
+ isError: true
79035
+ }
79036
+ });
79037
+ this.flushDeferredMessagesIfToolExchangeClosed();
79038
+ return interruptedToolCallIds.length;
79039
+ }
78914
79040
  pushHistory(...messages) {
78915
79041
  this._history.push(...messages);
78916
79042
  for (const message of messages) {
@@ -96333,6 +96459,11 @@ var ToolCallDeduplicator = class {
96333
96459
  };
96334
96460
  //#endregion
96335
96461
  //#region ../../packages/agent-core/src/agent/turn/index.ts
96462
+ /** Builds the error text synthesized for tool calls abandoned when a live
96463
+ * turn ends (cancelled, failed, or completed) before their results arrived. */
96464
+ function abandonedToolResultOutput(ended) {
96465
+ return `Tool call did not complete: ${ended.reason === "cancelled" ? "the turn was cancelled" : ended.reason === "failed" ? `the turn failed${ended.error !== void 0 ? ` (${ended.error.message})` : ""}` : "the turn ended"} before its result was recorded. Do not assume the tool completed successfully.`;
96466
+ }
96336
96467
  const GOAL_CONTINUATION_PROMPT = [
96337
96468
  "Continue working toward the active goal.",
96338
96469
  "Keep the self-audit brief. Do not explore unrelated interpretations once the goal can be",
@@ -96567,6 +96698,11 @@ var TurnFlow = class {
96567
96698
  turnId,
96568
96699
  reason: "completed"
96569
96700
  };
96701
+ try {
96702
+ this.agent.context.closeAbandonedToolExchange(abandonedToolResultOutput(ended));
96703
+ } catch (error) {
96704
+ console.error("closeAbandonedToolExchange failed", error);
96705
+ }
96570
96706
  this.agent.usage.endTurn();
96571
96707
  this.agent.emitEvent(ended);
96572
96708
  return ended;
@@ -96646,6 +96782,11 @@ var TurnFlow = class {
96646
96782
  };
96647
96783
  }
96648
96784
  }
96785
+ try {
96786
+ this.agent.context.closeAbandonedToolExchange(abandonedToolResultOutput(ended));
96787
+ } catch (error) {
96788
+ console.error("closeAbandonedToolExchange failed", error);
96789
+ }
96649
96790
  if (this.currentId === turnId) this.agent.usage.endTurn();
96650
96791
  this.agent.emitEvent(ended);
96651
96792
  if (standalone && this.currentId === turnId) this.activeTurn = null;
@@ -102568,7 +102709,7 @@ var SessionSubagentHost = class {
102568
102709
  text: childPrompt
102569
102710
  }], origin);
102570
102711
  await runChildTurnToCompletion(child, options.signal);
102571
- let result = lastAssistantText(child);
102712
+ let result = lastAssistantText$1(child);
102572
102713
  let remainingContinuations = SUMMARY_CONTINUATION_ATTEMPTS;
102573
102714
  while (remainingContinuations > 0 && result.length < SUMMARY_MIN_LENGTH) {
102574
102715
  remainingContinuations -= 1;
@@ -102578,7 +102719,7 @@ var SessionSubagentHost = class {
102578
102719
  text: summary_continuation_default
102579
102720
  }], origin);
102580
102721
  await runChildTurnToCompletion(child, options.signal);
102581
- result = lastAssistantText(child);
102722
+ result = lastAssistantText$1(child);
102582
102723
  }
102583
102724
  const usage = child.usage.data().total;
102584
102725
  let findingsBlock = "";
@@ -102694,7 +102835,7 @@ async function runChildTurnToCompletion(child, signal) {
102694
102835
  function throwIfSubagentStoppedAtMaxTokens(stopReason) {
102695
102836
  if (stopReason === "max_tokens") throw new Error(`${SUBAGENT_MAX_TOKENS_ERROR}.`);
102696
102837
  }
102697
- function lastAssistantText(agent) {
102838
+ function lastAssistantText$1(agent) {
102698
102839
  for (const message of [...agent.context.history].toReversed()) {
102699
102840
  if (message.role !== "assistant") continue;
102700
102841
  const text = message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
@@ -120191,10 +120332,10 @@ var ScreamAuthFacade = class {
120191
120332
  };
120192
120333
  //#endregion
120193
120334
  //#region ../../packages/node-sdk/src/rpc.ts
120194
- const MAIN_AGENT_ID$2 = "main";
120335
+ const MAIN_AGENT_ID$3 = "main";
120195
120336
  var SDKRpcClient = class {
120196
120337
  core;
120197
- interactiveAgentId = MAIN_AGENT_ID$2;
120338
+ interactiveAgentId = MAIN_AGENT_ID$3;
120198
120339
  ready;
120199
120340
  rpc;
120200
120341
  eventListeners = /* @__PURE__ */ new Set();
@@ -120772,7 +120913,7 @@ function errorMessage$2(error) {
120772
120913
  }
120773
120914
  //#endregion
120774
120915
  //#region ../../packages/node-sdk/src/session.ts
120775
- const MAIN_AGENT_ID$1 = "main";
120916
+ const MAIN_AGENT_ID$2 = "main";
120776
120917
  var Session = class {
120777
120918
  id;
120778
120919
  workDir;
@@ -121212,7 +121353,7 @@ var Session = class {
121212
121353
  this.emit({
121213
121354
  type: "session.meta.updated",
121214
121355
  sessionId: this.id,
121215
- agentId: MAIN_AGENT_ID$1,
121356
+ agentId: MAIN_AGENT_ID$2,
121216
121357
  title: patch.title,
121217
121358
  patch
121218
121359
  });
@@ -121634,7 +121775,7 @@ function optionalBuildString(value) {
121634
121775
  return typeof value === "string" && value.length > 0 ? value : void 0;
121635
121776
  }
121636
121777
  const SCREAM_BUILD_INFO = {
121637
- version: optionalBuildString("0.12.5"),
121778
+ version: optionalBuildString("0.12.7"),
121638
121779
  channel: optionalBuildString(""),
121639
121780
  commit: optionalBuildString(""),
121640
121781
  buildTarget: optionalBuildString("darwin-arm64")
@@ -122906,6 +123047,12 @@ const BUILTIN_SLASH_COMMANDS = [
122906
123047
  description: "registry.logout_desc",
122907
123048
  priority: 93
122908
123049
  },
123050
+ {
123051
+ name: "eval",
123052
+ aliases: [],
123053
+ description: "registry.eval_desc",
123054
+ priority: 92
123055
+ },
122909
123056
  {
122910
123057
  name: "exit",
122911
123058
  aliases: ["quit", "q"],
@@ -123022,7 +123169,7 @@ function getCtrlDHint() {
123022
123169
  function getCtrlCHint() {
123023
123170
  return t("constant.ctrl_c_hint");
123024
123171
  }
123025
- const MAIN_AGENT_ID = "main";
123172
+ const MAIN_AGENT_ID$1 = "main";
123026
123173
  const EXIT_CONFIRM_WINDOW_MS = 1500;
123027
123174
  function isManagedUsageProvider(providerKey) {
123028
123175
  return providerKey === DEFAULT_OAUTH_PROVIDER_NAME;
@@ -123993,7 +124140,7 @@ async function handleConnectCommand(host, args) {
123993
124140
  ok: false,
123994
124141
  label: "Failed to load catalog."
123995
124142
  });
123996
- host.showError(`Failed to fetch catalog: ${formatErrorMessage(error)}`);
124143
+ host.showError(t("auth.catalog_fetch_failed", { error: formatErrorMessage(error) }));
123997
124144
  }
123998
124145
  }
123999
124146
  }
@@ -127448,7 +127595,7 @@ async function guidedGoalSetup(host) {
127448
127595
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
127449
127596
  return;
127450
127597
  }
127451
- const { TextInputDialogComponent } = await import("./text-input-dialog-CEUCIznb.mjs");
127598
+ const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
127452
127599
  const initialDesc = await promptText(host, TextInputDialogComponent, {
127453
127600
  title: t("goal.setup_title_initial"),
127454
127601
  subtitle: t("goal.setup_desc_hint"),
@@ -127469,7 +127616,7 @@ async function guidedGoalSetup(host) {
127469
127616
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
127470
127617
  }
127471
127618
  async function showGoalConfigWizard(host, session, objective, replace) {
127472
- const { TextInputDialogComponent } = await import("./text-input-dialog-CEUCIznb.mjs");
127619
+ const { TextInputDialogComponent } = await import("./text-input-dialog-BIXonNNb.mjs");
127473
127620
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
127474
127621
  title: t("goal.wizard_title", { objective }),
127475
127622
  subtitle: t("goal.budget_turns_hint"),
@@ -128281,7 +128428,7 @@ var AssistantMessageComponent = class {
128281
128428
  const age = Date.now() - (this.fadeStartMs ?? 0);
128282
128429
  this.cachedWidth = void 0;
128283
128430
  this.cachedLines = void 0;
128284
- this.ui?.requestComponentRender(this);
128431
+ this.ui?.requestRender();
128285
128432
  if (age >= 1200) this.stopFade();
128286
128433
  }, FADE_TICK_MS);
128287
128434
  this.ui.requestRender();
@@ -128775,7 +128922,7 @@ var ThinkingComponent = class {
128775
128922
  if (this.ui === void 0 || this.spinnerInterval !== void 0) return;
128776
128923
  this.spinnerInterval = setInterval(() => {
128777
128924
  this.spinnerFrame = (this.spinnerFrame + 1) % BRAILLE_SPINNER_FRAMES.length;
128778
- this.ui?.requestComponentRender(this);
128925
+ this.ui?.requestRender();
128779
128926
  }, 80);
128780
128927
  }
128781
128928
  stopSpinner() {
@@ -133217,7 +133364,7 @@ var MoonLoader = class extends Text {
133217
133364
  const frame = BRAILLE_SPINNER_FRAMES[this.currentFrame];
133218
133365
  const coloredFrame = this.colorFn ? this.colorFn(frame) : frame;
133219
133366
  this.setText(this.label ? `${coloredFrame} ${this.label}` : coloredFrame);
133220
- this.ui.requestComponentRender(this);
133367
+ this.ui.requestRender();
133221
133368
  }
133222
133369
  };
133223
133370
  //#endregion
@@ -133353,9 +133500,6 @@ function getFallbackSkillMarketplace() {
133353
133500
  }
133354
133501
  //#endregion
133355
133502
  //#region src/tui/utils/component-capabilities.ts
133356
- function isExpandable(obj) {
133357
- return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
133358
- }
133359
133503
  function isPlanExpandable(obj) {
133360
133504
  return typeof obj === "object" && obj !== null && "setPlanExpanded" in obj && typeof obj.setPlanExpanded === "function";
133361
133505
  }
@@ -136035,6 +136179,429 @@ async function handleKnowledgeCommand(host, _args) {
136035
136179
  showMenu();
136036
136180
  }
136037
136181
  //#endregion
136182
+ //#region ../../packages/evals/src/judge.ts
136183
+ /** Runs every rule against the input; a rule passes when its check is true. */
136184
+ function judgeOutput(input, rules) {
136185
+ const passed = [];
136186
+ const failed = [];
136187
+ for (const rule of rules) if (rule.check(input)) passed.push(rule.name);
136188
+ else failed.push(rule.name);
136189
+ return {
136190
+ passed,
136191
+ failed
136192
+ };
136193
+ }
136194
+ //#endregion
136195
+ //#region ../../packages/evals/src/harness.ts
136196
+ /**
136197
+ * Minimal end-to-end eval harness built on the public node-sdk
136198
+ * `ScreamHarness`. Each eval run gets an isolated temp **workspace** so runs
136199
+ * never touch real project files; the scream home defaults to the real one so
136200
+ * the provider/API-key configuration in `~/.scream-code/config.toml` is
136201
+ * honored (pass `screamHome` to override for fully isolated runs). Model
136202
+ * selection follows `SCREAM_EVAL_MODEL` (e.g. `provider/model`);
136203
+ * when unset the harness throws with a clear message.
136204
+ */
136205
+ /** The main agent id; only its events settle the eval turn wait. */
136206
+ const MAIN_AGENT_ID = "main";
136207
+ /** The current scream default model, when resolvable from config. */
136208
+ const DEFAULT_EVAL_MODEL = process.env["SCREAM_EVAL_MODEL"];
136209
+ /**
136210
+ * Runs a single end-to-end prompt against a fresh isolated session and
136211
+ * returns the final assistant text plus token usage. The session, workspace
136212
+ * and scream home are torn down afterwards.
136213
+ */
136214
+ async function runEvalPrompt(input, options = {}) {
136215
+ const workDir = options.workDir ?? await mkdtemp(join(tmpdir(), "scream-eval-ws-"));
136216
+ const screamHome = options.screamHome;
136217
+ const createdWorkDir = options.workDir === void 0;
136218
+ let harness;
136219
+ let session;
136220
+ try {
136221
+ for (const fixture of (typeof input === "string" ? [] : input.fixtures) ?? []) await writeFile(join(workDir, fixture.name), fixture.content, "utf-8");
136222
+ const prompt = typeof input === "string" ? input : input.prompt;
136223
+ const model = options.model ?? DEFAULT_EVAL_MODEL;
136224
+ if (model === void 0 || model.length === 0) throw new Error("No eval model configured. Set SCREAM_EVAL_MODEL, e.g. SCREAM_EVAL_MODEL=provider/model pnpm eval");
136225
+ const [provider, modelId, ...rest] = model.split("/");
136226
+ const usageProvider = rest.length > 0 ? model : provider ?? "unknown";
136227
+ const usageModel = modelId === void 0 || rest.length > 0 ? model : modelId;
136228
+ harness = new ScreamHarness({ homeDir: screamHome });
136229
+ session = await harness.createSession({
136230
+ workDir,
136231
+ model,
136232
+ thinking: options.thinking ?? "off",
136233
+ permission: "yolo"
136234
+ });
136235
+ const { timedOut } = await promptAndWaitForTurnEnd(session, prompt);
136236
+ let output = "";
136237
+ try {
136238
+ output = lastAssistantText((await session.getContext()).history);
136239
+ } catch {
136240
+ output = "";
136241
+ }
136242
+ const total = (await session.getUsage().catch(() => void 0))?.total;
136243
+ let verifiedFile = false;
136244
+ if (options.verifyFileAfterTurn !== void 0) try {
136245
+ verifiedFile = await readFile(join(workDir, options.verifyFileAfterTurn.path), "utf-8") === options.verifyFileAfterTurn.content;
136246
+ } catch {
136247
+ verifiedFile = false;
136248
+ }
136249
+ return {
136250
+ output,
136251
+ timedOut,
136252
+ verifiedFile,
136253
+ usage: {
136254
+ provider: usageProvider,
136255
+ model: usageModel,
136256
+ inputTokens: total?.inputOther ?? 0,
136257
+ outputTokens: total?.output ?? 0,
136258
+ totalTokens: (total?.inputOther ?? 0) + (total?.output ?? 0) + (total?.inputCacheRead ?? 0) + (total?.inputCacheCreation ?? 0)
136259
+ }
136260
+ };
136261
+ } finally {
136262
+ if (session !== void 0) await session.close({ extractMemories: false }).catch(() => {});
136263
+ if (createdWorkDir) await rm(workDir, {
136264
+ recursive: true,
136265
+ force: true
136266
+ }).catch(() => {});
136267
+ }
136268
+ }
136269
+ function lastAssistantText(history) {
136270
+ for (let i = history.length - 1; i >= 0; i -= 1) {
136271
+ const message = history[i];
136272
+ if (message.role !== "assistant") continue;
136273
+ const text = (message.content ?? []).filter((part) => part.type === "text" && part.text !== void 0).map((part) => part.text).join("");
136274
+ if (text.trim().length > 0) return text.trim();
136275
+ }
136276
+ return "";
136277
+ }
136278
+ /**
136279
+ * Sends a prompt and resolves when the main agent's turn ends. `Session.prompt`
136280
+ * only enqueues the RPC request; the assistant reply arrives asynchronously as
136281
+ * events, so we must wait for the `turn.ended` event to know the run finished.
136282
+ *
136283
+ * A hard timeout guards against a turn that never ends (e.g. a model looping
136284
+ * through tools instead of converging, or a network interruption). On timeout
136285
+ * we cancel the session so the caller's cleanup path still runs instead of
136286
+ * leaking the session and temp workspace. 90s is enough to complete any
136287
+ * legitimate single-turn tool round-trip; longer waits are almost always a
136288
+ * stuck model, so we fail fast instead of burning tokens.
136289
+ */
136290
+ const TURN_WAIT_TIMEOUT_MS = 9e4;
136291
+ /**
136292
+ * Resolves with `timedOut: true` when the turn hit the timeout (model looping
136293
+ * or stuck) instead of throwing, so callers can still inspect side effects
136294
+ * (e.g. a file the Write tool already created). Real errors (turn failed /
136295
+ * error event) still reject.
136296
+ */
136297
+ async function promptAndWaitForTurnEnd(session, prompt) {
136298
+ const { promise, resolve, reject } = promiseWithResolvers();
136299
+ let activeTurnId;
136300
+ let activeAgentId;
136301
+ let settled = false;
136302
+ let timeout;
136303
+ let graceTimer;
136304
+ /** Set once the timeout fires; guards against the cancel-triggered
136305
+ * `turn.ended('cancelled')` being misread as a real failure. */
136306
+ let timeoutFired = false;
136307
+ const finish = (result) => {
136308
+ if (settled) return;
136309
+ settled = true;
136310
+ if (timeout !== void 0) clearTimeout(timeout);
136311
+ if (graceTimer !== void 0) clearTimeout(graceTimer);
136312
+ if (result instanceof Error) reject(result);
136313
+ else resolve(result);
136314
+ };
136315
+ const unsubscribe = session.onEvent((event) => {
136316
+ if (event.agentId !== MAIN_AGENT_ID) return;
136317
+ if (event.type === "error") {
136318
+ finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`));
136319
+ return;
136320
+ }
136321
+ if (event.type === "turn.started" && activeTurnId === void 0) {
136322
+ activeTurnId = event.turnId;
136323
+ activeAgentId = event.agentId;
136324
+ return;
136325
+ }
136326
+ if (activeTurnId === void 0 || activeAgentId === void 0 || !("turnId" in event) || event.turnId !== activeTurnId || event.agentId !== activeAgentId) return;
136327
+ if (event.type === "turn.ended") if (event.reason === "completed") finish({ timedOut: false });
136328
+ else if (timeoutFired && event.reason === "cancelled") return;
136329
+ else finish(/* @__PURE__ */ new Error(`Turn ended with reason: ${event.reason}`));
136330
+ });
136331
+ try {
136332
+ await session.prompt(prompt);
136333
+ timeout = setTimeout(() => {
136334
+ timeoutFired = true;
136335
+ session.cancel().catch(() => {});
136336
+ graceTimer = setTimeout(() => {
136337
+ finish({ timedOut: true });
136338
+ }, 500);
136339
+ }, TURN_WAIT_TIMEOUT_MS);
136340
+ return await promise;
136341
+ } finally {
136342
+ unsubscribe();
136343
+ if (timeout !== void 0) clearTimeout(timeout);
136344
+ if (graceTimer !== void 0) clearTimeout(graceTimer);
136345
+ }
136346
+ }
136347
+ function promiseWithResolvers() {
136348
+ let resolve;
136349
+ let reject;
136350
+ return {
136351
+ promise: new Promise((res, rej) => {
136352
+ resolve = res;
136353
+ reject = rej;
136354
+ }),
136355
+ resolve,
136356
+ reject
136357
+ };
136358
+ }
136359
+ const EVAL_CASES = [
136360
+ {
136361
+ id: "smoke",
136362
+ name: "Smoke — answers a trivial question",
136363
+ input: "What is the capital of France? Answer in one word.",
136364
+ rules: [{
136365
+ name: "answers the capital",
136366
+ check: (output) => {
136367
+ const lower = output.toLowerCase();
136368
+ return lower.includes("paris") || lower.includes("巴黎");
136369
+ }
136370
+ }],
136371
+ extra: (result) => {
136372
+ const failures = [];
136373
+ if (result.usage.totalTokens <= 0) failures.push("usage.totalTokens must be > 0");
136374
+ if (result.usage.model.length === 0) failures.push("usage.model must be non-empty");
136375
+ return failures;
136376
+ }
136377
+ },
136378
+ {
136379
+ id: "read-file",
136380
+ name: "Regression — reads a file via the Read tool",
136381
+ input: {
136382
+ prompt: "Read the file data.txt in the current directory using the Read tool, then tell me what it contains.",
136383
+ fixtures: [{
136384
+ name: "data.txt",
136385
+ content: "The quick brown fox jumps over the lazy dog."
136386
+ }]
136387
+ },
136388
+ rules: [{
136389
+ name: "reports file content",
136390
+ check: (output) => output.includes("quick brown fox")
136391
+ }]
136392
+ },
136393
+ {
136394
+ id: "write-file",
136395
+ name: "Regression — writes a file via the Write tool",
136396
+ input: { prompt: "Create a file named output.txt in the current directory using the Write tool. Write exactly the text: hello eval world. Make exactly one Write tool call and do not call any other tool afterwards. After the tool completes, immediately answer with the exact text you wrote and nothing else." },
136397
+ rules: [{
136398
+ name: "Write tool landed the file",
136399
+ check: (output) => output.includes("hello eval world")
136400
+ }],
136401
+ verifyFile: {
136402
+ path: "output.txt",
136403
+ content: "hello eval world"
136404
+ },
136405
+ extra: (result) => {
136406
+ const failures = [];
136407
+ if (!result.verifiedFile) failures.push("output.txt was not created with the exact content");
136408
+ return failures;
136409
+ }
136410
+ },
136411
+ {
136412
+ id: "isolated-workspace",
136413
+ name: "Regression — runs in an isolated temp workspace",
136414
+ input: {
136415
+ prompt: "Run \"pwd\" in a shell and answer with only the absolute path that the command printed.",
136416
+ fixtures: [{
136417
+ name: "marker.txt",
136418
+ content: "isolated"
136419
+ }]
136420
+ },
136421
+ rules: [{
136422
+ name: "prints an absolute path",
136423
+ check: (output) => output.match(/(?:\/[\w.\-/]+|[A-Za-z]:[\\/][\w.\\/-]*)/)?.[0] !== void 0
136424
+ }, {
136425
+ name: "path is not the repo",
136426
+ check: (output) => !output.match(/(?:\/[\w.\-/]+|[A-Za-z]:[\\/][\w.\\/-]*)/)?.[0]?.includes("scream-code")
136427
+ }]
136428
+ }
136429
+ ];
136430
+ /** Runs every eval case sequentially and returns a structured report. */
136431
+ async function runAllEvals(options = {}) {
136432
+ const results = [];
136433
+ let passedCount = 0;
136434
+ let failedCount = 0;
136435
+ for (const caseDef of EVAL_CASES) {
136436
+ let result;
136437
+ try {
136438
+ result = await runEvalPrompt(caseDef.input, {
136439
+ model: options.model,
136440
+ verifyFileAfterTurn: caseDef.verifyFile
136441
+ });
136442
+ } catch (error) {
136443
+ results.push({
136444
+ id: caseDef.id,
136445
+ name: caseDef.name,
136446
+ passed: false,
136447
+ failedRules: [],
136448
+ passedRules: [],
136449
+ extraFailures: [],
136450
+ output: "",
136451
+ error: error instanceof Error ? error.message : String(error),
136452
+ timedOut: false,
136453
+ toolCheckFailed: false
136454
+ });
136455
+ failedCount += 1;
136456
+ options.onProgress?.({
136457
+ completed: results.length,
136458
+ total: EVAL_CASES.length,
136459
+ currentId: caseDef.id,
136460
+ currentName: caseDef.name,
136461
+ passed: passedCount,
136462
+ failed: failedCount
136463
+ });
136464
+ continue;
136465
+ }
136466
+ const judged = judgeOutput(result.output, caseDef.rules);
136467
+ const extraFailures = caseDef.extra?.(result) ?? [];
136468
+ const passed = judged.failed.length === 0 && extraFailures.length === 0;
136469
+ if (passed) passedCount += 1;
136470
+ else failedCount += 1;
136471
+ const toolCheckFailed = caseDef.verifyFile !== void 0 && !result.verifiedFile;
136472
+ results.push({
136473
+ id: caseDef.id,
136474
+ name: caseDef.name,
136475
+ passed,
136476
+ failedRules: judged.failed,
136477
+ passedRules: judged.passed,
136478
+ extraFailures,
136479
+ output: result.output,
136480
+ timedOut: result.timedOut,
136481
+ toolCheckFailed
136482
+ });
136483
+ options.onProgress?.({
136484
+ completed: results.length,
136485
+ total: EVAL_CASES.length,
136486
+ currentId: caseDef.id,
136487
+ currentName: caseDef.name,
136488
+ passed: passedCount,
136489
+ failed: failedCount
136490
+ });
136491
+ }
136492
+ return {
136493
+ results,
136494
+ passed: passedCount,
136495
+ failed: failedCount
136496
+ };
136497
+ }
136498
+ //#endregion
136499
+ //#region src/tui/commands/eval.ts
136500
+ const EVAL_PANEL_DISMISS_MS = 6e4;
136501
+ let activeEvalPanel;
136502
+ let activeEvalTimer;
136503
+ /** Guards against overlapping runs (each run calls a real model and costs
136504
+ * tokens; a second /eval while one is running is almost certainly a mistake). */
136505
+ let evalRunning = false;
136506
+ function dismissEvalPanel(state) {
136507
+ if (activeEvalTimer !== void 0) {
136508
+ clearTimeout(activeEvalTimer);
136509
+ activeEvalTimer = void 0;
136510
+ }
136511
+ if (activeEvalPanel !== void 0) {
136512
+ state.transcriptContainer.removeChild(activeEvalPanel);
136513
+ activeEvalPanel = void 0;
136514
+ state.ui.requestRender();
136515
+ }
136516
+ }
136517
+ function clearEvalPanelState(state) {
136518
+ dismissEvalPanel(state);
136519
+ }
136520
+ /** Model selector for eval runs; falls back to the active session model. */
136521
+ function resolveEvalModel(host) {
136522
+ return host.state.appState.model;
136523
+ }
136524
+ /**
136525
+ * Runs the end-to-end evals in the background (they call a real model and can
136526
+ * take minutes) and renders the per-case report when done. Returns immediately
136527
+ * so the TUI keeps responding.
136528
+ */
136529
+ function runEvalCommand(host) {
136530
+ if (evalRunning) {
136531
+ host.showStatus(t("dispatch.eval_running"));
136532
+ return;
136533
+ }
136534
+ const model = resolveEvalModel(host);
136535
+ if (model === void 0 || model.length === 0) {
136536
+ host.showError(t("dispatch.eval_no_model"));
136537
+ return;
136538
+ }
136539
+ const sessionId = host.state.appState.sessionId;
136540
+ evalRunning = true;
136541
+ host.showStatus(t("dispatch.eval_started", { model }));
136542
+ (async () => {
136543
+ let summary;
136544
+ try {
136545
+ summary = await runAllEvals({
136546
+ model,
136547
+ onProgress: ({ completed, total, passed, failed, currentName }) => {
136548
+ host.showStatus(t("dispatch.eval_progress", {
136549
+ completed: String(completed),
136550
+ total: String(total),
136551
+ passed: String(passed),
136552
+ failed: String(failed),
136553
+ name: currentName
136554
+ }));
136555
+ }
136556
+ });
136557
+ } catch (error) {
136558
+ evalRunning = false;
136559
+ host.showError(t("dispatch.eval_failed", { error: error instanceof Error ? error.message : String(error) }));
136560
+ return;
136561
+ }
136562
+ evalRunning = false;
136563
+ if (sessionId !== void 0 && sessionId !== host.state.appState.sessionId) return;
136564
+ try {
136565
+ renderEvalSummary(host, summary);
136566
+ } catch (error) {
136567
+ host.showError(t("dispatch.eval_failed", { error: error instanceof Error ? error.message : String(error) }));
136568
+ }
136569
+ })();
136570
+ }
136571
+ function renderEvalSummary(host, summary) {
136572
+ const colors = host.state.theme.colors;
136573
+ const lines = [chalk.hex(colors.primary)(`${summary.passed} ${t("dispatch.eval_passed")} · ${summary.failed} ${t("dispatch.eval_failed_count")}`), ""];
136574
+ for (const result of summary.results) {
136575
+ const mark = result.passed ? "✓" : "×";
136576
+ const color = result.passed ? colors.success : colors.error;
136577
+ lines.push(`${chalk.hex(color)(mark)} ${result.name}`);
136578
+ if (result.passed) continue;
136579
+ lines.push(` ${chalk.hex(colors.error)(suggestionFor(result))}`);
136580
+ const details = [];
136581
+ for (const rule of result.failedRules) details.push(rule);
136582
+ for (const failure of result.extraFailures) details.push(failure);
136583
+ if (result.error !== void 0) details.push(result.error);
136584
+ if (result.output.length > 0) details.push(`output: ${result.output.slice(0, 160)}`);
136585
+ for (const detail of details) lines.push(` ${chalk.hex(colors.textDim)(`· ${detail}`)}`);
136586
+ }
136587
+ lines.push("");
136588
+ dismissEvalPanel(host.state);
136589
+ const panel = new UsagePanelComponent(lines, colors.primary, " Eval ");
136590
+ host.state.transcriptContainer.addChild(panel);
136591
+ activeEvalPanel = panel;
136592
+ activeEvalTimer = setTimeout(() => {
136593
+ dismissEvalPanel(host.state);
136594
+ }, EVAL_PANEL_DISMISS_MS);
136595
+ host.state.ui.requestRender();
136596
+ }
136597
+ /** Picks a user-facing self-check hint based on how the case failed. */
136598
+ function suggestionFor(result) {
136599
+ if (result.error !== void 0) return t("dispatch.eval_suggest_error");
136600
+ if (result.timedOut) return t("dispatch.eval_suggest_timeout");
136601
+ if (result.toolCheckFailed) return t("dispatch.eval_suggest_tool");
136602
+ return t("dispatch.eval_suggest_answer");
136603
+ }
136604
+ //#endregion
136038
136605
  //#region src/tui/commands/dispatch.ts
136039
136606
  function dispatchInput(host, text) {
136040
136607
  if (parseSlashInput(text) !== null) {
@@ -136208,6 +136775,9 @@ async function handleBuiltInSlashCommand(host, name, args) {
136208
136775
  case "logout":
136209
136776
  await handleLogoutCommand(host);
136210
136777
  return;
136778
+ case "eval":
136779
+ runEvalCommand(host);
136780
+ return;
136211
136781
  case "memory":
136212
136782
  await handleMemoryCommand(host, args);
136213
136783
  return;
@@ -136235,6 +136805,17 @@ var AuthFlowController = class {
136235
136805
  availableModels: config.models ?? {},
136236
136806
  availableProviders: config.providers ?? {}
136237
136807
  });
136808
+ this.prefetchCatalogCache();
136809
+ }
136810
+ /**
136811
+ * Best-effort background catalog refresh. Fetches the remote catalog and,
136812
+ * on success, updates the on-disk cache. On failure it stays silent: the
136813
+ * existing cache (or built-in) keeps working. Never throws.
136814
+ */
136815
+ async prefetchCatalogCache() {
136816
+ try {
136817
+ saveCatalogCache(await fetchCatalog(DEFAULT_CATALOG_URL), resolveScreamHome());
136818
+ } catch {}
136238
136819
  }
136239
136820
  async activateModelAfterLogin(model, thinkingLevel) {
136240
136821
  const { host } = this;
@@ -140590,9 +141171,8 @@ var TasksBrowserController = class {
140590
141171
  colors: state.theme.colors,
140591
141172
  ...this.buildCallbacks()
140592
141173
  }, state.terminal);
140593
- const savedChildren = [...state.ui.children];
140594
- state.ui.clear();
140595
- state.ui.addChild(component);
141174
+ const savedLayoutRoot = state.layoutRoot;
141175
+ state.ui.setLayoutRoot(component);
140596
141176
  state.ui.setFocus(component);
140597
141177
  state.ui.requestRender(true);
140598
141178
  const pollTimer = setInterval(() => {
@@ -140600,7 +141180,7 @@ var TasksBrowserController = class {
140600
141180
  }, 1e3);
140601
141181
  this.host.setTasksBrowser({
140602
141182
  component,
140603
- savedChildren,
141183
+ savedLayoutRoot,
140604
141184
  filter,
140605
141185
  selectedTaskId,
140606
141186
  tailOutput: void 0,
@@ -140620,8 +141200,7 @@ var TasksBrowserController = class {
140620
141200
  if (browser.viewer !== void 0) this.closeOutputViewer();
140621
141201
  if (browser.pollTimer !== void 0) clearInterval(browser.pollTimer);
140622
141202
  if (browser.flashTimer !== void 0) clearTimeout(browser.flashTimer);
140623
- state.ui.clear();
140624
- for (const child of browser.savedChildren) state.ui.addChild(child);
141203
+ state.ui.setLayoutRoot(browser.savedLayoutRoot);
140625
141204
  this.host.setTasksBrowser(void 0);
140626
141205
  state.ui.setFocus(state.editor);
140627
141206
  state.ui.requestRender(true);
@@ -140791,9 +141370,8 @@ var TasksBrowserController = class {
140791
141370
  this.closeOutputViewer();
140792
141371
  }
140793
141372
  }, state.terminal);
140794
- const savedBrowserChildren = [...state.ui.children];
140795
- state.ui.clear();
140796
- state.ui.addChild(viewer);
141373
+ const savedBrowserLayout = state.layoutRoot;
141374
+ state.ui.setLayoutRoot(viewer);
140797
141375
  state.ui.setFocus(viewer);
140798
141376
  state.ui.requestRender(true);
140799
141377
  const pollTimer = setInterval(() => {
@@ -140801,7 +141379,7 @@ var TasksBrowserController = class {
140801
141379
  }, 1e3);
140802
141380
  browser.viewer = {
140803
141381
  component: viewer,
140804
- savedChildren: savedBrowserChildren,
141382
+ savedLayoutRoot: savedBrowserLayout,
140805
141383
  taskId,
140806
141384
  output,
140807
141385
  refreshId: 0,
@@ -140857,8 +141435,7 @@ var TasksBrowserController = class {
140857
141435
  const viewer = browser.viewer;
140858
141436
  clearInterval(viewer.pollTimer);
140859
141437
  browser.viewer = void 0;
140860
- this.host.state.ui.clear();
140861
- for (const child of viewer.savedChildren) this.host.state.ui.addChild(child);
141438
+ this.host.state.ui.setLayoutRoot(viewer.savedLayoutRoot);
140862
141439
  this.host.state.ui.setFocus(browser.component);
140863
141440
  this.host.state.ui.requestRender(true);
140864
141441
  }
@@ -141294,13 +141871,6 @@ var TranscriptController = class TranscriptController {
141294
141871
  toggleToolOutputExpansion() {
141295
141872
  const { state } = this.host;
141296
141873
  state.toolOutputExpanded = !state.toolOutputExpanded;
141297
- const walk = (children) => {
141298
- for (const child of children) {
141299
- if (isExpandable(child)) child.setExpanded(state.toolOutputExpanded);
141300
- if ("children" in child && Array.isArray(child.children)) walk(child.children);
141301
- }
141302
- };
141303
- walk(state.transcriptContainer.children);
141304
141874
  state.ui.requestRender();
141305
141875
  }
141306
141876
  togglePlanExpansion() {
@@ -141349,6 +141919,44 @@ var GutterContainer = class extends Container {
141349
141919
  }
141350
141920
  };
141351
141921
  //#endregion
141922
+ //#region src/tui/components/panes/status-bar-pane.ts
141923
+ /**
141924
+ * A fixed one-line status bar shown directly above the input editor: it
141925
+ * displays the current work phase (idle / waiting / thinking / composing /
141926
+ * tool) with its spinner, pinned to the bottom of the terminal via the dock
141927
+ * VStack. Designed to hold more status widgets later — add children to the
141928
+ * container when needed.
141929
+ */
141930
+ var StatusBarPaneComponent = class extends Container {
141931
+ constructor(options) {
141932
+ super();
141933
+ this.update(options);
141934
+ }
141935
+ /** Re-renders the bar for a new mode/label. The spinner/pulse are the
141936
+ * caller's shared instances (kept alive across mode changes), so this only
141937
+ * swaps which children are visible. */
141938
+ update(options) {
141939
+ this.clear();
141940
+ if (options.mode === "idle") return;
141941
+ if (options.mode === "waiting" || options.mode === "tool" || options.mode === "composing" || options.mode === "thinking") {
141942
+ if (options.pulseWave !== void 0) {
141943
+ if (options.label.length > 0) {
141944
+ const row = new HStack();
141945
+ row.addChild(options.pulseWave);
141946
+ row.addChild(new Text(` ${options.label}`, 1, 0));
141947
+ this.addChild(row);
141948
+ } else this.addChild(options.pulseWave);
141949
+ return;
141950
+ }
141951
+ if (options.spinner !== void 0) {
141952
+ this.addChild(options.spinner);
141953
+ return;
141954
+ }
141955
+ if (options.label.length > 0) this.addChild(new Text(options.label, 1, 0));
141956
+ }
141957
+ }
141958
+ };
141959
+ //#endregion
141352
141960
  //#region src/tui/theme/bundle.ts
141353
141961
  function createScreamTUIThemeBundle(theme, resolvedTheme) {
141354
141962
  const actualTheme = resolvedTheme ?? resolveThemeSync(theme);
@@ -141640,36 +142248,11 @@ var PulseWaveLoader = class extends Text {
141640
142248
  }
141641
142249
  };
141642
142250
  //#endregion
141643
- //#region src/tui/components/panes/activity-pane.ts
141644
- var ActivityPaneComponent = class extends Container {
141645
- constructor(options) {
141646
- super();
141647
- if (options.mode === "waiting" || options.mode === "tool") {
141648
- if (options.spinner !== void 0) {
141649
- this.addChild(new Spacer(1));
141650
- this.addChild(options.spinner);
141651
- }
141652
- if (options.pulseWave !== void 0) {
141653
- this.addChild(new Spacer(1));
141654
- this.addChild(options.pulseWave);
141655
- }
141656
- return;
141657
- }
141658
- if (options.mode === "composing" && options.spinner !== void 0) {
141659
- this.addChild(new Spacer(1));
141660
- this.addChild(options.spinner);
141661
- if (options.pulseWave !== void 0) {
141662
- this.addChild(new Spacer(1));
141663
- this.addChild(options.pulseWave);
141664
- }
141665
- }
141666
- }
141667
- };
141668
- //#endregion
141669
142251
  //#region src/tui/controllers/lifecycle-controller.ts
141670
142252
  var LifecycleController = class LifecycleController {
141671
142253
  host;
141672
142254
  signalCleanupHandlers = [];
142255
+ footerWrap;
141673
142256
  ccConnectPollTimer;
141674
142257
  memoryIdleTimer;
141675
142258
  memoryCountdownTimer;
@@ -141677,6 +142260,9 @@ var LifecycleController = class LifecycleController {
141677
142260
  terminalFocusTrackingDispose;
141678
142261
  terminalThemeTrackingDispose;
141679
142262
  lastActivityMode;
142263
+ /** The active status-bar loader (PulseWaveLoader or MoonLoader). Owned by
142264
+ * this controller; stopped before replacement to avoid leaking timers. */
142265
+ statusBarLoader;
141680
142266
  static MEMORY_IDLE_MS = 900 * 1e3;
141681
142267
  static MEMORY_COUNTDOWN_MS = 15 * 1e3;
141682
142268
  static MEMORY_EXTRACT_COOLDOWN_MS = 600 * 1e3;
@@ -141816,19 +142402,72 @@ var LifecycleController = class LifecycleController {
141816
142402
  buildLayout() {
141817
142403
  const { ui } = this.host.state;
141818
142404
  ui.clear();
141819
- ui.addChild(this.host.state.transcriptContainer);
141820
- ui.addChild(this.host.state.activityContainer);
141821
- ui.addChild(this.host.state.todoPanelContainer);
141822
- ui.addChild(this.host.state.queueContainer);
141823
- ui.addChild(this.host.state.errorBannerContainer);
141824
- ui.addChild(this.host.state.planModeBannerContainer);
141825
- ui.addChild(new FlexSpacer());
141826
- ui.addChild(this.host.state.editorContainer);
142405
+ const scrollContent = new Container();
142406
+ scrollContent.addChild(this.host.state.transcriptContainer);
142407
+ scrollContent.addChild(this.host.state.activityContainer);
142408
+ scrollContent.addChild(this.host.state.queueContainer);
142409
+ const transcriptScrollView = new ScrollView(scrollContent, {
142410
+ follow: "end",
142411
+ primary: true,
142412
+ scrollbar: "auto"
142413
+ });
142414
+ const dock = new VStack([
142415
+ {
142416
+ component: this.host.state.todoPanelContainer,
142417
+ shrink: 1,
142418
+ minSize: 0,
142419
+ visible: (viewport) => viewport.width >= 90
142420
+ },
142421
+ {
142422
+ component: this.host.state.errorBannerContainer,
142423
+ shrink: 1,
142424
+ minSize: 0
142425
+ },
142426
+ {
142427
+ component: this.host.state.planModeBannerContainer,
142428
+ shrink: 1,
142429
+ minSize: 0
142430
+ },
142431
+ {
142432
+ component: this.host.state.statusBarContainer,
142433
+ shrink: 1,
142434
+ minSize: 1,
142435
+ visible: (viewport) => viewport.width >= 60
142436
+ },
142437
+ {
142438
+ component: this.host.state.editorContainer,
142439
+ shrink: 1,
142440
+ minSize: 3
142441
+ },
142442
+ {
142443
+ component: this.ensureFooterWrap(),
142444
+ shrink: 1,
142445
+ minSize: 1
142446
+ }
142447
+ ]);
142448
+ const layoutRoot = new VStack([{
142449
+ component: transcriptScrollView,
142450
+ basis: 0,
142451
+ grow: 1,
142452
+ shrink: 1,
142453
+ minSize: 1
142454
+ }, {
142455
+ component: dock,
142456
+ basis: "auto",
142457
+ grow: 0,
142458
+ shrink: 1,
142459
+ minSize: 1
142460
+ }]);
142461
+ ui.setLayoutRoot(layoutRoot);
142462
+ this.host.state.layoutRoot = layoutRoot;
141827
142463
  }
141828
142464
  mountFooter() {
141829
- const footerWrap = new GutterContainer(1, 1);
141830
- footerWrap.addChild(this.host.state.footer);
141831
- this.host.state.ui.addChild(footerWrap);
142465
+ this.ensureFooterWrap();
142466
+ this.footerWrap.addChild(this.host.state.footer);
142467
+ }
142468
+ ensureFooterWrap() {
142469
+ if (this.footerWrap === void 0) this.footerWrap = new GutterContainer(1, 1);
142470
+ return this.footerWrap;
141832
142471
  }
141833
142472
  refreshTerminalThemeTracking() {
141834
142473
  this.stopTerminalThemeTracking();
@@ -141863,48 +142502,57 @@ var LifecycleController = class LifecycleController {
141863
142502
  this.syncTerminalProgress(this.shouldShowTerminalProgress(effectiveMode));
141864
142503
  if (effectiveMode === this.lastActivityMode) return;
141865
142504
  this.lastActivityMode = effectiveMode;
142505
+ this.updateStatusBar(effectiveMode);
141866
142506
  const { state } = this.host;
141867
142507
  state.activityContainer.clear();
141868
142508
  switch (effectiveMode) {
141869
142509
  case "hidden":
142510
+ case "idle":
141870
142511
  this.stopActivitySpinner();
141871
142512
  this.stopPulseWave();
141872
- state.ui.requestRender();
141873
- return;
141874
- case "waiting": {
141875
- this.stopActivitySpinner();
141876
- const pulseWave = this.ensurePulseWave();
141877
- state.activityContainer.addChild(new ActivityPaneComponent({
141878
- mode: "waiting",
141879
- pulseWave
141880
- }));
141881
142513
  break;
141882
- }
142514
+ case "waiting":
141883
142515
  case "thinking":
142516
+ case "composing":
142517
+ case "tool":
141884
142518
  this.stopActivitySpinner();
141885
142519
  this.stopPulseWave();
141886
142520
  break;
141887
- case "composing": {
141888
- const spinner = this.ensureActivitySpinner("working...", (s) => chalk.hex(state.theme.colors.primary)(s));
141889
- state.activityContainer.addChild(new ActivityPaneComponent({
141890
- mode: "composing",
141891
- spinner
141892
- }));
141893
- break;
141894
- }
141895
- case "tool": {
141896
- this.stopActivitySpinner();
141897
- const pulseWave = this.ensurePulseWave();
141898
- state.activityContainer.addChild(new ActivityPaneComponent({
141899
- mode: "tool",
141900
- pulseWave
141901
- }));
142521
+ }
142522
+ state.ui.requestRender();
142523
+ }
142524
+ /**
142525
+ * Updates the fixed status bar above the editor to reflect the current
142526
+ * work phase. Called only when the phase actually changes. Uses its OWN
142527
+ * spinner/pulse instances (a component can only have one parent, so the
142528
+ * activity pane's instances must not be shared). Recreating a loader on
142529
+ * every call would leak setInterval timers (each PulseWaveLoader.start()
142530
+ * registers one and the old instance is never stopped), so the previous
142531
+ * instance is explicitly stopped before a new one is mounted. Idle leaves
142532
+ * the bar empty.
142533
+ */
142534
+ updateStatusBar(mode) {
142535
+ const { state } = this.host;
142536
+ state.statusBarContainer.clear();
142537
+ this.statusBarLoader?.stop();
142538
+ this.statusBarLoader = void 0;
142539
+ switch (mode) {
142540
+ case "waiting":
142541
+ case "composing":
142542
+ case "tool":
142543
+ {
142544
+ const loader = new PulseWaveLoader(state.ui, state.theme.colors.primary);
142545
+ this.statusBarLoader = loader;
142546
+ state.statusBarContainer.addChild(new StatusBarPaneComponent({
142547
+ mode,
142548
+ label: "",
142549
+ pulseWave: loader
142550
+ }));
142551
+ }
141902
142552
  break;
141903
- }
142553
+ case "thinking": break;
141904
142554
  case "idle":
141905
- this.stopActivitySpinner();
141906
- this.stopPulseWave();
141907
- break;
142555
+ case "hidden": break;
141908
142556
  }
141909
142557
  state.ui.requestRender();
141910
142558
  }
@@ -141927,28 +142575,12 @@ var LifecycleController = class LifecycleController {
141927
142575
  this.host.state.terminal.setProgress(active);
141928
142576
  this.host.state.terminalState.progressActive = active;
141929
142577
  }
141930
- ensureActivitySpinner(label = "", colorFn) {
141931
- if (this.host.state.activitySpinner === null) {
141932
- const instance = new MoonLoader(this.host.state.ui, colorFn, label);
141933
- this.host.state.activitySpinner = { instance };
141934
- return instance;
141935
- }
141936
- this.host.state.activitySpinner.instance.setLabel(label);
141937
- if (colorFn !== void 0) this.host.state.activitySpinner.instance.setColorFn(colorFn);
141938
- return this.host.state.activitySpinner.instance;
141939
- }
141940
142578
  stopActivitySpinner() {
141941
142579
  if (this.host.state.activitySpinner !== null) {
141942
142580
  this.host.state.activitySpinner.instance.stop();
141943
142581
  this.host.state.activitySpinner = null;
141944
142582
  }
141945
142583
  }
141946
- ensurePulseWave() {
141947
- if (this.host.state.pulseWave !== null) return this.host.state.pulseWave;
141948
- const instance = new PulseWaveLoader(this.host.state.ui, this.host.state.theme.colors.primary);
141949
- this.host.state.pulseWave = instance;
141950
- return instance;
141951
- }
141952
142584
  stopPulseWave() {
141953
142585
  if (this.host.state.pulseWave !== null) {
141954
142586
  this.host.state.pulseWave.stop();
@@ -142375,18 +143007,10 @@ var InputController = class InputController {
142375
143007
  this.host = host;
142376
143008
  }
142377
143009
  setupAutocomplete() {
142378
- const visible = this.host.getSlashCommands().filter((cmd) => !(cmd.name.startsWith("skill:") && cmd.source === "builtin"));
142379
- const slashCommands = visible.map((cmd) => cmd);
143010
+ const slashCommands = this.host.getSlashCommands().filter((cmd) => !(cmd.name.startsWith("skill:") && cmd.source === "builtin")).map((cmd) => cmd);
142380
143011
  const { state } = this.host;
142381
143012
  const provider = new FileMentionProvider(slashCommands, state.appState.workDir, state.fdPath, state.gitLsFilesCache);
142382
143013
  state.editor.setAutocompleteProvider(provider);
142383
- const argumentHints = /* @__PURE__ */ new Map();
142384
- for (const cmd of visible) {
142385
- if (cmd.argumentHint === void 0) continue;
142386
- argumentHints.set(cmd.name, cmd.argumentHint);
142387
- for (const alias of cmd.aliases) argumentHints.set(alias, cmd.argumentHint);
142388
- }
142389
- state.editor.setArgumentHints(argumentHints);
142390
143014
  state.editor.onFirstInput = () => {
142391
143015
  this.host.stopWelcomeBreathing();
142392
143016
  this.#permanentlyStopBreathing();
@@ -143681,8 +144305,7 @@ function createTUIState(options) {
143681
144305
  const initialAppState = options.initialAppState;
143682
144306
  const theme = createScreamTUIThemeBundle(initialAppState.theme, options.resolvedTheme);
143683
144307
  const terminal = new ProcessTerminal();
143684
- const ui = new TUI(terminal);
143685
- ui.setClearOnShrink(true);
144308
+ const ui = new TuiAltScreen(terminal);
143686
144309
  const uiAny = ui;
143687
144310
  const originalDoRender = uiAny["doRender"].bind(ui);
143688
144311
  let recovering = false;
@@ -143708,6 +144331,7 @@ function createTUIState(options) {
143708
144331
  };
143709
144332
  const transcriptContainer = new GutterContainer(1, 1);
143710
144333
  const activityContainer = new GutterContainer(1, 1);
144334
+ const statusBarContainer = new GutterContainer(1, 1);
143711
144335
  const todoPanelContainer = new GutterContainer(1, 1);
143712
144336
  const todoPanel = new TodoPanelComponent(theme.colors);
143713
144337
  const queueContainer = new GutterContainer(1, 1);
@@ -143725,8 +144349,10 @@ function createTUIState(options) {
143725
144349
  return {
143726
144350
  ui,
143727
144351
  terminal,
144352
+ layoutRoot: void 0,
143728
144353
  transcriptContainer,
143729
144354
  activityContainer,
144355
+ statusBarContainer,
143730
144356
  todoPanelContainer,
143731
144357
  todoPanel,
143732
144358
  queueContainer,
@@ -144484,7 +145110,7 @@ var SessionManager$1 = class {
144484
145110
  }
144485
145111
  resetSessionRuntime() {
144486
145112
  this.host.state.queuedMessages = [];
144487
- this.host.harness.interactiveAgentId = MAIN_AGENT_ID;
145113
+ this.host.harness.interactiveAgentId = MAIN_AGENT_ID$1;
144488
145114
  this.host.streamingUI.discardPending();
144489
145115
  this.host.streamingUI.resetToolCallState();
144490
145116
  this.host.streamingUI.resetToolUi();
@@ -144634,7 +145260,7 @@ var ApprovalPanelComponent = class extends Container {
144634
145260
  };
144635
145261
  if (this.ui !== void 0) this.intervalId = setInterval(() => {
144636
145262
  this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
144637
- this.ui?.requestComponentRender(this);
145263
+ this.ui?.requestRender();
144638
145264
  }, 100);
144639
145265
  }
144640
145266
  /** Stop the selection pulse. Idempotent; called on submit and on hide. */
@@ -145863,7 +146489,7 @@ var QuestionDialogComponent = class extends Container {
145863
146489
  this.answers = Array.from({ length: total }, () => void 0);
145864
146490
  if (this.ui !== void 0) this.intervalId = setInterval(() => {
145865
146491
  this.animFrame = (this.animFrame + 1) % PIXEL_PULSE_FRAMES.length;
145866
- this.ui?.requestComponentRender(this);
146492
+ this.ui?.requestRender();
145867
146493
  }, 100);
145868
146494
  }
145869
146495
  /** Stop the wizard pulse. Idempotent; called on respond and on hide. */
@@ -146575,7 +147201,7 @@ var DialogManager = class {
146575
147201
  }
146576
147202
  openApprovalPreview(panel, block) {
146577
147203
  if (this.approvalPreview !== void 0) return;
146578
- const savedChildren = [...this.host.state.ui.children];
147204
+ const savedLayoutRoot = this.host.state.layoutRoot;
146579
147205
  const viewer = new ApprovalPreviewViewer({
146580
147206
  block,
146581
147207
  colors: this.host.state.theme.colors,
@@ -146583,13 +147209,12 @@ var DialogManager = class {
146583
147209
  this.closeApprovalPreview();
146584
147210
  }
146585
147211
  }, this.host.state.terminal);
146586
- this.host.state.ui.clear();
146587
- this.host.state.ui.addChild(viewer);
147212
+ this.host.state.ui.setLayoutRoot(viewer);
146588
147213
  this.host.state.ui.setFocus(viewer);
146589
147214
  this.host.state.ui.requestRender(true);
146590
147215
  this.approvalPreview = {
146591
147216
  component: viewer,
146592
- savedChildren,
147217
+ savedLayoutRoot,
146593
147218
  panel
146594
147219
  };
146595
147220
  }
@@ -146597,8 +147222,7 @@ var DialogManager = class {
146597
147222
  const preview = this.approvalPreview;
146598
147223
  if (preview === void 0) return;
146599
147224
  this.approvalPreview = void 0;
146600
- this.host.state.ui.clear();
146601
- for (const child of preview.savedChildren) this.host.state.ui.addChild(child);
147225
+ this.host.state.ui.setLayoutRoot(preview.savedLayoutRoot);
146602
147226
  this.host.state.ui.setFocus(preview.panel);
146603
147227
  this.host.state.ui.requestRender(true);
146604
147228
  }
@@ -146689,7 +147313,6 @@ var ScreamTUI = class {
146689
147313
  deferUserMessages = false;
146690
147314
  aborted = false;
146691
147315
  isShuttingDown = false;
146692
- tightModeHandler = null;
146693
147316
  reverseRpcDisposers = [];
146694
147317
  startupNotice;
146695
147318
  /** Interval handle for the periodic provider-balance refresh. */
@@ -146788,11 +147411,6 @@ var ScreamTUI = class {
146788
147411
  this.inputController.setupAutocomplete();
146789
147412
  }
146790
147413
  async start() {
146791
- this.tightModeHandler = () => {
146792
- setTightMode((process.stdout.columns ?? 80) < 60);
146793
- };
146794
- this.tightModeHandler();
146795
- process.stdout.on("resize", this.tightModeHandler);
146796
147414
  this.lifecycleController.installSignalHandlers();
146797
147415
  try {
146798
147416
  const shouldReplayHistory = await this.initMainTui();
@@ -146883,11 +147501,6 @@ var ScreamTUI = class {
146883
147501
  async stop(exitCode) {
146884
147502
  if (this.isShuttingDown) return;
146885
147503
  this.isShuttingDown = true;
146886
- if (this.tightModeHandler !== null) {
146887
- process.stdout.off("resize", this.tightModeHandler);
146888
- this.tightModeHandler = null;
146889
- }
146890
- setTightMode(false);
146891
147504
  this.lifecycleController.stopCcConnectPolling();
146892
147505
  if (this.balancePollTimer !== void 0) {
146893
147506
  clearInterval(this.balancePollTimer);
@@ -147154,6 +147767,7 @@ var ScreamTUI = class {
147154
147767
  this.sessionEventHandler.stopAllMcpServerStatusSpinners();
147155
147768
  clearGoalState();
147156
147769
  clearInfoPanelState(this.state);
147770
+ clearEvalPanelState(this.state);
147157
147771
  this.state.terminal.write("\x1B[3J");
147158
147772
  this.transcriptController.clearAndRedraw();
147159
147773
  this.state.ui.requestRender(true);