scream-code 0.11.6 → 0.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-K1FbYyug.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-DYHGTG6m.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";
@@ -74934,7 +74934,7 @@ var BashTool = class {
74934
74934
  },
74935
74935
  approvalRule: literalRulePattern(this.name, args.command),
74936
74936
  matchesRule: (ruleArgs) => matchesGlobRuleSubject(ruleArgs, args.command),
74937
- execute: ({ signal }) => this.execution(args, signal)
74937
+ execute: (ctx) => this.execution(args, ctx)
74938
74938
  };
74939
74939
  }
74940
74940
  spawn(effectiveCwd, command) {
@@ -74954,7 +74954,8 @@ var BashTool = class {
74954
74954
  };
74955
74955
  return this.jian.execWithEnv(shellArgs, mergedEnv);
74956
74956
  }
74957
- async execution(args, signal) {
74957
+ async execution(args, ctx) {
74958
+ const { signal, onUpdate } = ctx;
74958
74959
  const completedBg = drainCompletedBackgroundTasks();
74959
74960
  const bgPrefix = completedBg.length > 0 ? completedBg.map((t) => `[Background task ${t.id} completed] Command: ${t.command}\nExit code: ${String(t.exitCode)} (${(t.elapsedMs / 1e3).toFixed(1)}s)\nOutput:\n${t.output}\n---\n`).join("") : "";
74960
74961
  if (signal.aborted) return {
@@ -74993,6 +74994,14 @@ var BashTool = class {
74993
74994
  } catch {}
74994
74995
  let aborted = false;
74995
74996
  let killed = false;
74997
+ let streaming = true;
74998
+ const forwardChunk = (kind, text) => {
74999
+ if (!streaming || signal.aborted || onUpdate === void 0) return;
75000
+ onUpdate({
75001
+ kind,
75002
+ text
75003
+ });
75004
+ };
74996
75005
  const killProc = async () => {
74997
75006
  if (killed) return;
74998
75007
  killed = true;
@@ -75030,17 +75039,39 @@ var BashTool = class {
75030
75039
  return artifactPath;
75031
75040
  } });
75032
75041
  if (bgPrefix.length > 0) builder.write(bgPrefix);
75033
- const completionPromise = Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder), readStreamIntoBuilder(proc.stderr, builder)]), proc.wait()]).then(([, exitCode]) => ({
75042
+ const completionPromise = Promise.all([Promise.all([readStreamIntoBuilder(proc.stdout, builder, (text) => forwardChunk("stdout", text)), readStreamIntoBuilder(proc.stderr, builder, (text) => forwardChunk("stderr", text))]), proc.wait()]).then(([, exitCode]) => ({
75034
75043
  timedOut: false,
75035
75044
  exitCode
75036
75045
  }));
75037
75046
  const raceResult = timeoutMs !== void 0 ? await Promise.race([completionPromise, timeoutPromise]) : await completionPromise;
75038
75047
  if (raceResult.timedOut) {
75048
+ streaming = false;
75039
75049
  const timeoutLabel = timeoutMs % 1e3 === 0 ? `${String(timeoutMs / 1e3)}s` : `${String(timeoutMs)}ms`;
75040
75050
  const taskId = createBackgroundTask(command, completionPromise.then(({ exitCode }) => ({
75041
75051
  exitCode,
75042
75052
  output: builder.toString()
75043
75053
  })));
75054
+ if (onUpdate !== void 0) completionPromise.then(({ exitCode }) => {
75055
+ onUpdate({
75056
+ kind: "custom",
75057
+ customKind: "background.task.terminated",
75058
+ customData: {
75059
+ id: taskId,
75060
+ command,
75061
+ exitCode
75062
+ }
75063
+ });
75064
+ }, () => {
75065
+ onUpdate({
75066
+ kind: "custom",
75067
+ customKind: "background.task.terminated",
75068
+ customData: {
75069
+ id: taskId,
75070
+ command,
75071
+ exitCode: -1
75072
+ }
75073
+ });
75074
+ });
75044
75075
  const outputSoFar = builder.toString();
75045
75076
  return {
75046
75077
  output: `Command timed out after ${timeoutLabel} but is still running in the background (task: ${taskId}).\nOutput so far (${String(builder.nChars)} chars):\n${outputSoFar}\n---\nThe command will complete in the background. The result will be included in your next Bash call.`,
@@ -75138,13 +75169,33 @@ human_shell_hint: Tell the human to run /tasks to open the interactive backgroun
75138
75169
  return builder.ok("Background task started", { brief: `Started ${taskId}` });
75139
75170
  }
75140
75171
  };
75141
- async function readStreamIntoBuilder(stream, builder) {
75172
+ const LIVE_OUTPUT_FLUSH_MS = 100;
75173
+ async function readStreamIntoBuilder(stream, builder, onChunk) {
75142
75174
  const decoder = new StringDecoder("utf8");
75175
+ let pending = "";
75176
+ let lastFlush = 0;
75177
+ const flush = (force) => {
75178
+ if (pending.length === 0) return;
75179
+ if (!force && Date.now() - lastFlush < LIVE_OUTPUT_FLUSH_MS) return;
75180
+ onChunk?.(pending);
75181
+ pending = "";
75182
+ lastFlush = Date.now();
75183
+ };
75143
75184
  for await (const chunk of stream) {
75144
75185
  const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
75145
- builder.write(decoder.write(buf));
75186
+ const text = decoder.write(buf);
75187
+ builder.write(text);
75188
+ if (onChunk !== void 0) {
75189
+ pending += text;
75190
+ flush(false);
75191
+ }
75192
+ }
75193
+ const tail = decoder.end();
75194
+ builder.write(tail);
75195
+ if (onChunk !== void 0) {
75196
+ pending += tail;
75197
+ flush(true);
75146
75198
  }
75147
- builder.write(decoder.end());
75148
75199
  }
75149
75200
  function shellQuote$1(s) {
75150
75201
  return `'${s.replaceAll("'", "'\\''")}'`;
@@ -119182,6 +119233,77 @@ function parsePositiveInt(value) {
119182
119233
  return n;
119183
119234
  }
119184
119235
  //#endregion
119236
+ //#region ../../packages/agent-core/src/session/provider-balance.ts
119237
+ const fetchers = [{
119238
+ matches: (baseUrl) => {
119239
+ try {
119240
+ return new URL(baseUrl).hostname === "api.deepseek.com";
119241
+ } catch {
119242
+ return false;
119243
+ }
119244
+ },
119245
+ fetch: async (baseUrl, apiKey) => {
119246
+ try {
119247
+ const root = new URL(baseUrl).origin;
119248
+ const res = await fetch(`${root}/user/balance`, {
119249
+ headers: {
119250
+ Accept: "application/json",
119251
+ Authorization: `Bearer ${apiKey}`
119252
+ },
119253
+ signal: AbortSignal.timeout(5e3)
119254
+ });
119255
+ if (!res.ok) return null;
119256
+ const info = (await res.json()).balance_infos?.[0];
119257
+ if (info?.total_balance === void 0) return null;
119258
+ return {
119259
+ currency: info.currency ?? "",
119260
+ totalBalance: info.total_balance
119261
+ };
119262
+ } catch {
119263
+ return null;
119264
+ }
119265
+ }
119266
+ }, {
119267
+ matches: (baseUrl) => {
119268
+ try {
119269
+ return new URL(baseUrl).hostname === "api.moonshot.cn";
119270
+ } catch {
119271
+ return false;
119272
+ }
119273
+ },
119274
+ fetch: async (baseUrl, apiKey) => {
119275
+ try {
119276
+ const root = new URL(baseUrl).origin;
119277
+ const res = await fetch(`${root}/v1/users/me/balance`, {
119278
+ headers: {
119279
+ Accept: "application/json",
119280
+ Authorization: `Bearer ${apiKey}`
119281
+ },
119282
+ signal: AbortSignal.timeout(5e3)
119283
+ });
119284
+ if (!res.ok) return null;
119285
+ const data = await res.json();
119286
+ if (typeof data.data?.available_balance !== "number") return null;
119287
+ return {
119288
+ currency: "CNY",
119289
+ totalBalance: data.data.available_balance.toFixed(2)
119290
+ };
119291
+ } catch {
119292
+ return null;
119293
+ }
119294
+ }
119295
+ }];
119296
+ /**
119297
+ * Look up the balance for the given provider endpoint.
119298
+ * Returns null when the endpoint is not a recognized official vendor or
119299
+ * when the lookup fails — callers render nothing in that case.
119300
+ */
119301
+ async function fetchProviderBalance(baseUrl, apiKey) {
119302
+ const fetcher = fetchers.find((f) => f.matches(baseUrl));
119303
+ if (!fetcher) return null;
119304
+ return fetcher.fetch(baseUrl, apiKey);
119305
+ }
119306
+ //#endregion
119185
119307
  //#region ../../packages/node-sdk/src/auth.ts
119186
119308
  var ScreamAuthFacade = class {
119187
119309
  options;
@@ -120602,7 +120724,7 @@ function optionalBuildString(value) {
120602
120724
  return typeof value === "string" && value.length > 0 ? value : void 0;
120603
120725
  }
120604
120726
  const SCREAM_BUILD_INFO = {
120605
- version: optionalBuildString("0.11.6"),
120727
+ version: optionalBuildString("0.11.8"),
120606
120728
  channel: optionalBuildString(""),
120607
120729
  commit: optionalBuildString(""),
120608
120730
  buildTarget: optionalBuildString("darwin-arm64")
@@ -120867,11 +120989,15 @@ const NotificationsConfigSchema = z.object({
120867
120989
  const TuiLikePreferencesSchema = z.object({
120868
120990
  nickname: z.string().optional(),
120869
120991
  tone: z.string().optional(),
120870
- other: z.string().optional()
120992
+ other: z.string().optional(),
120993
+ /** Explicit prohibitions: things the user does NOT want done. */
120994
+ doNot: z.string().optional()
120871
120995
  });
120872
120996
  const TuiConfigFileSchema = z.object({
120873
120997
  theme: TuiThemeSchema.optional(),
120874
120998
  language: z.enum(["zh", "en"]).optional(),
120999
+ /** Auto-enter the welcome page after the loading splash finishes. */
121000
+ autoStart: z.boolean().optional(),
120875
121001
  editor: z.object({ command: z.string().optional() }).optional(),
120876
121002
  notifications: z.object({
120877
121003
  enabled: z.boolean().optional(),
@@ -120887,6 +121013,7 @@ const TuiConfigFileSchema = z.object({
120887
121013
  const TuiConfigSchema = z.object({
120888
121014
  theme: TuiThemeSchema,
120889
121015
  language: z.enum(["zh", "en"]),
121016
+ autoStart: z.boolean(),
120890
121017
  editorCommand: z.string().nullable(),
120891
121018
  notifications: NotificationsConfigSchema,
120892
121019
  like: TuiLikePreferencesSchema,
@@ -120903,6 +121030,7 @@ const DEFAULT_NOTIFICATIONS_CONFIG = {
120903
121030
  const DEFAULT_TUI_CONFIG = TuiConfigSchema.parse({
120904
121031
  theme: "auto",
120905
121032
  language: getLocale(),
121033
+ autoStart: false,
120906
121034
  editorCommand: null,
120907
121035
  notifications: DEFAULT_NOTIFICATIONS_CONFIG,
120908
121036
  like: {},
@@ -120955,6 +121083,7 @@ function normalizeTuiConfig(config) {
120955
121083
  return TuiConfigSchema.parse({
120956
121084
  theme: config.theme ?? DEFAULT_TUI_CONFIG.theme,
120957
121085
  language: config.language ?? DEFAULT_TUI_CONFIG.language,
121086
+ autoStart: config.autoStart ?? DEFAULT_TUI_CONFIG.autoStart,
120958
121087
  editorCommand: command === void 0 || command.length === 0 ? null : command,
120959
121088
  notifications: {
120960
121089
  enabled: config.notifications?.enabled ?? DEFAULT_NOTIFICATIONS_CONFIG.enabled,
@@ -120998,6 +121127,7 @@ function renderTuiConfig(config) {
120998
121127
 
120999
121128
  theme = "${config.theme}" # "auto" | "dark" | "light"
121000
121129
  language = "${config.language}" # "zh" | "en"
121130
+ autoStart = ${String(config.autoStart)} # true = auto-enter welcome after loading
121001
121131
 
121002
121132
  [editor]
121003
121133
  command = "${escapeTomlBasicString(config.editorCommand ?? "")}" # Empty uses $VISUAL / $EDITOR
@@ -123250,7 +123380,7 @@ function mountProfileList(host) {
123250
123380
  const { subagentModels: bindings, availableModels } = host.state.appState;
123251
123381
  const options = getSubagentProfiles().map((profile) => {
123252
123382
  const alias = bindings[profile.name];
123253
- const bindingLabel = alias === void 0 ? t("subagent.follow_main") : modelDisplayName$1(alias, availableModels[alias]);
123383
+ const bindingLabel = alias === void 0 ? t("subagent.follow_main") : availableModels[alias] !== void 0 ? modelDisplayName$1(alias, availableModels[alias]) : t("subagent.stale_binding", { alias });
123254
123384
  return {
123255
123385
  value: profile.name,
123256
123386
  label: `${profile.name} → ${bindingLabel}`,
@@ -123304,6 +123434,10 @@ async function applyBinding(host, profileName, value) {
123304
123434
  const configPath = getTuiConfigPath();
123305
123435
  try {
123306
123436
  const current = await loadTuiConfig(configPath);
123437
+ if (value !== FOLLOW_MAIN && host.state.appState.availableModels[value] === void 0) {
123438
+ host.showError(t("subagent.invalid_alias", { alias: value }));
123439
+ return;
123440
+ }
123307
123441
  const updated = { ...current.subagentModels };
123308
123442
  if (value === FOLLOW_MAIN) delete updated[profileName];
123309
123443
  else updated[profileName] = value;
@@ -123813,14 +123947,33 @@ function formatTokenCount(n) {
123813
123947
  function safeUsage(usage) {
123814
123948
  return safeUsageRatio(usage);
123815
123949
  }
123950
+ const CONTEXT_BAR_WIDTH = 10;
123951
+ const CONTEXT_BAR_FILLED = "▰";
123952
+ const CONTEXT_BAR_EMPTY = "▱";
123953
+ function currencySymbol(currency) {
123954
+ if (currency === "CNY") return "¥";
123955
+ if (currency === "USD") return "$";
123956
+ return `${currency} `;
123957
+ }
123958
+ /**
123959
+ * Half-block progress bar for context usage: `▰▰▰▱▱▱▱▱▱▱` (10 cells).
123960
+ * Filled cells are rounded from the clamped ratio, so 0% is all-empty and
123961
+ * >=100% is all-filled; NaN/undefined coerce through safeUsageRatio first.
123962
+ */
123963
+ function formatContextBar(usage, width = CONTEXT_BAR_WIDTH) {
123964
+ const clamped = Math.min(1, Math.max(0, safeUsageRatio(usage)));
123965
+ const filled = Math.round(clamped * width);
123966
+ return CONTEXT_BAR_FILLED.repeat(filled) + CONTEXT_BAR_EMPTY.repeat(width - filled);
123967
+ }
123816
123968
  function formatContextStatus(usage, tokens, maxTokens) {
123817
123969
  const pct = `${(safeUsage(usage) * 100).toFixed(1)}%`;
123970
+ const barAndPct = `${formatContextBar(usage)} ${pct}`;
123818
123971
  if (maxTokens && maxTokens > 0 && tokens !== void 0) return t("footer.context", {
123819
- pct,
123972
+ pct: barAndPct,
123820
123973
  tokens: formatTokenCount(tokens),
123821
123974
  maxTokens: formatTokenCount(maxTokens)
123822
123975
  });
123823
- return t("footer.context_short", { pct });
123976
+ return t("footer.context_short", { pct: barAndPct });
123824
123977
  }
123825
123978
  /** Format goal wall-clock duration compactly: `3m`, `1m30s`, `45s`. */
123826
123979
  function formatGoalDuration(ms) {
@@ -124023,8 +124176,12 @@ var FooterComponent = class {
124023
124176
  left.push(chalk.hex(colors.primary).bold(goalLabel));
124024
124177
  }
124025
124178
  const model = shortenModel(modelDisplayName(state));
124026
- if (model) if (state.streamingPhase === "thinking") left.push(shimmerText(model, colors));
124027
- else left.push(chalk.hex(colors.textDim)(model));
124179
+ if (model) {
124180
+ if (state.streamingPhase === "thinking") left.push(shimmerText(model, colors));
124181
+ else left.push(chalk.hex(colors.textDim)(model));
124182
+ const balance = state.providerBalance;
124183
+ if (balance !== null && balance !== void 0) left.push(chalk.hex(colors.textDim)(`${currencySymbol(balance.currency)}${balance.totalBalance}`));
124184
+ }
124028
124185
  if (this.backgroundBashTaskCount > 0) left.push(chalk.hex(colors.primary)(`[${t("footer.tasks_running", { count: String(this.backgroundBashTaskCount) })}]`));
124029
124186
  if (this.backgroundAgentCount > 0) left.push(chalk.hex(colors.primary)(`[${t("footer.agents_running", { count: String(this.backgroundAgentCount) })}]`));
124030
124187
  const git = this.gitCache.getStatus();
@@ -124093,6 +124250,7 @@ async function applyLanguageChoice(host, locale) {
124093
124250
  setLocale(locale);
124094
124251
  host.state.appState.language = locale;
124095
124252
  await saveTuiConfig({
124253
+ ...await loadTuiConfig(),
124096
124254
  theme: host.state.appState.theme,
124097
124255
  language: locale,
124098
124256
  editorCommand: host.state.appState.editorCommand,
@@ -124470,6 +124628,7 @@ function createMarkdownTheme(colors) {
124470
124628
  quote: (text) => chalk.hex(colors.mdQuote)(text),
124471
124629
  quoteBorder: (text) => chalk.hex(colors.mdQuote)(text),
124472
124630
  hr: (text) => border(text),
124631
+ tableHeader: (text) => chalk.bold.hex(colors.accent)(text),
124473
124632
  listBullet: (text) => chalk.hex(colors.roleAssistant)(text.replace(/^-/, "•")),
124474
124633
  bold: (text) => chalk.bold(text),
124475
124634
  italic: (text) => chalk.italic(text),
@@ -124861,6 +125020,84 @@ async function loadManagedUsageReport(host) {
124861
125020
  } };
124862
125021
  }
124863
125022
  //#endregion
125023
+ //#region src/tui/api-balance.ts
125024
+ /**
125025
+ * TUI-side provider balance lookup.
125026
+ *
125027
+ * Reads the runtime config.toml (same file the engine consumes), resolves
125028
+ * the plaintext API key (direct value or the provider's ENV_VAR env
125029
+ * reference), and delegates the actual query to the engine's
125030
+ * fetchProviderBalance. Results are cached briefly so footer renders do
125031
+ * not hammer the vendor endpoint.
125032
+ */
125033
+ const BALANCE_CACHE_MS = 6e4;
125034
+ const cache = /* @__PURE__ */ new Map();
125035
+ /** Monotonic id so an out-of-order lookup can never overwrite a newer one. */
125036
+ let latestRequestId = 0;
125037
+ function resolveApiKey(providerName) {
125038
+ const provider = readConfigFile(join(getDataDir(), "config.toml")).providers?.[providerName];
125039
+ if (!provider) return void 0;
125040
+ const direct = provider.apiKey?.trim();
125041
+ if (direct !== void 0 && direct.length > 0) return direct;
125042
+ const envKey = envKeyForProviderType(provider.type);
125043
+ const stored = envKey !== void 0 ? provider.env?.[envKey]?.trim() : void 0;
125044
+ return stored !== void 0 && stored.length > 0 ? stored : void 0;
125045
+ }
125046
+ function envKeyForProviderType(type) {
125047
+ switch (type) {
125048
+ case "anthropic": return "ANTHROPIC_API_KEY";
125049
+ case "openai":
125050
+ case "openai_responses": return "OPENAI_API_KEY";
125051
+ case "scream": return "SCREAM_API_KEY";
125052
+ case "google-genai": return "GOOGLE_API_KEY";
125053
+ case "vertexai": return "VERTEXAI_API_KEY";
125054
+ default: return;
125055
+ }
125056
+ }
125057
+ async function loadBalance(providerName) {
125058
+ try {
125059
+ const provider = readConfigFile(join(getDataDir(), "config.toml")).providers?.[providerName];
125060
+ if (!provider?.baseUrl) return null;
125061
+ const apiKey = resolveApiKey(providerName);
125062
+ if (apiKey === void 0) return null;
125063
+ return await fetchProviderBalance(provider.baseUrl, apiKey);
125064
+ } catch {
125065
+ return null;
125066
+ }
125067
+ }
125068
+ /**
125069
+ * Look up the balance for the provider that serves the given model
125070
+ * (model names follow the "provider/model" convention). Returns null when
125071
+ * the provider is unknown, unofficial, or the lookup failed — callers
125072
+ * render nothing in that case. Cached for BALANCE_CACHE_MS per provider.
125073
+ */
125074
+ async function getProviderBalanceForModel(model) {
125075
+ const providerName = model.split("/")[0] ?? "";
125076
+ if (providerName.length === 0) return null;
125077
+ const cached = cache.get(providerName);
125078
+ if (cached !== void 0 && Date.now() - cached.at < BALANCE_CACHE_MS) return cached.balance;
125079
+ const balance = await loadBalance(providerName);
125080
+ cache.set(providerName, {
125081
+ balance,
125082
+ at: Date.now()
125083
+ });
125084
+ return balance;
125085
+ }
125086
+ /**
125087
+ * Fetch the balance for a model and push it into app state. Fire-and-forget:
125088
+ * failures resolve to null inside getProviderBalanceForModel, so callers
125089
+ * (startup sync and model switches) never await it. A request id guards
125090
+ * against out-of-order resolutions: when the user switches models quickly,
125091
+ * only the latest lookup's result is committed.
125092
+ */
125093
+ function refreshProviderBalance(model, setAppState) {
125094
+ const requestId = ++latestRequestId;
125095
+ getProviderBalanceForModel(model).then((balance) => {
125096
+ if (requestId !== latestRequestId) return;
125097
+ setAppState({ providerBalance: balance });
125098
+ });
125099
+ }
125100
+ //#endregion
124864
125101
  //#region src/tui/commands/config.ts
124865
125102
  /**
124866
125103
  * Storm Breaker guard for model switches. Returns the (currentTokens,
@@ -125159,6 +125396,7 @@ async function applyEditorChoice(host, value) {
125159
125396
  const editorCommand = value.length > 0 ? value : null;
125160
125397
  try {
125161
125398
  await saveTuiConfig({
125399
+ ...await loadTuiConfig(),
125162
125400
  theme: host.state.appState.theme,
125163
125401
  language: host.state.appState.language,
125164
125402
  editorCommand,
@@ -125286,8 +125524,10 @@ async function performModelSwitch(host, alias, thinkingLevel) {
125286
125524
  }
125287
125525
  host.setAppState({
125288
125526
  model: effectiveAlias,
125289
- thinkingLevel: effectiveThinking
125527
+ thinkingLevel: effectiveThinking,
125528
+ providerBalance: null
125290
125529
  });
125530
+ refreshProviderBalance(effectiveAlias, (patch) => host.setAppState(patch));
125291
125531
  let persisted = false;
125292
125532
  try {
125293
125533
  persisted = await persistModelSelection(host, alias, thinkingLevel);
@@ -125344,6 +125584,7 @@ async function applyThemeChoice(host, theme) {
125344
125584
  }
125345
125585
  try {
125346
125586
  await saveTuiConfig({
125587
+ ...await loadTuiConfig(),
125347
125588
  theme,
125348
125589
  language: host.state.appState.language,
125349
125590
  editorCommand: host.state.appState.editorCommand,
@@ -126121,7 +126362,7 @@ async function guidedGoalSetup(host) {
126121
126362
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
126122
126363
  return;
126123
126364
  }
126124
- const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126365
+ const { TextInputDialogComponent } = await import("./text-input-dialog-DX3hKoBF.mjs");
126125
126366
  const initialDesc = await promptText(host, TextInputDialogComponent, {
126126
126367
  title: t("goal.setup_title_initial"),
126127
126368
  subtitle: t("goal.setup_desc_hint"),
@@ -126142,7 +126383,7 @@ async function guidedGoalSetup(host) {
126142
126383
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
126143
126384
  }
126144
126385
  async function showGoalConfigWizard(host, session, objective, replace) {
126145
- const { TextInputDialogComponent } = await import("./text-input-dialog-Crp_BPkd.mjs");
126386
+ const { TextInputDialogComponent } = await import("./text-input-dialog-DX3hKoBF.mjs");
126146
126387
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
126147
126388
  title: t("goal.wizard_title", { objective }),
126148
126389
  subtitle: t("goal.budget_turns_hint"),
@@ -128529,7 +128770,7 @@ const globGlance = (_toolCall, result, colors) => {
128529
128770
  const more = names.length - GLANCE_SAMPLES;
128530
128771
  return `${head}${shown}${more > 0 ? dim(` (+${String(more)})`) : ""}`;
128531
128772
  }).join(dim(" "));
128532
- const extLine = [...countExtensions(lines).entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_EXTENSION_COUNTS).map(([ext, count]) => `${dim(ext)}:${dim(` ${String(count)}`)}`).join(dim(", "));
128773
+ const extLine = [...countExtensions(lines).entries()].toSorted((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, MAX_EXTENSION_COUNTS).map(([ext, count]) => `${dim(ext)}:${dim(` ${String(count)}`)}`).join(dim(", "));
128533
128774
  if (extLine.length === 0) return dirLine;
128534
128775
  return `${dirLine}${dim(" ")}${extLine}`;
128535
128776
  };
@@ -128559,8 +128800,56 @@ const readGlance = (toolCall, result, colors) => {
128559
128800
  if (parts.length === 0) return "";
128560
128801
  return parts.join(dim(" · "));
128561
128802
  };
128803
+ /**
128804
+ * Parse the fixed WebSearch output protocol emitted by
128805
+ * `web-search.ts` (`Title: …` / `Date: …` / `URL: …` / `Snippet: …`,
128806
+ * entries separated by `---`) into structured entries so the collapsed
128807
+ * card can show a title/URL glance instead of a bare "N results" chip.
128808
+ */
128809
+ function parseWebSearchOutput(output) {
128810
+ const entries = [];
128811
+ let title;
128812
+ let url;
128813
+ let inSnippet = false;
128814
+ for (const line of output.split("\n")) {
128815
+ if (line.startsWith("---")) {
128816
+ if (title !== void 0 || url !== void 0) entries.push({
128817
+ title: title ?? "",
128818
+ url: url ?? ""
128819
+ });
128820
+ title = void 0;
128821
+ url = void 0;
128822
+ inSnippet = false;
128823
+ continue;
128824
+ }
128825
+ if (inSnippet) continue;
128826
+ if (line.startsWith("Snippet: ")) inSnippet = true;
128827
+ else if (line.startsWith("Title: ")) title = line.slice(7);
128828
+ else if (line.startsWith("URL: ")) url = line.slice(5);
128829
+ }
128830
+ if (title !== void 0 || url !== void 0) entries.push({
128831
+ title: title ?? "",
128832
+ url: url ?? ""
128833
+ });
128834
+ return entries;
128835
+ }
128836
+ function truncateText(text, max = 60) {
128837
+ return text.length <= max ? text : `${text.slice(0, max - 1)}…`;
128838
+ }
128839
+ const webSearchGlance = (_toolCall, result, colors) => {
128840
+ const entries = parseWebSearchOutput(result.output);
128841
+ if (entries.length === 0) return "";
128842
+ const titleColor = chalk.hex(colors.roleTool);
128843
+ const dim = chalk.dim;
128844
+ const lines = entries.slice(0, GLANCE_SAMPLES).map((e) => {
128845
+ return [e.title.length > 0 ? titleColor(truncateText(e.title)) : "", e.url.length > 0 ? dim(truncateText(e.url)) : ""].filter((s) => s.length > 0).join(dim(" — "));
128846
+ });
128847
+ const remaining = entries.length - GLANCE_SAMPLES;
128848
+ if (remaining > 0) lines.push(dim(`+${String(remaining)} more`));
128849
+ return lines.join("\n");
128850
+ };
128562
128851
  const fetchSummary = withGlance(null);
128563
- const webSearchSummary = withGlance(null);
128852
+ const webSearchSummary = withGlance(webSearchGlance);
128564
128853
  const thinkSummary = withGlance(null);
128565
128854
  const editSummary = withGlance(null);
128566
128855
  const writeSummary = withGlance(null);
@@ -128608,6 +128897,7 @@ const STREAMING_PROGRESS_INTERVAL_MS = 1e3;
128608
128897
  const SUBAGENT_ELAPSED_INTERVAL_MS = 1e3;
128609
128898
  const PROGRESS_URL_RE = /https?:\/\/\S+/g;
128610
128899
  const MAX_PROGRESS_LINE_CHARS = 1e4;
128900
+ const MAX_LIVE_OUTPUT_CHARS = 5e4;
128611
128901
  function backgroundFailureMessage(status) {
128612
128902
  switch (status) {
128613
128903
  case "lost": return t("toolcall.bg_agent_lost");
@@ -128926,6 +129216,10 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
128926
129216
  subagentEndedAtMs;
128927
129217
  progressLines = [];
128928
129218
  static MAX_PROGRESS_LINES = 24;
129219
+ /** Live stdout/stderr accumulated via appendLiveOutput while the tool runs. */
129220
+ liveOutput = "";
129221
+ /** Session permission mode at card creation; drives ExitPlanMode chip wording. */
129222
+ permissionMode;
128929
129223
  writeStreamContentStart = -1;
128930
129224
  writeStreamNlScanOffset = 0;
128931
129225
  writeStreamNlCount = 0;
@@ -128988,6 +129282,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
128988
129282
  setResult(result) {
128989
129283
  this.result = result;
128990
129284
  this.progressLines = [];
129285
+ this.liveOutput = "";
128991
129286
  this.finalizeSubagentElapsedIfNeeded();
128992
129287
  this.syncStreamingProgressTimer();
128993
129288
  this.syncSubagentElapsedTimer();
@@ -129018,6 +129313,31 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129018
129313
  this.notifySnapshotChange();
129019
129314
  this.ui?.requestRender();
129020
129315
  }
129316
+ /**
129317
+ * Append live stdout/stderr from a running tool (Bash). Kept separate
129318
+ * from appendProgress so streaming command output renders with the same
129319
+ * tail-preview styling as the final result. The buffer is capped and
129320
+ * tail-preserving so a runaway command cannot grow the box unboundedly;
129321
+ * the block is dropped entirely once the real result lands.
129322
+ */
129323
+ appendLiveOutput(text) {
129324
+ if (this.result !== void 0 || text.length === 0) return;
129325
+ this.liveOutput += text;
129326
+ if (this.liveOutput.length > MAX_LIVE_OUTPUT_CHARS) this.liveOutput = `[...truncated]\n${this.liveOutput.slice(this.liveOutput.length - MAX_LIVE_OUTPUT_CHARS)}`;
129327
+ this.rebuildContent();
129328
+ this.notifySnapshotChange();
129329
+ this.ui?.requestRender();
129330
+ }
129331
+ /**
129332
+ * Records the session permission mode so ExitPlanMode can render an
129333
+ * honest "auto-approved" chip when the plan passed without user review.
129334
+ */
129335
+ setPermissionMode(mode) {
129336
+ if (this.permissionMode === mode) return;
129337
+ this.permissionMode = mode;
129338
+ this.headerText.setText(this.buildHeader());
129339
+ this.ui?.requestRender();
129340
+ }
129021
129341
  dispose() {
129022
129342
  if (this.disposed) return;
129023
129343
  this.disposed = true;
@@ -129433,6 +129753,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129433
129753
  if (!isFinished || result === void 0 || result.is_error === true) return label;
129434
129754
  const outcome = interpretExitPlanModeOutcome(result.output);
129435
129755
  if (outcome.kind === "approved") {
129756
+ if (this.permissionMode === "auto" && outcome.chosen === void 0) return `${label}${chalk.hex(colors.warning)(` · ${t("toolcall.auto_approved")}`)}`;
129436
129757
  const chipText = outcome.chosen !== void 0 && outcome.chosen.length > 0 ? t("toolcall.approved", { chosen: outcome.chosen }) : t("toolcall.approved_label");
129437
129758
  return `${label}${chalk.hex(colors.success)(` · ${chipText}`)}`;
129438
129759
  }
@@ -129465,6 +129786,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129465
129786
  this.markDirty();
129466
129787
  while (this.children.length > this.callPreviewEndIndex) this.children.pop();
129467
129788
  this.buildProgressBlock();
129789
+ this.buildLiveOutputBlock();
129468
129790
  this.buildContent();
129469
129791
  this.buildSubagentBlock();
129470
129792
  }
@@ -129474,6 +129796,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129474
129796
  this.buildCallPreview();
129475
129797
  this.callPreviewEndIndex = this.children.length;
129476
129798
  this.buildProgressBlock();
129799
+ this.buildLiveOutputBlock();
129477
129800
  this.buildContent();
129478
129801
  this.buildSubagentBlock();
129479
129802
  }
@@ -129503,6 +129826,25 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
129503
129826
  this.addChild(new Text(styled, 2, 0));
129504
129827
  }
129505
129828
  }
129829
+ /**
129830
+ * Render live stdout/stderr while the tool is still running. Reuses the
129831
+ * shell result renderer so the streaming tail matches the final output's
129832
+ * preview styling (including ctrl+o expansion); the block is skipped once
129833
+ * the real result has landed.
129834
+ */
129835
+ buildLiveOutputBlock() {
129836
+ if (this.result !== void 0) return;
129837
+ if (this.liveOutput.length === 0) return;
129838
+ const components = shellExecutionResultRenderer(this.toolCall, {
129839
+ tool_call_id: this.toolCall.id,
129840
+ output: this.liveOutput,
129841
+ is_error: false
129842
+ }, {
129843
+ expanded: this.expanded,
129844
+ colors: this.colors
129845
+ });
129846
+ for (const component of components) this.addChild(component);
129847
+ }
129506
129848
  buildSubagentBlock() {
129507
129849
  if (this.subagentAgentId === void 0 && this.ongoingSubCalls.size === 0 && this.finishedSubCalls.length === 0 && this.subagentText.length === 0 && this.subagentPhase === void 0 && this.backgroundTaskTerminalPhase === void 0) return;
129508
129850
  if (this.isSingleSubagentView()) {
@@ -132442,8 +132784,11 @@ function buildRoleAdditionalText(prefs) {
132442
132784
  if (prefs.nickname !== void 0 && prefs.nickname.trim().length > 0) items.push(`- Nickname: address the user as "${prefs.nickname.trim()}".`);
132443
132785
  if (prefs.tone !== void 0 && prefs.tone.trim().length > 0) items.push(`- Tone: respond in ${prefs.tone.trim()} tone.`);
132444
132786
  if (prefs.other !== void 0 && prefs.other.trim().length > 0) items.push(`- Other: ${prefs.other.trim()}`);
132445
- if (items.length === 0) return "";
132446
- lines.push("", ...items, "", t("like.priority"));
132787
+ const doNot = prefs.doNot?.trim();
132788
+ if (items.length === 0 && (doNot === void 0 || doNot.length === 0)) return "";
132789
+ lines.push("", ...items);
132790
+ if (doNot !== void 0 && doNot.length > 0) lines.push("", "## Do NOT (explicit prohibitions — NEVER do these)", doNot);
132791
+ lines.push("", t("like.priority"));
132447
132792
  return lines.join("\n");
132448
132793
  }
132449
132794
  async function getUserPrefsPath() {
@@ -132451,12 +132796,23 @@ async function getUserPrefsPath() {
132451
132796
  }
132452
132797
  async function persistLikePreferences(host, prefs) {
132453
132798
  const configPath = getTuiConfigPath();
132454
- await saveTuiConfig({
132455
- ...await loadTuiConfig(configPath),
132799
+ const current = await loadTuiConfig(configPath);
132800
+ const updated = {
132801
+ ...current,
132456
132802
  like: prefs
132457
- }, configPath);
132458
- const roleAdditional = buildRoleAdditionalText(prefs);
132459
- await writeFile(await getUserPrefsPath(), roleAdditional, "utf-8");
132803
+ };
132804
+ try {
132805
+ await saveTuiConfig(updated, configPath);
132806
+ await writeFile(await getUserPrefsPath(), buildRoleAdditionalText(prefs), "utf-8");
132807
+ } catch (error) {
132808
+ try {
132809
+ await saveTuiConfig(current, configPath);
132810
+ } catch {}
132811
+ try {
132812
+ await writeFile(await getUserPrefsPath(), buildRoleAdditionalText(current.like ?? {}), "utf-8");
132813
+ } catch {}
132814
+ throw error;
132815
+ }
132460
132816
  host.setAppState({ like: prefs });
132461
132817
  }
132462
132818
  async function handleLikeCommand(host) {
@@ -132491,10 +132847,21 @@ async function handleLikeCommand(host) {
132491
132847
  host.showStatus(t("like.cancelled"), host.state.theme.colors.textDim);
132492
132848
  return;
132493
132849
  }
132850
+ const doNot = await promptTextInput$1(host, t("like.do_not"), {
132851
+ subtitle: t("like.do_not_hint"),
132852
+ placeholder: t("like.do_not_example"),
132853
+ initialValue: current.doNot,
132854
+ allowEmpty: true
132855
+ });
132856
+ if (doNot === void 0) {
132857
+ host.showStatus(t("like.cancelled"), host.state.theme.colors.textDim);
132858
+ return;
132859
+ }
132494
132860
  await persistLikePreferences(host, {
132495
132861
  nickname: nickname.trim().length > 0 ? nickname.trim() : void 0,
132496
132862
  tone: tone.trim().length > 0 ? tone.trim() : void 0,
132497
- other: other.trim().length > 0 ? other.trim() : void 0
132863
+ other: other.trim().length > 0 ? other.trim() : void 0,
132864
+ doNot: doNot.trim().length > 0 ? doNot.trim() : void 0
132498
132865
  });
132499
132866
  host.showStatus(t("like.saved"), host.state.theme.colors.success);
132500
132867
  }
@@ -136525,12 +136892,32 @@ var SessionEventHandler = class {
136525
136892
  streamingUI.scheduleFlush();
136526
136893
  }
136527
136894
  handleToolProgress(event) {
136528
- if (event.update.kind !== "status") return;
136895
+ if (event.update.kind === "custom" && event.update.customKind === "background.task.terminated") {
136896
+ const data = event.update.customData;
136897
+ const id = typeof data?.id === "string" ? data.id : "";
136898
+ const command = typeof data?.command === "string" ? data.command : "";
136899
+ const exitCode = typeof data?.exitCode === "number" ? data.exitCode : -1;
136900
+ const preview = command.length > 60 ? `${command.slice(0, 59)}…` : command;
136901
+ if (exitCode === 0) this.host.showNotice(t("bash.background_completed", {
136902
+ id,
136903
+ command: preview
136904
+ }));
136905
+ else this.host.showNotice(t("bash.background_failed", {
136906
+ id,
136907
+ command: preview,
136908
+ exitCode: String(exitCode)
136909
+ }));
136910
+ return;
136911
+ }
136529
136912
  const text = event.update.text;
136530
136913
  if (text === void 0 || text.length === 0) return;
136531
136914
  const tc = this.host.streamingUI.getToolComponent(event.toolCallId);
136532
136915
  if (tc === void 0) return;
136533
- tc.appendProgress(text);
136916
+ if (event.update.kind === "status") {
136917
+ tc.appendProgress(text);
136918
+ return;
136919
+ }
136920
+ if (event.update.kind === "stdout" || event.update.kind === "stderr") tc.appendLiveOutput(text);
136534
136921
  }
136535
136922
  handleToolResult(event) {
136536
136923
  const { streamingUI } = this.host;
@@ -138146,6 +138533,7 @@ var StreamingUIController = class {
138146
138533
  }
138147
138534
  const { state } = this.host;
138148
138535
  const tc = new ToolCallComponent(toolCall, void 0, state.theme.colors, state.ui, state.theme.markdownTheme, state.appState.workDir);
138536
+ tc.setPermissionMode(state.appState.permissionMode);
138149
138537
  const entry = {
138150
138538
  id: nextTranscriptId(),
138151
138539
  kind: "tool_call",
@@ -138231,6 +138619,7 @@ var StreamingUIController = class {
138231
138619
  }
138232
138620
  if (matchedCall?.name === "AskUserQuestion") {
138233
138621
  const completed = new ToolCallComponent(matchedCall, result, state.theme.colors, state.ui, state.theme.markdownTheme, state.appState.workDir);
138622
+ completed.setPermissionMode(state.appState.permissionMode);
138234
138623
  if (state.toolOutputExpanded) completed.setExpanded(true);
138235
138624
  if (state.planExpanded) completed.setPlanExpanded(true);
138236
138625
  const entry = {
@@ -142770,8 +143159,10 @@ var SessionManager$1 = class {
142770
143159
  wallClockBaseAt: Date.now()
142771
143160
  } : null,
142772
143161
  goalActive: goal?.status === "active",
142773
- goalContinuationCount: 0
143162
+ goalContinuationCount: 0,
143163
+ providerBalance: null
142774
143164
  });
143165
+ refreshProviderBalance(status.model ?? "", (patch) => this.host.setAppState(patch));
142775
143166
  }
142776
143167
  async activateRuntime() {
142777
143168
  const session = this.requireSession();
@@ -143618,6 +144009,8 @@ var MemoryPickerComponent = class extends Container {
143618
144009
  searchQuery = "";
143619
144010
  isSearching = false;
143620
144011
  searchInput = "";
144012
+ /** Memo ids ticked for batch delete (space toggles, a selects all). */
144013
+ selectedIds = /* @__PURE__ */ new Set();
143621
144014
  memos;
143622
144015
  total;
143623
144016
  loading;
@@ -143646,6 +144039,7 @@ var MemoryPickerComponent = class extends Container {
143646
144039
  this.memos = result.memos;
143647
144040
  this.total = result.total;
143648
144041
  this.selectedIndex = 0;
144042
+ this.selectedIds.clear();
143649
144043
  } catch {
143650
144044
  this.memos = [];
143651
144045
  this.total = 0;
@@ -143677,8 +144071,11 @@ var MemoryPickerComponent = class extends Container {
143677
144071
  }
143678
144072
  if (this.mode === "confirmDelete") {
143679
144073
  if (matchesKey(data, Key.enter)) {
143680
- const memo = this.memos[this.selectedIndex];
143681
- if (memo) this.deleteAndReload(memo.id);
144074
+ if (this.selectedIds.size > 0) this.deleteSelectedAndReload();
144075
+ else {
144076
+ const memo = this.memos[this.selectedIndex];
144077
+ if (memo) this.deleteAndReload(memo.id);
144078
+ }
143682
144079
  return;
143683
144080
  }
143684
144081
  if (matchesKey(data, Key.escape)) {
@@ -143725,7 +144122,23 @@ var MemoryPickerComponent = class extends Container {
143725
144122
  return;
143726
144123
  }
143727
144124
  if (ch === "d" || ch === "D") {
143728
- if (this.memos.length > 0) this.mode = "confirmDelete";
144125
+ if (this.selectedIds.size > 0 || this.memos.length > 0) this.mode = "confirmDelete";
144126
+ return;
144127
+ }
144128
+ if (ch === " ") {
144129
+ const memo = this.memos[this.selectedIndex];
144130
+ if (memo) {
144131
+ if (this.selectedIds.has(memo.id)) this.selectedIds.delete(memo.id);
144132
+ else this.selectedIds.add(memo.id);
144133
+ this.ui?.requestRender();
144134
+ }
144135
+ return;
144136
+ }
144137
+ if (ch === "a" || ch === "A") {
144138
+ if (this.memos.length === 0) return;
144139
+ if (this.memos.every((memo) => this.selectedIds.has(memo.id))) this.selectedIds.clear();
144140
+ else for (const memo of this.memos) this.selectedIds.add(memo.id);
144141
+ this.ui?.requestRender();
143729
144142
  return;
143730
144143
  }
143731
144144
  if (ch === "/") {
@@ -143754,6 +144167,15 @@ var MemoryPickerComponent = class extends Container {
143754
144167
  if (this.selectedIndex >= this.memos.length) this.selectedIndex = Math.max(0, this.memos.length - 1);
143755
144168
  this.ui?.requestRender();
143756
144169
  }
144170
+ async deleteSelectedAndReload() {
144171
+ const ids = [...this.selectedIds];
144172
+ for (const id of ids) try {
144173
+ await this.store.delete(id);
144174
+ } catch {}
144175
+ await this.loadMemos();
144176
+ this.mode = "list";
144177
+ this.ui?.requestRender();
144178
+ }
143757
144179
  render(width) {
143758
144180
  const c = this.colors;
143759
144181
  const lines = [];
@@ -143766,7 +144188,7 @@ var MemoryPickerComponent = class extends Container {
143766
144188
  return lines;
143767
144189
  }
143768
144190
  const headerLabel = t("memory.notebook_title");
143769
- const headerHint = this.searchQuery.length > 0 ? t("memory.esc_clear_search") : t("memory.nav_hint");
144191
+ const headerHint = this.searchQuery.length > 0 ? t("memory.esc_clear_search") : this.selectedIds.size > 0 ? t("memory.batch_hint", { count: String(this.selectedIds.size) }) : t("memory.nav_hint");
143770
144192
  const labelWidth = visibleWidth(headerLabel);
143771
144193
  const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$1);
143772
144194
  lines.push(chalk.hex(c.primary).bold(headerLabel) + chalk.hex(c.textMuted)(shownHint));
@@ -143787,10 +144209,15 @@ var MemoryPickerComponent = class extends Container {
143787
144209
  }
143788
144210
  if (this.mode === "detail" && this.detailMemo) return this.renderDetail(lines, width, c);
143789
144211
  if (this.mode === "confirmDelete") {
143790
- const memo = this.memos[this.selectedIndex];
143791
- if (memo) {
143792
- lines.push(truncateToWidth(chalk.hex(c.warning).bold(` ${t("memory.deleting")}${memo.userNeed}`), width, ELLIPSIS$1));
143793
- lines.push(chalk.hex(c.warning)(t("memory.delete_confirm_hint")));
144212
+ if (this.selectedIds.size > 0) {
144213
+ lines.push(truncateToWidth(chalk.hex(c.warning).bold(t("memory.batch_delete_confirm", { count: String(this.selectedIds.size) })), width, ELLIPSIS$1));
144214
+ lines.push(chalk.hex(c.warning)(t("memory.batch_delete_hint")));
144215
+ } else {
144216
+ const memo = this.memos[this.selectedIndex];
144217
+ if (memo) {
144218
+ lines.push(truncateToWidth(chalk.hex(c.warning).bold(` ${t("memory.deleting")}${memo.userNeed}`), width, ELLIPSIS$1));
144219
+ lines.push(chalk.hex(c.warning)(t("memory.delete_confirm_hint")));
144220
+ }
143794
144221
  }
143795
144222
  lines.push(chalk.hex(c.primary)("─".repeat(width)));
143796
144223
  return lines;
@@ -143818,6 +144245,7 @@ var MemoryPickerComponent = class extends Container {
143818
144245
  renderMemoCard(width, memo, isSelected) {
143819
144246
  const c = this.colors;
143820
144247
  const pointer = isSelected ? "❯" : " ";
144248
+ const check = this.selectedIds.has(memo.id) ? "☑" : "□";
143821
144249
  const indent = " ";
143822
144250
  const indentWidth = visibleWidth(indent);
143823
144251
  const titleColor = isSelected ? c.primary : c.text;
@@ -143825,10 +144253,12 @@ var MemoryPickerComponent = class extends Container {
143825
144253
  const trailingParts = [formatRelativeTime$1(memo.recordedAt), sourceLabel(memo.extractionSource)].filter((p) => p.length > 0);
143826
144254
  const trailingText = trailingParts.length > 0 ? " " + trailingParts.join(" ") : "";
143827
144255
  const trailingWidth = visibleWidth(trailingText);
143828
- const headerPrefixWidth = visibleWidth(pointer) + 1;
144256
+ const headerPrefixWidth = visibleWidth(pointer) + 1 + visibleWidth(check) + 1;
143829
144257
  const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
143830
144258
  const shownTitle = truncateToWidth(singleLine$1(memo.userNeed), titleBudget, ELLIPSIS$1);
144259
+ const checkColor = this.selectedIds.has(memo.id) ? c.primary : c.textDim;
143831
144260
  let header = chalk.hex(isSelected ? c.primary : c.textDim)(pointer + " ");
144261
+ header += chalk.hex(checkColor)(check + " ");
143832
144262
  header += titleStyle(shownTitle);
143833
144263
  if (trailingText.length > 0) header += chalk.hex(c.textDim)(trailingText);
143834
144264
  const card = [truncateToWidth(header, width, ELLIPSIS$1)];
@@ -143965,12 +144395,15 @@ var SessionPickerComponent = class extends Container {
143965
144395
  onSelect;
143966
144396
  onCancel;
143967
144397
  onDelete;
144398
+ onDeleteMany;
143968
144399
  onStatus;
143969
144400
  maxVisibleSessions;
143970
144401
  loading;
143971
144402
  focused = false;
143972
144403
  selectedIndex = 0;
143973
144404
  confirmingDelete = false;
144405
+ /** Session ids ticked for batch delete (space toggles, a selects all). */
144406
+ selectedIds = /* @__PURE__ */ new Set();
143974
144407
  constructor(opts) {
143975
144408
  super();
143976
144409
  this.sessions = opts.sessions;
@@ -143980,6 +144413,7 @@ var SessionPickerComponent = class extends Container {
143980
144413
  this.onSelect = opts.onSelect;
143981
144414
  this.onCancel = opts.onCancel;
143982
144415
  this.onDelete = opts.onDelete;
144416
+ this.onDeleteMany = opts.onDeleteMany;
143983
144417
  this.onStatus = opts.onStatus;
143984
144418
  this.maxVisibleSessions = opts.maxVisibleSessions ?? 4;
143985
144419
  }
@@ -143997,6 +144431,15 @@ var SessionPickerComponent = class extends Container {
143997
144431
  if (!session) return;
143998
144432
  if (this.confirmingDelete) {
143999
144433
  this.confirmingDelete = false;
144434
+ if (this.selectedIds.size > 0) {
144435
+ if (this.onDeleteMany === void 0) {
144436
+ this.confirmingDelete = true;
144437
+ this.onStatus(t("session_picker.batch_delete_unavailable"));
144438
+ return;
144439
+ }
144440
+ this.onDeleteMany?.([...this.selectedIds]);
144441
+ return;
144442
+ }
144000
144443
  if (session.metadata?.["source"] === "cc-connect") {
144001
144444
  this.onStatus(t("session_picker.cc_restricted"));
144002
144445
  return;
@@ -144012,11 +144455,13 @@ var SessionPickerComponent = class extends Container {
144012
144455
  return;
144013
144456
  }
144014
144457
  if (matchesKey(data, Key.up)) {
144458
+ if (this.confirmingDelete) return;
144015
144459
  this.selectedIndex = Math.max(0, this.selectedIndex - 1);
144016
144460
  this.confirmingDelete = false;
144017
144461
  return;
144018
144462
  }
144019
144463
  if (matchesKey(data, Key.down)) {
144464
+ if (this.confirmingDelete) return;
144020
144465
  this.selectedIndex = Math.min(this.sessions.length - 1, this.selectedIndex + 1);
144021
144466
  this.confirmingDelete = false;
144022
144467
  return;
@@ -144031,6 +144476,26 @@ var SessionPickerComponent = class extends Container {
144031
144476
  }
144032
144477
  if (session.id !== this.currentSessionId) this.confirmingDelete = true;
144033
144478
  }
144479
+ if (k === " " && !this.confirmingDelete) {
144480
+ const session = this.sessions[this.selectedIndex];
144481
+ if (!session || session.id === this.currentSessionId) return;
144482
+ if (session.metadata?.["source"] === "cc-connect") {
144483
+ this.onStatus(t("session_picker.cc_restricted"));
144484
+ return;
144485
+ }
144486
+ if (this.selectedIds.has(session.id)) this.selectedIds.delete(session.id);
144487
+ else this.selectedIds.add(session.id);
144488
+ return;
144489
+ }
144490
+ if (k === "a" || k === "A") {
144491
+ if (this.confirmingDelete) return;
144492
+ if (this.sessions.length === 0) return;
144493
+ const selectable = this.sessions.filter((s) => s.id !== this.currentSessionId && s.metadata?.["source"] !== "cc-connect");
144494
+ if (selectable.length === 0) return;
144495
+ if (selectable.every((s) => this.selectedIds.has(s.id))) this.selectedIds.clear();
144496
+ else for (const s of selectable) this.selectedIds.add(s.id);
144497
+ return;
144498
+ }
144034
144499
  }
144035
144500
  render(width) {
144036
144501
  const colors = this.colors;
@@ -144048,7 +144513,7 @@ var SessionPickerComponent = class extends Container {
144048
144513
  return lines;
144049
144514
  }
144050
144515
  const headerLabel = t("session.picker_title");
144051
- const headerHint = this.confirmingDelete ? t("session.delete_confirm") : t("session.picker_hint");
144516
+ const headerHint = this.confirmingDelete ? this.selectedIds.size > 0 ? t("session_picker.batch_delete_confirm", { count: String(this.selectedIds.size) }) : t("session.delete_confirm") : this.selectedIds.size > 0 ? t("session_picker.batch_hint", { count: String(this.selectedIds.size) }) : t("session.picker_hint");
144052
144517
  const labelWidth = visibleWidth(headerLabel);
144053
144518
  const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS);
144054
144519
  const hintColor = this.confirmingDelete ? colors.warning : colors.textMuted;
@@ -144074,6 +144539,7 @@ var SessionPickerComponent = class extends Container {
144074
144539
  renderSessionCard(width, session, isSelected, isCurrent) {
144075
144540
  const colors = this.colors;
144076
144541
  const pointer = isSelected ? "❯" : " ";
144542
+ const check = this.selectedIds.has(session.id) ? "☑" : "□";
144077
144543
  const indent = " ";
144078
144544
  const indentWidth = visibleWidth(indent);
144079
144545
  const titleColor = isSelected ? colors.primary : colors.text;
@@ -144087,14 +144553,16 @@ var SessionPickerComponent = class extends Container {
144087
144553
  });
144088
144554
  const trailingParts = [time, badge].filter((p) => p.length > 0);
144089
144555
  const trailingWidth = visibleWidth(trailingParts.length > 0 ? " " + trailingParts.join(" ") : "");
144090
- const headerPrefixWidth = visibleWidth(pointer) + 1;
144556
+ const headerPrefixWidth = visibleWidth(pointer) + 1 + visibleWidth(check) + 1;
144091
144557
  const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
144092
144558
  const shownTitle = truncateToWidth(singleLine(titleSource), titleBudget, ELLIPSIS);
144559
+ const checkColor = this.selectedIds.has(session.id) ? colors.primary : colors.textDim;
144093
144560
  let header = chalk.hex(isSelected ? colors.primary : colors.textDim)(pointer + " ");
144561
+ header += chalk.hex(checkColor)(check + " ");
144094
144562
  header += titleStyle(shownTitle);
144095
144563
  if (time.length > 0) header += " " + chalk.hex(colors.textDim)(time);
144096
144564
  if (badge.length > 0) header += " " + chalk.hex(colors.success)(badge);
144097
- const card = [header];
144565
+ const card = [truncateToWidth(header, width)];
144098
144566
  const fullId = session.id;
144099
144567
  const idWidth = visibleWidth(fullId);
144100
144568
  const metaGap = " ";
@@ -144839,6 +145307,27 @@ var DialogManager = class {
144839
145307
  }).catch((error) => {
144840
145308
  this.host.showError(error instanceof Error ? error.message : String(error));
144841
145309
  });
145310
+ },
145311
+ onDeleteMany: (sessionIds) => {
145312
+ const ccIds = new Set(this.host.getSessions().filter((s) => s.metadata?.["source"] === "cc-connect").map((s) => s.id));
145313
+ const deletable = sessionIds.filter((id) => !ccIds.has(id));
145314
+ if (deletable.length === 0) {
145315
+ this.host.showStatus(t("dialog.cc_managed"));
145316
+ return;
145317
+ }
145318
+ (async () => {
145319
+ for (const id of deletable) try {
145320
+ await this.host.deleteSession(id);
145321
+ } catch (error) {
145322
+ this.host.showError(error instanceof Error ? error.message : String(error));
145323
+ break;
145324
+ }
145325
+ await this.host.fetchSessions();
145326
+ if (this.host.getSessions().length === 0) this.hideSessionPicker();
145327
+ else if (this.host.state.activeDialog === "session-picker") this.mountSessionPicker(onCancel);
145328
+ })().catch((error) => {
145329
+ this.host.showError(error instanceof Error ? error.message : String(error));
145330
+ });
144842
145331
  }
144843
145332
  }));
144844
145333
  }
@@ -144973,6 +145462,7 @@ function createInitialAppState(input) {
144973
145462
  contextUsage: 0,
144974
145463
  contextTokens: 0,
144975
145464
  maxContextTokens: 0,
145465
+ providerBalance: null,
144976
145466
  isCompacting: false,
144977
145467
  lastCompactionFinishedAt: void 0,
144978
145468
  autoCompactionCount: 0,
@@ -145768,14 +146258,20 @@ function supportsAnsi() {
145768
146258
  ansiSupported = false;
145769
146259
  return false;
145770
146260
  }
145771
- function runLoadingAnimation(theme = "dark") {
146261
+ async function runLoadingAnimation(theme = "dark") {
145772
146262
  if (!supportsAnsi()) {
145773
146263
  const { cols } = getTerminalSize();
145774
146264
  if (cols >= FULL_LOGO_MIN_COLS) for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
145775
146265
  else for (const line of COMPACT_LOGO) stdout.write(`${fg(...BLOCK_RGB)}${line}${RESET}\n`);
145776
146266
  stdout.write(`${BOLD}${fg(...THEME_PRIMARY[theme])}${t("loading.waking")}${RESET}\n`);
145777
- return Promise.resolve();
146267
+ return;
145778
146268
  }
146269
+ let autoStart = false;
146270
+ try {
146271
+ autoStart = (await loadTuiConfig()).autoStart;
146272
+ } catch {}
146273
+ const autoStartAtBoot = autoStart;
146274
+ let toggled = false;
145779
146275
  return new Promise((resolve) => {
145780
146276
  if (process$1.platform !== "win32") stdout.write("\x1B[?1049h");
145781
146277
  stdout.write("\x1B[2J");
@@ -145809,7 +146305,9 @@ function runLoadingAnimation(theme = "dark") {
145809
146305
  else lines.push(centerPad(`${BOLD}${fg(...breatheColor)}${t("loading.press_enter")}${RESET}`, cols));
145810
146306
  lines.push("");
145811
146307
  lines.push("");
145812
- lines.push(centerPad(`${fg(...DIM_RGB)}${t("loading.quit_hint")}${RESET}`, cols));
146308
+ const rightText = toggled ? autoStart ? t("loading.auto_start_on") : t("loading.auto_start_off") : t("loading.auto_start_hint");
146309
+ const hint = `${t("loading.quit_hint")} ${rightText}`;
146310
+ lines.push(centerPad(`${fg(...DIM_RGB)}${hint}${RESET}`, cols));
145813
146311
  while (lines.length < rows) lines.push("");
145814
146312
  stdout.write("\x1B[H");
145815
146313
  stdout.write(lines.join("\n"));
@@ -145830,6 +146328,20 @@ function runLoadingAnimation(theme = "dark") {
145830
146328
  interrupt();
145831
146329
  return;
145832
146330
  }
146331
+ if (key === "") {
146332
+ toggled = true;
146333
+ autoStart = !autoStart;
146334
+ (async () => {
146335
+ try {
146336
+ await saveTuiConfig({
146337
+ ...await loadTuiConfig(),
146338
+ autoStart
146339
+ });
146340
+ } catch {}
146341
+ })();
146342
+ render();
146343
+ return;
146344
+ }
145833
146345
  if ((key === "\r" || key === "\n") && phase === "ready") {
145834
146346
  cleanup();
145835
146347
  resolve();
@@ -145875,6 +146387,10 @@ function runLoadingAnimation(theme = "dark") {
145875
146387
  }).then(() => {
145876
146388
  phase = "ready";
145877
146389
  render();
146390
+ if (autoStartAtBoot) setTimeout(() => {
146391
+ cleanup();
146392
+ resolve();
146393
+ }, 350);
145878
146394
  });
145879
146395
  });
145880
146396
  }
package/dist/main.mjs CHANGED
@@ -6,7 +6,7 @@ const __dirname = __cjsShimDirname(__filename);
6
6
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
7
7
  //#region src/main.ts
8
8
  try {
9
- (await import("./app-D7u8KJ3h.mjs")).main();
9
+ (await import("./app-zSNn5qdo.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
@@ -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 { t as TextInputDialogComponent } from "./text-input-dialog-K1FbYyug.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-DYHGTG6m.mjs";
7
7
  export { TextInputDialogComponent };
@@ -221,6 +221,9 @@ const dictionaries = {
221
221
  "loading.waking": "正在唤醒核心...",
222
222
  "loading.press_enter": "按下 ENTER 唤醒核心",
223
223
  "loading.quit_hint": "按住 Ctrl+C 即可退出 Scream Code",
224
+ "loading.auto_start_hint": "Ctrl+E 即可切换一键启动",
225
+ "loading.auto_start_on": "Ctrl+E 即可切换一键启动(默认启动开)",
226
+ "loading.auto_start_off": "Ctrl+E 即可切换一键启动(默认启动关)",
224
227
  "language.picker_title": "语言 / Language",
225
228
  "language.picker_hint": "↑↓ 选择 · Enter 确认 · Esc 取消",
226
229
  "language.unchanged": "语言未更改:\"{locale}\"。",
@@ -428,6 +431,9 @@ const dictionaries = {
428
431
  "like.other": "其他偏好",
429
432
  "like.other_hint": "例如:多说例子、先给结论再展开、避免术语等(留空表示不设置)",
430
433
  "like.other_example": "例如:请用中文回答,避免缩写",
434
+ "like.do_not": "用户禁止",
435
+ "like.do_not_hint": "用户明确不让做的事。Agent 绝不能做这些。",
436
+ "like.do_not_example": "例如:未经询问绝不修改用户的配置文件",
431
437
  "like.cancelled": "已取消 /like 设置",
432
438
  "like.saved": "偏好已保存(下次新会话生效)",
433
439
  "revoke.streaming": "无法在 streaming 中撤回 — 请先按 Esc 或 Ctrl-C 取消。",
@@ -467,9 +473,12 @@ const dictionaries = {
467
473
  "session.loading": "正在加载会话...",
468
474
  "session_picker.empty": "未找到会话。按 Escape 关闭。",
469
475
  "session_picker.cc_restricted": "CC专属会话不支持切换或删除,请点击或复制下方文件路径进入手动管理",
476
+ "session_picker.batch_delete_confirm": "删除选中的 {count} 个会话?",
477
+ "session_picker.batch_delete_unavailable": "此处不支持批量删除。",
478
+ "session_picker.batch_hint": "[空格] 选择 · [a] 全选 · [d] 删除选中({count})",
470
479
  "session.picker_title": "会话 ",
471
480
  "session.delete_confirm": "⚠️ 按 Enter 确认删除,Esc 取消",
472
- "session.picker_hint": "(↑↓ 导航,Enter 选择,d 删除,Esc 取消)",
481
+ "session.picker_hint": "(↑↓ 导航,Enter 打开,空格 标记,a 全选,d 删除,Esc 取消)",
473
482
  "model.nav_model": "↑↓ 模型",
474
483
  "model.nav_thinking": "←→ 思考等级",
475
484
  "model.nav_page": "PgUp/PgDn 翻页",
@@ -532,13 +541,16 @@ const dictionaries = {
532
541
  "memory.search_label": "搜索: ",
533
542
  "memory.notebook_title": "记忆备忘录 ",
534
543
  "memory.esc_clear_search": "(Esc 清除搜索)",
535
- "memory.nav_hint": "(↑↓ 导航,Enter 查看,i 注入,d 删除,/ 搜索,Esc 关闭)",
544
+ "memory.nav_hint": "(↑↓ 导航,Enter 查看,空格 标记,a 全选,i 注入,d 删除,/ 搜索,Esc 关闭)",
536
545
  "memory.loading": "正在加载...",
537
546
  "memory.no_match": "未找到匹配 \"{query}\" 的记忆。",
538
547
  "memory.empty": "暂无记忆备忘录。",
539
548
  "memory.auto_extract_hint": " 压缩对话或退出会话时,系统会自动提取并保存。",
540
549
  "memory.deleting": "删除: ",
541
550
  "memory.delete_confirm_hint": " 按 Enter 确认删除,Esc 取消",
551
+ "memory.batch_delete_confirm": "删除选中的 {count} 条记忆?",
552
+ "memory.batch_delete_hint": " 按 Enter 确认批量删除,Esc 取消",
553
+ "memory.batch_hint": "已选 {count} 条 · [空格] 选择 · [a] 全选 · [d] 删除选中",
542
554
  "memory.showing_range": "{start}-{end} / {total} 条",
543
555
  "memory.id_label": "ID: ",
544
556
  "memory.source_label": "来源: ",
@@ -593,6 +605,7 @@ const dictionaries = {
593
605
  "toolcall.current_plan": "当前计划",
594
606
  "toolcall.approved": "已批准:{chosen}",
595
607
  "toolcall.approved_label": "已批准",
608
+ "toolcall.auto_approved": "自动批准",
596
609
  "toolcall.input_unavailable": "无法收集你的输入",
597
610
  "toolcall.input_collected": "已收集你的答案",
598
611
  "toolcall.waiting_input": "等待你的输入",
@@ -720,6 +733,8 @@ const dictionaries = {
720
733
  "subagent.bind_title": "绑定 {profile}",
721
734
  "subagent.model_hint": "↑↓ 选择模型 · Enter 确认 · Esc 返回",
722
735
  "subagent.save_failed": "保存失败:{msg}",
736
+ "subagent.stale_binding": "{alias}(已失效)",
737
+ "subagent.invalid_alias": "模型别名 {alias} 不存在",
723
738
  "kdoctree.no_title": "(无标题)",
724
739
  "kdoctree.empty": "(空)",
725
740
  "kdoctree.move": "移动",
@@ -803,6 +818,8 @@ const dictionaries = {
803
818
  "export.session_title": "# Scream 会话导出",
804
819
  "bgtask.agent_task": "代理任务",
805
820
  "bgtask.bash_task": "bash 任务",
821
+ "bash.background_completed": "后台任务 {id} 已完成:{command}",
822
+ "bash.background_failed": "后台任务 {id} 执行失败(退出码 {exitCode}):{command}",
806
823
  "bgtask.started_bg": "{subject} 已在后台启动",
807
824
  "bgtask.awaiting_approval": "{subject} 等待审批",
808
825
  "bgtask.completed_bg": "{subject} 已在后台完成",
@@ -1252,6 +1269,9 @@ const dictionaries = {
1252
1269
  "loading.waking": "Waking core...",
1253
1270
  "loading.press_enter": "Press ENTER to wake core",
1254
1271
  "loading.quit_hint": "Hold Ctrl+C to quit Scream Code",
1272
+ "loading.auto_start_hint": "Ctrl+E to toggle auto-start",
1273
+ "loading.auto_start_on": "Ctrl+E to toggle auto-start (ON)",
1274
+ "loading.auto_start_off": "Ctrl+E to toggle auto-start (OFF)",
1255
1275
  "language.picker_title": "Language / 语言",
1256
1276
  "language.picker_hint": "↑↓ Select · Enter confirm · Esc cancel",
1257
1277
  "language.unchanged": "Language unchanged: \"{locale}\".",
@@ -1459,6 +1479,9 @@ const dictionaries = {
1459
1479
  "like.other": "Other preferences",
1460
1480
  "like.other_hint": "e.g. more examples, conclusion first, avoid jargon (leave empty to skip)",
1461
1481
  "like.other_example": "e.g. Answer in English, avoid abbreviations",
1482
+ "like.do_not": "User Prohibitions",
1483
+ "like.do_not_hint": "Things the user explicitly does NOT want done. The agent must NEVER do these.",
1484
+ "like.do_not_example": "e.g. Never modify user config files without asking",
1462
1485
  "like.cancelled": "/like setup cancelled",
1463
1486
  "like.saved": "Preferences saved (takes effect in next session)",
1464
1487
  "revoke.streaming": "Cannot revoke during streaming — press Esc or Ctrl-C to cancel first.",
@@ -1498,9 +1521,12 @@ const dictionaries = {
1498
1521
  "session.loading": "Loading sessions...",
1499
1522
  "session_picker.empty": "No sessions found. Press Escape to close.",
1500
1523
  "session_picker.cc_restricted": "CC sessions cannot be switched or deleted. Use the file path below to manage manually",
1524
+ "session_picker.batch_delete_confirm": "Delete {count} selected sessions?",
1525
+ "session_picker.batch_delete_unavailable": "Batch delete is not available here.",
1526
+ "session_picker.batch_hint": "[space] select · [a] all · [d] delete selected ({count})",
1501
1527
  "session.picker_title": "Sessions ",
1502
1528
  "session.delete_confirm": "⚠️ Press Enter to confirm delete, Esc to cancel",
1503
- "session.picker_hint": "(↑↓ Navigate, Enter Select, d Delete, Esc Cancel)",
1529
+ "session.picker_hint": "(↑↓ Navigate, Enter Open, Space Mark, a All, d Delete, Esc Cancel)",
1504
1530
  "model.nav_model": "↑↓ Model",
1505
1531
  "model.nav_thinking": "←→ Thinking",
1506
1532
  "model.nav_page": "PgUp/PgDn Page",
@@ -1563,13 +1589,16 @@ const dictionaries = {
1563
1589
  "memory.search_label": "Search: ",
1564
1590
  "memory.notebook_title": "Memory Notebook ",
1565
1591
  "memory.esc_clear_search": "(Esc clear search)",
1566
- "memory.nav_hint": "(↑↓ Navigate, Enter View, i Inject, d Delete, / Search, Esc Close)",
1592
+ "memory.nav_hint": "(↑↓ Navigate, Enter View, Space Mark, a All, i Inject, d Delete, / Search, Esc Close)",
1567
1593
  "memory.loading": "Loading...",
1568
1594
  "memory.no_match": "No memories matching \"{query}\".",
1569
1595
  "memory.empty": "No memories yet.",
1570
1596
  "memory.auto_extract_hint": " Memories are extracted automatically when compacting or exiting a session.",
1571
1597
  "memory.deleting": "Delete: ",
1572
1598
  "memory.delete_confirm_hint": " Press Enter to confirm delete, Esc to cancel",
1599
+ "memory.batch_delete_confirm": "Delete {count} selected memos?",
1600
+ "memory.batch_delete_hint": " Press Enter to confirm batch delete, Esc to cancel",
1601
+ "memory.batch_hint": "{count} selected · [space] select · [a] all · [d] delete selected",
1573
1602
  "memory.showing_range": "{start}-{end} / {total} items",
1574
1603
  "memory.id_label": "ID: ",
1575
1604
  "memory.source_label": "Source: ",
@@ -1624,6 +1653,7 @@ const dictionaries = {
1624
1653
  "toolcall.current_plan": "Current Plan",
1625
1654
  "toolcall.approved": "Approved: {chosen}",
1626
1655
  "toolcall.approved_label": "Approved",
1656
+ "toolcall.auto_approved": "Auto-approved",
1627
1657
  "toolcall.input_unavailable": "Could not collect your input",
1628
1658
  "toolcall.input_collected": "Collected your answer",
1629
1659
  "toolcall.waiting_input": "Waiting for your input",
@@ -1751,6 +1781,8 @@ const dictionaries = {
1751
1781
  "subagent.bind_title": "Bind {profile}",
1752
1782
  "subagent.model_hint": "↑↓ Select model · Enter confirm · Esc back",
1753
1783
  "subagent.save_failed": "Save failed: {msg}",
1784
+ "subagent.stale_binding": "{alias} (stale)",
1785
+ "subagent.invalid_alias": "Model alias {alias} does not exist",
1754
1786
  "kdoctree.no_title": "(untitled)",
1755
1787
  "kdoctree.empty": "(empty)",
1756
1788
  "kdoctree.move": "Move",
@@ -1834,6 +1866,8 @@ const dictionaries = {
1834
1866
  "export.session_title": "# Scream Session Export",
1835
1867
  "bgtask.agent_task": "Agent task",
1836
1868
  "bgtask.bash_task": "Bash task",
1869
+ "bash.background_completed": "Background task {id} completed: {command}",
1870
+ "bash.background_failed": "Background task {id} failed (exit {exitCode}): {command}",
1837
1871
  "bgtask.started_bg": "{subject} started in background",
1838
1872
  "bgtask.awaiting_approval": "{subject} awaiting approval",
1839
1873
  "bgtask.completed_bg": "{subject} completed in background",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.11.6",
3
+ "version": "0.11.8",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -59,7 +59,7 @@
59
59
  "smoke": "node dist/main.mjs --version"
60
60
  },
61
61
  "dependencies": {
62
- "@liutod-scream/pi-tui": "^0.80.31",
62
+ "@liutod-scream/pi-tui": "^0.80.32",
63
63
  "@mariozechner/clipboard": "^0.3.2",
64
64
  "chalk": "^5.4.1",
65
65
  "cli-highlight": "^2.1.11",