scream-code 0.11.7 → 0.11.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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-BdUmo73M.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-COl6Uu7m.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";
@@ -59888,7 +59888,6 @@ function formatFullSkill(skill) {
59888
59888
  function formatModelSkill(skill) {
59889
59889
  const lines = [`- ${skill.name}: ${truncate$2(skill.description, LISTING_DESC_MAX)}`];
59890
59890
  if (typeof skill.metadata.whenToUse === "string" && skill.metadata.whenToUse.length > 0) lines.push(` When to use: ${skill.metadata.whenToUse}`);
59891
- lines.push(` Path: ${skill.path}`);
59892
59891
  return lines;
59893
59892
  }
59894
59893
  function truncate$2(value, max) {
@@ -119233,6 +119232,85 @@ function parsePositiveInt(value) {
119233
119232
  return n;
119234
119233
  }
119235
119234
  //#endregion
119235
+ //#region ../../packages/agent-core/src/session/provider-balance.ts
119236
+ const fetchers = [{
119237
+ matches: (baseUrl) => {
119238
+ try {
119239
+ return new URL(baseUrl).hostname === "api.deepseek.com";
119240
+ } catch {
119241
+ return false;
119242
+ }
119243
+ },
119244
+ fetch: async (baseUrl, apiKey) => {
119245
+ try {
119246
+ const root = new URL(baseUrl).origin;
119247
+ const res = await fetch(`${root}/user/balance`, {
119248
+ headers: {
119249
+ Accept: "application/json",
119250
+ Authorization: `Bearer ${apiKey}`
119251
+ },
119252
+ signal: AbortSignal.timeout(5e3)
119253
+ });
119254
+ if (!res.ok) return null;
119255
+ const info = (await res.json()).balance_infos?.[0];
119256
+ if (info?.total_balance === void 0) return null;
119257
+ return {
119258
+ currency: info.currency ?? "",
119259
+ totalBalance: info.total_balance
119260
+ };
119261
+ } catch {
119262
+ return null;
119263
+ }
119264
+ }
119265
+ }, {
119266
+ matches: (baseUrl) => {
119267
+ try {
119268
+ return new URL(baseUrl).hostname === "api.moonshot.cn";
119269
+ } catch {
119270
+ return false;
119271
+ }
119272
+ },
119273
+ fetch: async (baseUrl, apiKey) => {
119274
+ try {
119275
+ const root = new URL(baseUrl).origin;
119276
+ const res = await fetch(`${root}/v1/users/me/balance`, {
119277
+ headers: {
119278
+ Accept: "application/json",
119279
+ Authorization: `Bearer ${apiKey}`
119280
+ },
119281
+ signal: AbortSignal.timeout(5e3)
119282
+ });
119283
+ if (!res.ok) return null;
119284
+ const data = await res.json();
119285
+ if (typeof data.data?.available_balance !== "number") return null;
119286
+ return {
119287
+ currency: "CNY",
119288
+ totalBalance: data.data.available_balance.toFixed(2)
119289
+ };
119290
+ } catch {
119291
+ return null;
119292
+ }
119293
+ }
119294
+ }];
119295
+ /**
119296
+ * Look up the balance for the given provider endpoint.
119297
+ * Returns null when the endpoint is not a recognized official vendor or
119298
+ * when the lookup fails — callers render nothing in that case.
119299
+ */
119300
+ async function fetchProviderBalance(baseUrl, apiKey) {
119301
+ const fetcher = fetchers.find((f) => f.matches(baseUrl));
119302
+ if (!fetcher) return null;
119303
+ return fetcher.fetch(baseUrl, apiKey);
119304
+ }
119305
+ /**
119306
+ * True when the given base URL belongs to a vendor we can query balances
119307
+ * for. Pure local check (no network) — used to skip polling for
119308
+ * unsupported providers.
119309
+ */
119310
+ function isSupportedBalanceProvider(baseUrl) {
119311
+ return fetchers.some((f) => f.matches(baseUrl));
119312
+ }
119313
+ //#endregion
119236
119314
  //#region ../../packages/node-sdk/src/auth.ts
119237
119315
  var ScreamAuthFacade = class {
119238
119316
  options;
@@ -120653,7 +120731,7 @@ function optionalBuildString(value) {
120653
120731
  return typeof value === "string" && value.length > 0 ? value : void 0;
120654
120732
  }
120655
120733
  const SCREAM_BUILD_INFO = {
120656
- version: optionalBuildString("0.11.7"),
120734
+ version: optionalBuildString("0.11.9"),
120657
120735
  channel: optionalBuildString(""),
120658
120736
  commit: optionalBuildString(""),
120659
120737
  buildTarget: optionalBuildString("darwin-arm64")
@@ -120918,7 +120996,9 @@ const NotificationsConfigSchema = z.object({
120918
120996
  const TuiLikePreferencesSchema = z.object({
120919
120997
  nickname: z.string().optional(),
120920
120998
  tone: z.string().optional(),
120921
- other: z.string().optional()
120999
+ other: z.string().optional(),
121000
+ /** Explicit prohibitions: things the user does NOT want done. */
121001
+ doNot: z.string().optional()
120922
121002
  });
120923
121003
  const TuiConfigFileSchema = z.object({
120924
121004
  theme: TuiThemeSchema.optional(),
@@ -121982,14 +122062,16 @@ function buildSkillSlashCommands(skills, builtinCommandNames) {
121982
122062
  commands.push({
121983
122063
  name: commandName,
121984
122064
  aliases: [],
121985
- description: skill.description ?? ""
122065
+ description: skill.description ?? "",
122066
+ source: skill.source
121986
122067
  });
121987
122068
  if (skill.source === "builtin" && !reservedNames.has(skill.name)) {
121988
122069
  commandMap.set(skill.name, skill.name);
121989
122070
  commands.push({
121990
122071
  name: skill.name,
121991
122072
  aliases: [],
121992
- description: skill.description ?? ""
122073
+ description: skill.description ?? "",
122074
+ source: skill.source
121993
122075
  });
121994
122076
  }
121995
122077
  }
@@ -123307,7 +123389,7 @@ function mountProfileList(host) {
123307
123389
  const { subagentModels: bindings, availableModels } = host.state.appState;
123308
123390
  const options = getSubagentProfiles().map((profile) => {
123309
123391
  const alias = bindings[profile.name];
123310
- const bindingLabel = alias === void 0 ? t("subagent.follow_main") : modelDisplayName$1(alias, availableModels[alias]);
123392
+ const bindingLabel = alias === void 0 ? t("subagent.follow_main") : availableModels[alias] !== void 0 ? modelDisplayName$1(alias, availableModels[alias]) : t("subagent.stale_binding", { alias });
123311
123393
  return {
123312
123394
  value: profile.name,
123313
123395
  label: `${profile.name} → ${bindingLabel}`,
@@ -123361,6 +123443,10 @@ async function applyBinding(host, profileName, value) {
123361
123443
  const configPath = getTuiConfigPath();
123362
123444
  try {
123363
123445
  const current = await loadTuiConfig(configPath);
123446
+ if (value !== FOLLOW_MAIN && host.state.appState.availableModels[value] === void 0) {
123447
+ host.showError(t("subagent.invalid_alias", { alias: value }));
123448
+ return;
123449
+ }
123364
123450
  const updated = { ...current.subagentModels };
123365
123451
  if (value === FOLLOW_MAIN) delete updated[profileName];
123366
123452
  else updated[profileName] = value;
@@ -123873,6 +123959,11 @@ function safeUsage(usage) {
123873
123959
  const CONTEXT_BAR_WIDTH = 10;
123874
123960
  const CONTEXT_BAR_FILLED = "▰";
123875
123961
  const CONTEXT_BAR_EMPTY = "▱";
123962
+ function currencySymbol(currency) {
123963
+ if (currency === "CNY") return "¥";
123964
+ if (currency === "USD") return "$";
123965
+ return `${currency} `;
123966
+ }
123876
123967
  /**
123877
123968
  * Half-block progress bar for context usage: `▰▰▰▱▱▱▱▱▱▱` (10 cells).
123878
123969
  * Filled cells are rounded from the clamped ratio, so 0% is all-empty and
@@ -124094,8 +124185,12 @@ var FooterComponent = class {
124094
124185
  left.push(chalk.hex(colors.primary).bold(goalLabel));
124095
124186
  }
124096
124187
  const model = shortenModel(modelDisplayName(state));
124097
- if (model) if (state.streamingPhase === "thinking") left.push(shimmerText(model, colors));
124098
- else left.push(chalk.hex(colors.textDim)(model));
124188
+ if (model) {
124189
+ if (state.streamingPhase === "thinking") left.push(shimmerText(model, colors));
124190
+ else left.push(chalk.hex(colors.textDim)(model));
124191
+ const balance = state.providerBalance;
124192
+ if (balance !== null && balance !== void 0) left.push(chalk.hex(colors.textDim)(`${currencySymbol(balance.currency)}${balance.totalBalance}`));
124193
+ }
124099
124194
  if (this.backgroundBashTaskCount > 0) left.push(chalk.hex(colors.primary)(`[${t("footer.tasks_running", { count: String(this.backgroundBashTaskCount) })}]`));
124100
124195
  if (this.backgroundAgentCount > 0) left.push(chalk.hex(colors.primary)(`[${t("footer.agents_running", { count: String(this.backgroundAgentCount) })}]`));
124101
124196
  const git = this.gitCache.getStatus();
@@ -124542,6 +124637,7 @@ function createMarkdownTheme(colors) {
124542
124637
  quote: (text) => chalk.hex(colors.mdQuote)(text),
124543
124638
  quoteBorder: (text) => chalk.hex(colors.mdQuote)(text),
124544
124639
  hr: (text) => border(text),
124640
+ tableHeader: (text) => chalk.bold.hex(colors.accent)(text),
124545
124641
  listBullet: (text) => chalk.hex(colors.roleAssistant)(text.replace(/^-/, "•")),
124546
124642
  bold: (text) => chalk.bold(text),
124547
124643
  italic: (text) => chalk.italic(text),
@@ -124933,6 +125029,99 @@ async function loadManagedUsageReport(host) {
124933
125029
  } };
124934
125030
  }
124935
125031
  //#endregion
125032
+ //#region src/tui/api-balance.ts
125033
+ /**
125034
+ * TUI-side provider balance lookup.
125035
+ *
125036
+ * Reads the runtime config.toml (same file the engine consumes), resolves
125037
+ * the plaintext API key (direct value or the provider's ENV_VAR env
125038
+ * reference), and delegates the actual query to the engine's
125039
+ * fetchProviderBalance. Results are cached briefly so footer renders do
125040
+ * not hammer the vendor endpoint.
125041
+ */
125042
+ const BALANCE_CACHE_MS = 6e4;
125043
+ const cache = /* @__PURE__ */ new Map();
125044
+ /** Monotonic id so an out-of-order lookup can never overwrite a newer one. */
125045
+ let latestRequestId = 0;
125046
+ function resolveApiKey(providerName) {
125047
+ const provider = readConfigFile(join(getDataDir(), "config.toml")).providers?.[providerName];
125048
+ if (!provider) return void 0;
125049
+ const direct = provider.apiKey?.trim();
125050
+ if (direct !== void 0 && direct.length > 0) return direct;
125051
+ const envKey = envKeyForProviderType(provider.type);
125052
+ const stored = envKey !== void 0 ? provider.env?.[envKey]?.trim() : void 0;
125053
+ return stored !== void 0 && stored.length > 0 ? stored : void 0;
125054
+ }
125055
+ function envKeyForProviderType(type) {
125056
+ switch (type) {
125057
+ case "anthropic": return "ANTHROPIC_API_KEY";
125058
+ case "openai":
125059
+ case "openai_responses": return "OPENAI_API_KEY";
125060
+ case "scream": return "SCREAM_API_KEY";
125061
+ case "google-genai": return "GOOGLE_API_KEY";
125062
+ case "vertexai": return "VERTEXAI_API_KEY";
125063
+ default: return;
125064
+ }
125065
+ }
125066
+ async function loadBalance(providerName) {
125067
+ try {
125068
+ const provider = readConfigFile(join(getDataDir(), "config.toml")).providers?.[providerName];
125069
+ if (!provider?.baseUrl) return null;
125070
+ const apiKey = resolveApiKey(providerName);
125071
+ if (apiKey === void 0) return null;
125072
+ return await fetchProviderBalance(provider.baseUrl, apiKey);
125073
+ } catch {
125074
+ return null;
125075
+ }
125076
+ }
125077
+ /**
125078
+ * Look up the balance for the provider that serves the given model
125079
+ * (model names follow the "provider/model" convention). Returns null when
125080
+ * the provider is unknown, unofficial, or the lookup failed — callers
125081
+ * render nothing in that case. Cached for BALANCE_CACHE_MS per provider.
125082
+ */
125083
+ async function getProviderBalanceForModel(model) {
125084
+ const providerName = model.split("/")[0] ?? "";
125085
+ if (providerName.length === 0) return null;
125086
+ const cached = cache.get(providerName);
125087
+ if (cached !== void 0 && Date.now() - cached.at < BALANCE_CACHE_MS) return cached.balance;
125088
+ const balance = await loadBalance(providerName);
125089
+ cache.set(providerName, {
125090
+ balance,
125091
+ at: Date.now()
125092
+ });
125093
+ return balance;
125094
+ }
125095
+ /**
125096
+ * True when the given model is served by a provider whose balance we can
125097
+ * query. Pure local check (config lookup + hostname match, no network) —
125098
+ * the poller uses it to skip unsupported providers entirely.
125099
+ */
125100
+ function supportsBalance(model) {
125101
+ const providerName = model.split("/")[0] ?? "";
125102
+ if (providerName.length === 0) return false;
125103
+ try {
125104
+ const baseUrl = readConfigFile(join(getDataDir(), "config.toml")).providers?.[providerName]?.baseUrl;
125105
+ return baseUrl !== void 0 && isSupportedBalanceProvider(baseUrl);
125106
+ } catch {
125107
+ return false;
125108
+ }
125109
+ }
125110
+ /**
125111
+ * Fetch the balance for a model and push it into app state. Fire-and-forget:
125112
+ * failures resolve to null inside getProviderBalanceForModel, so callers
125113
+ * (startup sync and model switches) never await it. A request id guards
125114
+ * against out-of-order resolutions: when the user switches models quickly,
125115
+ * only the latest lookup's result is committed.
125116
+ */
125117
+ function refreshProviderBalance(model, setAppState) {
125118
+ const requestId = ++latestRequestId;
125119
+ getProviderBalanceForModel(model).then((balance) => {
125120
+ if (requestId !== latestRequestId) return;
125121
+ setAppState({ providerBalance: balance });
125122
+ });
125123
+ }
125124
+ //#endregion
124936
125125
  //#region src/tui/commands/config.ts
124937
125126
  /**
124938
125127
  * Storm Breaker guard for model switches. Returns the (currentTokens,
@@ -125359,8 +125548,10 @@ async function performModelSwitch(host, alias, thinkingLevel) {
125359
125548
  }
125360
125549
  host.setAppState({
125361
125550
  model: effectiveAlias,
125362
- thinkingLevel: effectiveThinking
125551
+ thinkingLevel: effectiveThinking,
125552
+ providerBalance: null
125363
125553
  });
125554
+ refreshProviderBalance(effectiveAlias, (patch) => host.setAppState(patch));
125364
125555
  let persisted = false;
125365
125556
  try {
125366
125557
  persisted = await persistModelSelection(host, alias, thinkingLevel);
@@ -126195,7 +126386,7 @@ async function guidedGoalSetup(host) {
126195
126386
  host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
126196
126387
  return;
126197
126388
  }
126198
- const { TextInputDialogComponent } = await import("./text-input-dialog-Ct-Yn67a.mjs");
126389
+ const { TextInputDialogComponent } = await import("./text-input-dialog-xqTCBHZF.mjs");
126199
126390
  const initialDesc = await promptText(host, TextInputDialogComponent, {
126200
126391
  title: t("goal.setup_title_initial"),
126201
126392
  subtitle: t("goal.setup_desc_hint"),
@@ -126216,7 +126407,7 @@ async function guidedGoalSetup(host) {
126216
126407
  await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
126217
126408
  }
126218
126409
  async function showGoalConfigWizard(host, session, objective, replace) {
126219
- const { TextInputDialogComponent } = await import("./text-input-dialog-Ct-Yn67a.mjs");
126410
+ const { TextInputDialogComponent } = await import("./text-input-dialog-xqTCBHZF.mjs");
126220
126411
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
126221
126412
  title: t("goal.wizard_title", { objective }),
126222
126413
  subtitle: t("goal.budget_turns_hint"),
@@ -132617,8 +132808,11 @@ function buildRoleAdditionalText(prefs) {
132617
132808
  if (prefs.nickname !== void 0 && prefs.nickname.trim().length > 0) items.push(`- Nickname: address the user as "${prefs.nickname.trim()}".`);
132618
132809
  if (prefs.tone !== void 0 && prefs.tone.trim().length > 0) items.push(`- Tone: respond in ${prefs.tone.trim()} tone.`);
132619
132810
  if (prefs.other !== void 0 && prefs.other.trim().length > 0) items.push(`- Other: ${prefs.other.trim()}`);
132620
- if (items.length === 0) return "";
132621
- lines.push("", ...items, "", t("like.priority"));
132811
+ const doNot = prefs.doNot?.trim();
132812
+ if (items.length === 0 && (doNot === void 0 || doNot.length === 0)) return "";
132813
+ lines.push("", ...items);
132814
+ if (doNot !== void 0 && doNot.length > 0) lines.push("", "## Do NOT (explicit prohibitions — NEVER do these)", doNot);
132815
+ lines.push("", t("like.priority"));
132622
132816
  return lines.join("\n");
132623
132817
  }
132624
132818
  async function getUserPrefsPath() {
@@ -132626,12 +132820,23 @@ async function getUserPrefsPath() {
132626
132820
  }
132627
132821
  async function persistLikePreferences(host, prefs) {
132628
132822
  const configPath = getTuiConfigPath();
132629
- await saveTuiConfig({
132630
- ...await loadTuiConfig(configPath),
132823
+ const current = await loadTuiConfig(configPath);
132824
+ const updated = {
132825
+ ...current,
132631
132826
  like: prefs
132632
- }, configPath);
132633
- const roleAdditional = buildRoleAdditionalText(prefs);
132634
- await writeFile(await getUserPrefsPath(), roleAdditional, "utf-8");
132827
+ };
132828
+ try {
132829
+ await saveTuiConfig(updated, configPath);
132830
+ await writeFile(await getUserPrefsPath(), buildRoleAdditionalText(prefs), "utf-8");
132831
+ } catch (error) {
132832
+ try {
132833
+ await saveTuiConfig(current, configPath);
132834
+ } catch {}
132835
+ try {
132836
+ await writeFile(await getUserPrefsPath(), buildRoleAdditionalText(current.like ?? {}), "utf-8");
132837
+ } catch {}
132838
+ throw error;
132839
+ }
132635
132840
  host.setAppState({ like: prefs });
132636
132841
  }
132637
132842
  async function handleLikeCommand(host) {
@@ -132666,10 +132871,21 @@ async function handleLikeCommand(host) {
132666
132871
  host.showStatus(t("like.cancelled"), host.state.theme.colors.textDim);
132667
132872
  return;
132668
132873
  }
132874
+ const doNot = await promptTextInput$1(host, t("like.do_not"), {
132875
+ subtitle: t("like.do_not_hint"),
132876
+ placeholder: t("like.do_not_example"),
132877
+ initialValue: current.doNot,
132878
+ allowEmpty: true
132879
+ });
132880
+ if (doNot === void 0) {
132881
+ host.showStatus(t("like.cancelled"), host.state.theme.colors.textDim);
132882
+ return;
132883
+ }
132669
132884
  await persistLikePreferences(host, {
132670
132885
  nickname: nickname.trim().length > 0 ? nickname.trim() : void 0,
132671
132886
  tone: tone.trim().length > 0 ? tone.trim() : void 0,
132672
- other: other.trim().length > 0 ? other.trim() : void 0
132887
+ other: other.trim().length > 0 ? other.trim() : void 0,
132888
+ doNot: doNot.trim().length > 0 ? doNot.trim() : void 0
132673
132889
  });
132674
132890
  host.showStatus(t("like.saved"), host.state.theme.colors.success);
132675
132891
  }
@@ -134681,8 +134897,8 @@ async function handleKnowledgeCommand(host, _args) {
134681
134897
  switch (status) {
134682
134898
  case "ready": return "";
134683
134899
  case "downloading": return " · " + t("kw.embedding_downloading");
134684
- case "failed": return " · " + t("kw.embedding_failed");
134685
- case "idle": return " · " + t("kw.embedding_not_downloaded");
134900
+ case "failed": return " · " + t("kw.embedding_failed") + t("kw.embedding_data_intact");
134901
+ case "idle": return " · " + t("kw.embedding_not_downloaded") + t("kw.embedding_data_intact");
134686
134902
  }
134687
134903
  };
134688
134904
  const showMenu = () => {
@@ -141059,7 +141275,7 @@ var InputController = class InputController {
141059
141275
  this.host = host;
141060
141276
  }
141061
141277
  setupAutocomplete() {
141062
- const visible = this.host.getSlashCommands().filter((cmd) => !cmd.name.startsWith("skill:"));
141278
+ const visible = this.host.getSlashCommands().filter((cmd) => !(cmd.name.startsWith("skill:") && cmd.source === "builtin"));
141063
141279
  const slashCommands = visible.map((cmd) => cmd);
141064
141280
  const { state } = this.host;
141065
141281
  const provider = new FileMentionProvider(slashCommands, state.appState.workDir, state.fdPath, state.gitLsFilesCache);
@@ -142967,8 +143183,10 @@ var SessionManager$1 = class {
142967
143183
  wallClockBaseAt: Date.now()
142968
143184
  } : null,
142969
143185
  goalActive: goal?.status === "active",
142970
- goalContinuationCount: 0
143186
+ goalContinuationCount: 0,
143187
+ providerBalance: null
142971
143188
  });
143189
+ refreshProviderBalance(status.model ?? "", (patch) => this.host.setAppState(patch));
142972
143190
  }
142973
143191
  async activateRuntime() {
142974
143192
  const session = this.requireSession();
@@ -143815,6 +144033,8 @@ var MemoryPickerComponent = class extends Container {
143815
144033
  searchQuery = "";
143816
144034
  isSearching = false;
143817
144035
  searchInput = "";
144036
+ /** Memo ids ticked for batch delete (space toggles, a selects all). */
144037
+ selectedIds = /* @__PURE__ */ new Set();
143818
144038
  memos;
143819
144039
  total;
143820
144040
  loading;
@@ -143843,6 +144063,7 @@ var MemoryPickerComponent = class extends Container {
143843
144063
  this.memos = result.memos;
143844
144064
  this.total = result.total;
143845
144065
  this.selectedIndex = 0;
144066
+ this.selectedIds.clear();
143846
144067
  } catch {
143847
144068
  this.memos = [];
143848
144069
  this.total = 0;
@@ -143874,8 +144095,11 @@ var MemoryPickerComponent = class extends Container {
143874
144095
  }
143875
144096
  if (this.mode === "confirmDelete") {
143876
144097
  if (matchesKey(data, Key.enter)) {
143877
- const memo = this.memos[this.selectedIndex];
143878
- if (memo) this.deleteAndReload(memo.id);
144098
+ if (this.selectedIds.size > 0) this.deleteSelectedAndReload();
144099
+ else {
144100
+ const memo = this.memos[this.selectedIndex];
144101
+ if (memo) this.deleteAndReload(memo.id);
144102
+ }
143879
144103
  return;
143880
144104
  }
143881
144105
  if (matchesKey(data, Key.escape)) {
@@ -143922,7 +144146,23 @@ var MemoryPickerComponent = class extends Container {
143922
144146
  return;
143923
144147
  }
143924
144148
  if (ch === "d" || ch === "D") {
143925
- if (this.memos.length > 0) this.mode = "confirmDelete";
144149
+ if (this.selectedIds.size > 0 || this.memos.length > 0) this.mode = "confirmDelete";
144150
+ return;
144151
+ }
144152
+ if (ch === " ") {
144153
+ const memo = this.memos[this.selectedIndex];
144154
+ if (memo) {
144155
+ if (this.selectedIds.has(memo.id)) this.selectedIds.delete(memo.id);
144156
+ else this.selectedIds.add(memo.id);
144157
+ this.ui?.requestRender();
144158
+ }
144159
+ return;
144160
+ }
144161
+ if (ch === "a" || ch === "A") {
144162
+ if (this.memos.length === 0) return;
144163
+ if (this.memos.every((memo) => this.selectedIds.has(memo.id))) this.selectedIds.clear();
144164
+ else for (const memo of this.memos) this.selectedIds.add(memo.id);
144165
+ this.ui?.requestRender();
143926
144166
  return;
143927
144167
  }
143928
144168
  if (ch === "/") {
@@ -143951,6 +144191,15 @@ var MemoryPickerComponent = class extends Container {
143951
144191
  if (this.selectedIndex >= this.memos.length) this.selectedIndex = Math.max(0, this.memos.length - 1);
143952
144192
  this.ui?.requestRender();
143953
144193
  }
144194
+ async deleteSelectedAndReload() {
144195
+ const ids = [...this.selectedIds];
144196
+ for (const id of ids) try {
144197
+ await this.store.delete(id);
144198
+ } catch {}
144199
+ await this.loadMemos();
144200
+ this.mode = "list";
144201
+ this.ui?.requestRender();
144202
+ }
143954
144203
  render(width) {
143955
144204
  const c = this.colors;
143956
144205
  const lines = [];
@@ -143963,7 +144212,7 @@ var MemoryPickerComponent = class extends Container {
143963
144212
  return lines;
143964
144213
  }
143965
144214
  const headerLabel = t("memory.notebook_title");
143966
- const headerHint = this.searchQuery.length > 0 ? t("memory.esc_clear_search") : t("memory.nav_hint");
144215
+ 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");
143967
144216
  const labelWidth = visibleWidth(headerLabel);
143968
144217
  const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS$1);
143969
144218
  lines.push(chalk.hex(c.primary).bold(headerLabel) + chalk.hex(c.textMuted)(shownHint));
@@ -143984,10 +144233,15 @@ var MemoryPickerComponent = class extends Container {
143984
144233
  }
143985
144234
  if (this.mode === "detail" && this.detailMemo) return this.renderDetail(lines, width, c);
143986
144235
  if (this.mode === "confirmDelete") {
143987
- const memo = this.memos[this.selectedIndex];
143988
- if (memo) {
143989
- lines.push(truncateToWidth(chalk.hex(c.warning).bold(` ${t("memory.deleting")}${memo.userNeed}`), width, ELLIPSIS$1));
143990
- lines.push(chalk.hex(c.warning)(t("memory.delete_confirm_hint")));
144236
+ if (this.selectedIds.size > 0) {
144237
+ lines.push(truncateToWidth(chalk.hex(c.warning).bold(t("memory.batch_delete_confirm", { count: String(this.selectedIds.size) })), width, ELLIPSIS$1));
144238
+ lines.push(chalk.hex(c.warning)(t("memory.batch_delete_hint")));
144239
+ } else {
144240
+ const memo = this.memos[this.selectedIndex];
144241
+ if (memo) {
144242
+ lines.push(truncateToWidth(chalk.hex(c.warning).bold(` ${t("memory.deleting")}${memo.userNeed}`), width, ELLIPSIS$1));
144243
+ lines.push(chalk.hex(c.warning)(t("memory.delete_confirm_hint")));
144244
+ }
143991
144245
  }
143992
144246
  lines.push(chalk.hex(c.primary)("─".repeat(width)));
143993
144247
  return lines;
@@ -144015,6 +144269,7 @@ var MemoryPickerComponent = class extends Container {
144015
144269
  renderMemoCard(width, memo, isSelected) {
144016
144270
  const c = this.colors;
144017
144271
  const pointer = isSelected ? "❯" : " ";
144272
+ const check = this.selectedIds.has(memo.id) ? "☑" : "□";
144018
144273
  const indent = " ";
144019
144274
  const indentWidth = visibleWidth(indent);
144020
144275
  const titleColor = isSelected ? c.primary : c.text;
@@ -144022,10 +144277,12 @@ var MemoryPickerComponent = class extends Container {
144022
144277
  const trailingParts = [formatRelativeTime$1(memo.recordedAt), sourceLabel(memo.extractionSource)].filter((p) => p.length > 0);
144023
144278
  const trailingText = trailingParts.length > 0 ? " " + trailingParts.join(" ") : "";
144024
144279
  const trailingWidth = visibleWidth(trailingText);
144025
- const headerPrefixWidth = visibleWidth(pointer) + 1;
144280
+ const headerPrefixWidth = visibleWidth(pointer) + 1 + visibleWidth(check) + 1;
144026
144281
  const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
144027
144282
  const shownTitle = truncateToWidth(singleLine$1(memo.userNeed), titleBudget, ELLIPSIS$1);
144283
+ const checkColor = this.selectedIds.has(memo.id) ? c.primary : c.textDim;
144028
144284
  let header = chalk.hex(isSelected ? c.primary : c.textDim)(pointer + " ");
144285
+ header += chalk.hex(checkColor)(check + " ");
144029
144286
  header += titleStyle(shownTitle);
144030
144287
  if (trailingText.length > 0) header += chalk.hex(c.textDim)(trailingText);
144031
144288
  const card = [truncateToWidth(header, width, ELLIPSIS$1)];
@@ -144162,12 +144419,15 @@ var SessionPickerComponent = class extends Container {
144162
144419
  onSelect;
144163
144420
  onCancel;
144164
144421
  onDelete;
144422
+ onDeleteMany;
144165
144423
  onStatus;
144166
144424
  maxVisibleSessions;
144167
144425
  loading;
144168
144426
  focused = false;
144169
144427
  selectedIndex = 0;
144170
144428
  confirmingDelete = false;
144429
+ /** Session ids ticked for batch delete (space toggles, a selects all). */
144430
+ selectedIds = /* @__PURE__ */ new Set();
144171
144431
  constructor(opts) {
144172
144432
  super();
144173
144433
  this.sessions = opts.sessions;
@@ -144177,6 +144437,7 @@ var SessionPickerComponent = class extends Container {
144177
144437
  this.onSelect = opts.onSelect;
144178
144438
  this.onCancel = opts.onCancel;
144179
144439
  this.onDelete = opts.onDelete;
144440
+ this.onDeleteMany = opts.onDeleteMany;
144180
144441
  this.onStatus = opts.onStatus;
144181
144442
  this.maxVisibleSessions = opts.maxVisibleSessions ?? 4;
144182
144443
  }
@@ -144194,6 +144455,15 @@ var SessionPickerComponent = class extends Container {
144194
144455
  if (!session) return;
144195
144456
  if (this.confirmingDelete) {
144196
144457
  this.confirmingDelete = false;
144458
+ if (this.selectedIds.size > 0) {
144459
+ if (this.onDeleteMany === void 0) {
144460
+ this.confirmingDelete = true;
144461
+ this.onStatus(t("session_picker.batch_delete_unavailable"));
144462
+ return;
144463
+ }
144464
+ this.onDeleteMany?.([...this.selectedIds]);
144465
+ return;
144466
+ }
144197
144467
  if (session.metadata?.["source"] === "cc-connect") {
144198
144468
  this.onStatus(t("session_picker.cc_restricted"));
144199
144469
  return;
@@ -144209,11 +144479,13 @@ var SessionPickerComponent = class extends Container {
144209
144479
  return;
144210
144480
  }
144211
144481
  if (matchesKey(data, Key.up)) {
144482
+ if (this.confirmingDelete) return;
144212
144483
  this.selectedIndex = Math.max(0, this.selectedIndex - 1);
144213
144484
  this.confirmingDelete = false;
144214
144485
  return;
144215
144486
  }
144216
144487
  if (matchesKey(data, Key.down)) {
144488
+ if (this.confirmingDelete) return;
144217
144489
  this.selectedIndex = Math.min(this.sessions.length - 1, this.selectedIndex + 1);
144218
144490
  this.confirmingDelete = false;
144219
144491
  return;
@@ -144228,6 +144500,26 @@ var SessionPickerComponent = class extends Container {
144228
144500
  }
144229
144501
  if (session.id !== this.currentSessionId) this.confirmingDelete = true;
144230
144502
  }
144503
+ if (k === " " && !this.confirmingDelete) {
144504
+ const session = this.sessions[this.selectedIndex];
144505
+ if (!session || session.id === this.currentSessionId) return;
144506
+ if (session.metadata?.["source"] === "cc-connect") {
144507
+ this.onStatus(t("session_picker.cc_restricted"));
144508
+ return;
144509
+ }
144510
+ if (this.selectedIds.has(session.id)) this.selectedIds.delete(session.id);
144511
+ else this.selectedIds.add(session.id);
144512
+ return;
144513
+ }
144514
+ if (k === "a" || k === "A") {
144515
+ if (this.confirmingDelete) return;
144516
+ if (this.sessions.length === 0) return;
144517
+ const selectable = this.sessions.filter((s) => s.id !== this.currentSessionId && s.metadata?.["source"] !== "cc-connect");
144518
+ if (selectable.length === 0) return;
144519
+ if (selectable.every((s) => this.selectedIds.has(s.id))) this.selectedIds.clear();
144520
+ else for (const s of selectable) this.selectedIds.add(s.id);
144521
+ return;
144522
+ }
144231
144523
  }
144232
144524
  render(width) {
144233
144525
  const colors = this.colors;
@@ -144245,7 +144537,7 @@ var SessionPickerComponent = class extends Container {
144245
144537
  return lines;
144246
144538
  }
144247
144539
  const headerLabel = t("session.picker_title");
144248
- const headerHint = this.confirmingDelete ? t("session.delete_confirm") : t("session.picker_hint");
144540
+ 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");
144249
144541
  const labelWidth = visibleWidth(headerLabel);
144250
144542
  const shownHint = truncateToWidth(headerHint, Math.max(0, width - labelWidth), ELLIPSIS);
144251
144543
  const hintColor = this.confirmingDelete ? colors.warning : colors.textMuted;
@@ -144271,6 +144563,7 @@ var SessionPickerComponent = class extends Container {
144271
144563
  renderSessionCard(width, session, isSelected, isCurrent) {
144272
144564
  const colors = this.colors;
144273
144565
  const pointer = isSelected ? "❯" : " ";
144566
+ const check = this.selectedIds.has(session.id) ? "☑" : "□";
144274
144567
  const indent = " ";
144275
144568
  const indentWidth = visibleWidth(indent);
144276
144569
  const titleColor = isSelected ? colors.primary : colors.text;
@@ -144284,14 +144577,16 @@ var SessionPickerComponent = class extends Container {
144284
144577
  });
144285
144578
  const trailingParts = [time, badge].filter((p) => p.length > 0);
144286
144579
  const trailingWidth = visibleWidth(trailingParts.length > 0 ? " " + trailingParts.join(" ") : "");
144287
- const headerPrefixWidth = visibleWidth(pointer) + 1;
144580
+ const headerPrefixWidth = visibleWidth(pointer) + 1 + visibleWidth(check) + 1;
144288
144581
  const titleBudget = Math.max(8, width - headerPrefixWidth - trailingWidth);
144289
144582
  const shownTitle = truncateToWidth(singleLine(titleSource), titleBudget, ELLIPSIS);
144583
+ const checkColor = this.selectedIds.has(session.id) ? colors.primary : colors.textDim;
144290
144584
  let header = chalk.hex(isSelected ? colors.primary : colors.textDim)(pointer + " ");
144585
+ header += chalk.hex(checkColor)(check + " ");
144291
144586
  header += titleStyle(shownTitle);
144292
144587
  if (time.length > 0) header += " " + chalk.hex(colors.textDim)(time);
144293
144588
  if (badge.length > 0) header += " " + chalk.hex(colors.success)(badge);
144294
- const card = [header];
144589
+ const card = [truncateToWidth(header, width)];
144295
144590
  const fullId = session.id;
144296
144591
  const idWidth = visibleWidth(fullId);
144297
144592
  const metaGap = " ";
@@ -145036,6 +145331,27 @@ var DialogManager = class {
145036
145331
  }).catch((error) => {
145037
145332
  this.host.showError(error instanceof Error ? error.message : String(error));
145038
145333
  });
145334
+ },
145335
+ onDeleteMany: (sessionIds) => {
145336
+ const ccIds = new Set(this.host.getSessions().filter((s) => s.metadata?.["source"] === "cc-connect").map((s) => s.id));
145337
+ const deletable = sessionIds.filter((id) => !ccIds.has(id));
145338
+ if (deletable.length === 0) {
145339
+ this.host.showStatus(t("dialog.cc_managed"));
145340
+ return;
145341
+ }
145342
+ (async () => {
145343
+ for (const id of deletable) try {
145344
+ await this.host.deleteSession(id);
145345
+ } catch (error) {
145346
+ this.host.showError(error instanceof Error ? error.message : String(error));
145347
+ break;
145348
+ }
145349
+ await this.host.fetchSessions();
145350
+ if (this.host.getSessions().length === 0) this.hideSessionPicker();
145351
+ else if (this.host.state.activeDialog === "session-picker") this.mountSessionPicker(onCancel);
145352
+ })().catch((error) => {
145353
+ this.host.showError(error instanceof Error ? error.message : String(error));
145354
+ });
145039
145355
  }
145040
145356
  }));
145041
145357
  }
@@ -145170,6 +145486,7 @@ function createInitialAppState(input) {
145170
145486
  contextUsage: 0,
145171
145487
  contextTokens: 0,
145172
145488
  maxContextTokens: 0,
145489
+ providerBalance: null,
145173
145490
  isCompacting: false,
145174
145491
  lastCompactionFinishedAt: void 0,
145175
145492
  autoCompactionCount: 0,
@@ -145217,6 +145534,8 @@ var ScreamTUI = class {
145217
145534
  tightModeHandler = null;
145218
145535
  reverseRpcDisposers = [];
145219
145536
  startupNotice;
145537
+ /** Interval handle for the periodic provider-balance refresh. */
145538
+ balancePollTimer;
145220
145539
  updatePrefetched;
145221
145540
  sessionManager;
145222
145541
  dialogManager;
@@ -145323,6 +145642,7 @@ var ScreamTUI = class {
145323
145642
  try {
145324
145643
  await this.finishStartup(shouldReplayHistory);
145325
145644
  this.lifecycleController.startCcConnectPolling();
145645
+ this.startBalancePolling();
145326
145646
  } catch (error) {
145327
145647
  this.lifecycleController.disposeTerminalTracking();
145328
145648
  this.state.footer.dispose();
@@ -145357,6 +145677,18 @@ var ScreamTUI = class {
145357
145677
  this.state.ui.setFocus(this.state.editor);
145358
145678
  return shouldReplayHistory;
145359
145679
  }
145680
+ /**
145681
+ * Refresh the provider balance badge every 60s, but only for providers
145682
+ * whose balance we can actually query (local hostname check, no network
145683
+ * for unsupported ones). The api-balance layer caches per provider for
145684
+ * 60s, so each tick either serves the cache or performs one real lookup.
145685
+ */
145686
+ startBalancePolling() {
145687
+ this.balancePollTimer ??= setInterval(() => {
145688
+ const model = this.state.appState.model;
145689
+ if (supportsBalance(model)) refreshProviderBalance(model, (patch) => this.setAppState(patch));
145690
+ }, 6e4);
145691
+ }
145360
145692
  async finishStartup(shouldReplayHistory) {
145361
145693
  if (this.startupNotice !== void 0) {
145362
145694
  this.showStatus(this.startupNotice);
@@ -145399,6 +145731,10 @@ var ScreamTUI = class {
145399
145731
  }
145400
145732
  setTightMode(false);
145401
145733
  this.lifecycleController.stopCcConnectPolling();
145734
+ if (this.balancePollTimer !== void 0) {
145735
+ clearInterval(this.balancePollTimer);
145736
+ this.balancePollTimer = void 0;
145737
+ }
145402
145738
  this.lifecycleController.uninstallSignalHandlers();
145403
145739
  this.aborted = true;
145404
145740
  this.cancelInFlight?.();
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-D95_TREY.mjs")).main();
9
+ (await import("./app-CTeIrRAX.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);
@@ -431,6 +431,9 @@ const dictionaries = {
431
431
  "like.other": "其他偏好",
432
432
  "like.other_hint": "例如:多说例子、先给结论再展开、避免术语等(留空表示不设置)",
433
433
  "like.other_example": "例如:请用中文回答,避免缩写",
434
+ "like.do_not": "用户禁止",
435
+ "like.do_not_hint": "用户明确不让做的事。Agent 绝不能做这些。",
436
+ "like.do_not_example": "例如:未经询问绝不修改用户的配置文件",
434
437
  "like.cancelled": "已取消 /like 设置",
435
438
  "like.saved": "偏好已保存(下次新会话生效)",
436
439
  "revoke.streaming": "无法在 streaming 中撤回 — 请先按 Esc 或 Ctrl-C 取消。",
@@ -470,9 +473,12 @@ const dictionaries = {
470
473
  "session.loading": "正在加载会话...",
471
474
  "session_picker.empty": "未找到会话。按 Escape 关闭。",
472
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})",
473
479
  "session.picker_title": "会话 ",
474
480
  "session.delete_confirm": "⚠️ 按 Enter 确认删除,Esc 取消",
475
- "session.picker_hint": "(↑↓ 导航,Enter 选择,d 删除,Esc 取消)",
481
+ "session.picker_hint": "(↑↓ 导航,Enter 打开,空格 标记,a 全选,d 删除,Esc 取消)",
476
482
  "model.nav_model": "↑↓ 模型",
477
483
  "model.nav_thinking": "←→ 思考等级",
478
484
  "model.nav_page": "PgUp/PgDn 翻页",
@@ -535,13 +541,16 @@ const dictionaries = {
535
541
  "memory.search_label": "搜索: ",
536
542
  "memory.notebook_title": "记忆备忘录 ",
537
543
  "memory.esc_clear_search": "(Esc 清除搜索)",
538
- "memory.nav_hint": "(↑↓ 导航,Enter 查看,i 注入,d 删除,/ 搜索,Esc 关闭)",
544
+ "memory.nav_hint": "(↑↓ 导航,Enter 查看,空格 标记,a 全选,i 注入,d 删除,/ 搜索,Esc 关闭)",
539
545
  "memory.loading": "正在加载...",
540
546
  "memory.no_match": "未找到匹配 \"{query}\" 的记忆。",
541
547
  "memory.empty": "暂无记忆备忘录。",
542
548
  "memory.auto_extract_hint": " 压缩对话或退出会话时,系统会自动提取并保存。",
543
549
  "memory.deleting": "删除: ",
544
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] 删除选中",
545
554
  "memory.showing_range": "{start}-{end} / {total} 条",
546
555
  "memory.id_label": "ID: ",
547
556
  "memory.source_label": "来源: ",
@@ -724,6 +733,8 @@ const dictionaries = {
724
733
  "subagent.bind_title": "绑定 {profile}",
725
734
  "subagent.model_hint": "↑↓ 选择模型 · Enter 确认 · Esc 返回",
726
735
  "subagent.save_failed": "保存失败:{msg}",
736
+ "subagent.stale_binding": "{alias}(已失效)",
737
+ "subagent.invalid_alias": "模型别名 {alias} 不存在",
727
738
  "kdoctree.no_title": "(无标题)",
728
739
  "kdoctree.empty": "(空)",
729
740
  "kdoctree.move": "移动",
@@ -1134,6 +1145,7 @@ const dictionaries = {
1134
1145
  "kw.embedding_failed": "向量模型加载失败,请返回菜单选择重新下载(建议科学上网)",
1135
1146
  "kw.embedding_ready": "向量模型已就绪",
1136
1147
  "kw.embedding_not_downloaded": "向量模型未下载(选择「下载向量模型」手动下载)",
1148
+ "kw.embedding_data_intact": " —— 你的知识数据仍然存在,下载模型后即可正常使用",
1137
1149
  "kw.embedding_already_installed": "向量模型已安装,无需重新下载"
1138
1150
  },
1139
1151
  en: {
@@ -1468,6 +1480,9 @@ const dictionaries = {
1468
1480
  "like.other": "Other preferences",
1469
1481
  "like.other_hint": "e.g. more examples, conclusion first, avoid jargon (leave empty to skip)",
1470
1482
  "like.other_example": "e.g. Answer in English, avoid abbreviations",
1483
+ "like.do_not": "User Prohibitions",
1484
+ "like.do_not_hint": "Things the user explicitly does NOT want done. The agent must NEVER do these.",
1485
+ "like.do_not_example": "e.g. Never modify user config files without asking",
1471
1486
  "like.cancelled": "/like setup cancelled",
1472
1487
  "like.saved": "Preferences saved (takes effect in next session)",
1473
1488
  "revoke.streaming": "Cannot revoke during streaming — press Esc or Ctrl-C to cancel first.",
@@ -1507,9 +1522,12 @@ const dictionaries = {
1507
1522
  "session.loading": "Loading sessions...",
1508
1523
  "session_picker.empty": "No sessions found. Press Escape to close.",
1509
1524
  "session_picker.cc_restricted": "CC sessions cannot be switched or deleted. Use the file path below to manage manually",
1525
+ "session_picker.batch_delete_confirm": "Delete {count} selected sessions?",
1526
+ "session_picker.batch_delete_unavailable": "Batch delete is not available here.",
1527
+ "session_picker.batch_hint": "[space] select · [a] all · [d] delete selected ({count})",
1510
1528
  "session.picker_title": "Sessions ",
1511
1529
  "session.delete_confirm": "⚠️ Press Enter to confirm delete, Esc to cancel",
1512
- "session.picker_hint": "(↑↓ Navigate, Enter Select, d Delete, Esc Cancel)",
1530
+ "session.picker_hint": "(↑↓ Navigate, Enter Open, Space Mark, a All, d Delete, Esc Cancel)",
1513
1531
  "model.nav_model": "↑↓ Model",
1514
1532
  "model.nav_thinking": "←→ Thinking",
1515
1533
  "model.nav_page": "PgUp/PgDn Page",
@@ -1572,13 +1590,16 @@ const dictionaries = {
1572
1590
  "memory.search_label": "Search: ",
1573
1591
  "memory.notebook_title": "Memory Notebook ",
1574
1592
  "memory.esc_clear_search": "(Esc clear search)",
1575
- "memory.nav_hint": "(↑↓ Navigate, Enter View, i Inject, d Delete, / Search, Esc Close)",
1593
+ "memory.nav_hint": "(↑↓ Navigate, Enter View, Space Mark, a All, i Inject, d Delete, / Search, Esc Close)",
1576
1594
  "memory.loading": "Loading...",
1577
1595
  "memory.no_match": "No memories matching \"{query}\".",
1578
1596
  "memory.empty": "No memories yet.",
1579
1597
  "memory.auto_extract_hint": " Memories are extracted automatically when compacting or exiting a session.",
1580
1598
  "memory.deleting": "Delete: ",
1581
1599
  "memory.delete_confirm_hint": " Press Enter to confirm delete, Esc to cancel",
1600
+ "memory.batch_delete_confirm": "Delete {count} selected memos?",
1601
+ "memory.batch_delete_hint": " Press Enter to confirm batch delete, Esc to cancel",
1602
+ "memory.batch_hint": "{count} selected · [space] select · [a] all · [d] delete selected",
1582
1603
  "memory.showing_range": "{start}-{end} / {total} items",
1583
1604
  "memory.id_label": "ID: ",
1584
1605
  "memory.source_label": "Source: ",
@@ -1761,6 +1782,8 @@ const dictionaries = {
1761
1782
  "subagent.bind_title": "Bind {profile}",
1762
1783
  "subagent.model_hint": "↑↓ Select model · Enter confirm · Esc back",
1763
1784
  "subagent.save_failed": "Save failed: {msg}",
1785
+ "subagent.stale_binding": "{alias} (stale)",
1786
+ "subagent.invalid_alias": "Model alias {alias} does not exist",
1764
1787
  "kdoctree.no_title": "(untitled)",
1765
1788
  "kdoctree.empty": "(empty)",
1766
1789
  "kdoctree.move": "Move",
@@ -2171,6 +2194,7 @@ const dictionaries = {
2171
2194
  "kw.embedding_failed": "Vector model load failed. Return to the menu and select Download to retry.",
2172
2195
  "kw.embedding_ready": "Vector model ready",
2173
2196
  "kw.embedding_not_downloaded": "Vector model not downloaded (select Download vector model)",
2197
+ "kw.embedding_data_intact": " — your knowledge data is still there; download the model to use it again",
2174
2198
  "kw.embedding_already_installed": "Vector model already installed, no need to re-download"
2175
2199
  }
2176
2200
  };
@@ -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-BdUmo73M.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-COl6Uu7m.mjs";
7
7
  export { TextInputDialogComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.11.7",
3
+ "version": "0.11.9",
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",