scream-code 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img width="128" height="128" alt="welcome_副本" src="https://github.com/user-attachments/assets/11d86774-b308-4cf7-857b-4c313670ade4" />
2
+ <img width="200" height="200" alt="welcome_副本" src="https://github.com/user-attachments/assets/11d86774-b308-4cf7-857b-4c313670ade4" />
3
3
  </p>
4
4
 
5
5
  <p align="center">
@@ -5,8 +5,8 @@ const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
6
  import { a as __toESM, i as __require, r as __exportAll, t as __commonJSMin } from "./chunk-apG1qJts.mjs";
7
7
  import "./suppress-sqlite-warning-C2VB0doZ.mjs";
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-1LHYB0xM.mjs";
9
- import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-os5jWxGT.mjs";
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-oeUnRY3N.mjs";
9
+ import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-D_xK0DJH.mjs";
10
10
  import { createRequire } from "node:module";
11
11
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
12
12
  import Jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -22,7 +22,7 @@ import { finished, pipeline as pipeline$1 } from "node:stream/promises";
22
22
  import * as vs from "zlib";
23
23
  import Qr from "zlib";
24
24
  import * as fs$9 from "node:fs";
25
- import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
25
+ import Vt, { appendFileSync, chmodSync, closeSync, constants, createReadStream, createWriteStream as createWriteStream$1, existsSync, fsyncSync, mkdirSync, openSync, promises, readFileSync, readSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
26
26
  import * as path$8 from "node:path";
27
27
  import path, { basename, dirname as dirname$1, extname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
28
28
  import { z } from "zod";
@@ -9190,7 +9190,7 @@ var AnthropicChatProvider = class {
9190
9190
  const kwargs = {
9191
9191
  thinking: {
9192
9192
  type: "enabled",
9193
- budget_tokens: budgetTokensForEffort(effectiveEffort)
9193
+ budget_tokens: budgetTokensForEffort(effectiveEffort === "xhigh" || effectiveEffort === "max" ? "high" : effectiveEffort)
9194
9194
  },
9195
9195
  betaFeatures: newBetas
9196
9196
  };
@@ -37877,6 +37877,7 @@ function messagesToGoogleGenAIContents(messages) {
37877
37877
  toolMessages.push(toolMsg);
37878
37878
  j += 1;
37879
37879
  }
37880
+ if (toolMessages.length === 0) throw new ChatProviderError(`Missing tool responses for ids: ${expectedToolCallIds.join(", ")}`);
37880
37881
  if (toolMessages.length > 0) {
37881
37882
  const toolMsgById = /* @__PURE__ */ new Map();
37882
37883
  const seenToolCallIds = /* @__PURE__ */ new Set();
@@ -48773,6 +48774,7 @@ var OpenAILegacyChatProvider = class {
48773
48774
  _defaultHeaders;
48774
48775
  _reasoningKey;
48775
48776
  _reasoningEffort;
48777
+ _thinkingExplicitlyOff = false;
48776
48778
  _generationKwargs;
48777
48779
  _toolMessageConversion;
48778
48780
  _client;
@@ -48821,7 +48823,7 @@ var OpenAILegacyChatProvider = class {
48821
48823
  for (const msg of normalizedHistory) messages.push(convertMessage$1(msg, this._reasoningKey, this._toolMessageConversion));
48822
48824
  const kwargs = { ...this._generationKwargs };
48823
48825
  let reasoningEffort = this._reasoningEffort;
48824
- if (reasoningEffort === void 0 && kwargs["reasoning_effort"] === void 0) {
48826
+ if (!this._thinkingExplicitlyOff && reasoningEffort === void 0 && kwargs["reasoning_effort"] === void 0) {
48825
48827
  if (history.some((message) => message.content.some((part) => part.type === "think"))) reasoningEffort = "medium";
48826
48828
  }
48827
48829
  for (const key of Object.keys(kwargs)) if (kwargs[key] === void 0) delete kwargs[key];
@@ -48844,6 +48846,7 @@ var OpenAILegacyChatProvider = class {
48844
48846
  const reasoningEffort = thinkingEffortToReasoningEffort(effort);
48845
48847
  const clone = this._clone();
48846
48848
  clone._reasoningEffort = reasoningEffort;
48849
+ clone._thinkingExplicitlyOff = effort === "off";
48847
48850
  return clone;
48848
48851
  }
48849
48852
  withGenerationKwargs(kwargs) {
@@ -56837,7 +56840,10 @@ var MemoryMemoStore = class MemoryMemoStore {
56837
56840
  initPromise = null;
56838
56841
  async init() {
56839
56842
  if (this.initPromise) return this.initPromise;
56840
- this.initPromise = this._doInit();
56843
+ this.initPromise = this._doInit().catch((error) => {
56844
+ this.initPromise = null;
56845
+ throw error;
56846
+ });
56841
56847
  return this.initPromise;
56842
56848
  }
56843
56849
  async _doInit() {
@@ -56853,6 +56859,11 @@ var MemoryMemoStore = class MemoryMemoStore {
56853
56859
  }
56854
56860
  /** Close the database handle and checkpoint WAL. */
56855
56861
  close() {
56862
+ if (this.embeddingTimer !== void 0) {
56863
+ clearTimeout(this.embeddingTimer);
56864
+ this.embeddingTimer = void 0;
56865
+ }
56866
+ this.embeddingQueue.clear();
56856
56867
  if (this.db !== void 0) {
56857
56868
  this.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
56858
56869
  this.db.close();
@@ -57126,6 +57137,7 @@ var MemoryMemoStore = class MemoryMemoStore {
57126
57137
  try {
57127
57138
  for (const memo of memos) {
57128
57139
  const row = insert.get(memo.id, memo.sourceSessionId, memo.sourceSessionTitle ?? null, memo.userNeed, memo.approach, memo.outcome, memo.whatFailed, memo.whatWorked, memo.extractionSource, memo.recordedAt, memo.projectDir ?? "", JSON.stringify(memo.tags ?? []));
57140
+ if (row === void 0) continue;
57129
57141
  insertFts.run(row.rowid, toFtsText(memo.userNeed), toFtsText(memo.approach), toFtsText(memo.whatFailed), toFtsText(memo.whatWorked), toFtsText(memo.sourceSessionTitle ?? ""));
57130
57142
  }
57131
57143
  this.db.exec("COMMIT");
@@ -57188,6 +57200,7 @@ var MemoryMemoStore = class MemoryMemoStore {
57188
57200
  const updateFts = this.db.prepare(`INSERT INTO memos_fts(rowid, user_need, approach, what_failed, what_worked, source_session_title)
57189
57201
  VALUES (?, ?, ?, ?, ?, ?)`);
57190
57202
  const deleteFts = this.db.prepare("INSERT INTO memos_fts(memos_fts, rowid) VALUES ('delete', ?)");
57203
+ const deleteEmbedding = this.db.prepare("DELETE FROM memory_embeddings WHERE memory_id = ?");
57191
57204
  this.db.exec("BEGIN TRANSACTION");
57192
57205
  try {
57193
57206
  const oldRow = selectRow.get(id);
@@ -57202,7 +57215,9 @@ var MemoryMemoStore = class MemoryMemoStore {
57202
57215
  }
57203
57216
  deleteFts.run(oldRow.rowid);
57204
57217
  updateFts.run(row.rowid, toFtsText(updated.userNeed), toFtsText(updated.approach), toFtsText(updated.whatFailed), toFtsText(updated.whatWorked), toFtsText(updated.sourceSessionTitle ?? ""));
57218
+ deleteEmbedding.run(id);
57205
57219
  this.db.exec("COMMIT");
57220
+ this.scheduleEmbedding(updated);
57206
57221
  return true;
57207
57222
  } catch {
57208
57223
  this.db.exec("ROLLBACK");
@@ -57613,6 +57628,7 @@ async function applyConsolidation(store, plan) {
57613
57628
  whatWorked: worked || "none",
57614
57629
  whatFailed: failed || "none",
57615
57630
  tags: normalizeTags(plan.resolved.flatMap((m) => m.tags ?? [])),
57631
+ projectDir: plan.resolved[0].projectDir,
57616
57632
  extractionSource: "compaction"
57617
57633
  });
57618
57634
  await store.append(archive);
@@ -57631,6 +57647,7 @@ async function applyConsolidation(store, plan) {
57631
57647
  whatWorked: worked || "none",
57632
57648
  whatFailed: failed || "none",
57633
57649
  tags: normalizeTags(plan.stale.flatMap((m) => m.tags ?? [])),
57650
+ projectDir: plan.stale[0].projectDir,
57634
57651
  extractionSource: "compaction"
57635
57652
  });
57636
57653
  await store.append(archive);
@@ -57649,6 +57666,7 @@ async function applyConsolidation(store, plan) {
57649
57666
  whatFailed: group.merged.whatFailed,
57650
57667
  whatWorked: group.merged.whatWorked,
57651
57668
  tags: group.merged.tags ?? mergedTags,
57669
+ projectDir: newest.projectDir,
57652
57670
  extractionSource: "compaction"
57653
57671
  });
57654
57672
  await store.append(merged);
@@ -58334,7 +58352,7 @@ var KnowledgeLookupTool = class {
58334
58352
  const llm = { generate: async (systemPrompt, userPrompt) => {
58335
58353
  return this.agent.generateText(systemPrompt, userPrompt);
58336
58354
  } };
58337
- const { multiSearchWithTrace } = await import("./src-DA9VZFf3.mjs");
58355
+ const { multiSearchWithTrace } = await import("./src-B2kaYK-M.mjs");
58338
58356
  const { results, trace } = await multiSearchWithTrace(store, llm, query, { topK });
58339
58357
  if (results.length === 0) return {
58340
58358
  isError: false,
@@ -76673,7 +76691,8 @@ var ReadTool = class {
76673
76691
  hasLf: false,
76674
76692
  hasLoneCr: false
76675
76693
  };
76676
- const rawBuffer = [];
76694
+ const rawHash = createHash("sha256");
76695
+ const normalizedHash = createHash("sha256");
76677
76696
  let currentLineNo = 0;
76678
76697
  let maxLinesReached = false;
76679
76698
  let collectionClosed = false;
@@ -76684,7 +76703,8 @@ var ReadTool = class {
76684
76703
  };
76685
76704
  currentLineNo += 1;
76686
76705
  updateLineEndingFlags(flags, rawLine);
76687
- rawBuffer.push(rawLine);
76706
+ rawHash.update(rawLine);
76707
+ normalizedHash.update(rawLine.replaceAll("\r\n", "\n"));
76688
76708
  if (collectionClosed) {
76689
76709
  if (effectiveLimit >= 1e3 && currentLineNo >= lineOffset) maxLinesReached = true;
76690
76710
  continue;
@@ -76702,7 +76722,7 @@ var ReadTool = class {
76702
76722
  if (selectedEntries.length >= effectiveLimit) collectionClosed = true;
76703
76723
  }
76704
76724
  const lineEndingStyle = lineEndingStyleFromFlags(flags);
76705
- const anchor = computeAnchor(toModelTextView(rawBuffer.join("")).text);
76725
+ const anchor = (lineEndingStyle === "crlf" ? normalizedHash : rawHash).digest("hex").slice(0, 8);
76706
76726
  const renderedLines = [];
76707
76727
  const truncatedLineNumbers = [];
76708
76728
  let bytes = 0;
@@ -76742,7 +76762,8 @@ var ReadTool = class {
76742
76762
  hasLf: false,
76743
76763
  hasLoneCr: false
76744
76764
  };
76745
- const rawBuffer = [];
76765
+ const rawHash = createHash("sha256");
76766
+ const normalizedHash = createHash("sha256");
76746
76767
  let currentLineNo = 0;
76747
76768
  for await (const rawLine of this.jian.readLines(safePath, { errors: "strict" })) {
76748
76769
  if (containsNulByte(rawLine)) return {
@@ -76751,7 +76772,8 @@ var ReadTool = class {
76751
76772
  };
76752
76773
  currentLineNo += 1;
76753
76774
  updateLineEndingFlags(flags, rawLine);
76754
- rawBuffer.push(rawLine);
76775
+ rawHash.update(rawLine);
76776
+ normalizedHash.update(rawLine.replaceAll("\r\n", "\n"));
76755
76777
  entries.push({
76756
76778
  lineNo: currentLineNo,
76757
76779
  rawContent: stripTrailingLf(rawLine)
@@ -76759,7 +76781,7 @@ var ReadTool = class {
76759
76781
  if (entries.length > tailCount) entries.shift();
76760
76782
  }
76761
76783
  const lineEndingStyle = lineEndingStyleFromFlags(flags);
76762
- const anchor = computeAnchor(toModelTextView(rawBuffer.join("")).text);
76784
+ const anchor = (lineEndingStyle === "crlf" ? normalizedHash : rawHash).digest("hex").slice(0, 8);
76763
76785
  let renderedCandidates = entries.slice(0, effectiveLimit).map((entry) => {
76764
76786
  return {
76765
76787
  entry,
@@ -78086,7 +78108,7 @@ var BashTool = class {
78086
78108
  const info = backgroundManager.getTask(taskId);
78087
78109
  if (info && info.status === "running") backgroundManager.stop(taskId, "Timed out");
78088
78110
  })();
78089
- }, timeoutMs);
78111
+ }, timeoutMs).unref();
78090
78112
  const status = backgroundManager.getTask(taskId).status;
78091
78113
  const builder = new ToolResultBuilder();
78092
78114
  builder.write(`task_id: ${taskId}\npid: ${String(proc.pid)}\ndescription: ${args.description.trim()}\nstatus: ${status}\nautomatic_notification: true\nnext_step: You will be automatically notified when it completes.
@@ -79152,7 +79174,10 @@ var FullCompaction = class {
79152
79174
  if (this.compactionCountInTurn > this.strategy.maxCompactionPerTurn) return;
79153
79175
  if (this.agent.records.restoring) return;
79154
79176
  const compactedCount = this.strategy.computeCompactCount(this.agent.context.history, data.source);
79155
- if (compactedCount === 0) throw new ScreamError(ErrorCodes.COMPACTION_UNABLE, "No prefix that can be compacted in current history.");
79177
+ if (compactedCount === 0) {
79178
+ if (data.source === "manual") throw new ScreamError(ErrorCodes.COMPACTION_UNABLE, "No prefix that can be compacted in current history.");
79179
+ return;
79180
+ }
79156
79181
  this.agent.records.logRecord({
79157
79182
  type: "full_compaction.begin",
79158
79183
  ...data
@@ -80881,7 +80906,8 @@ var ContextMemory = class {
80881
80906
  toolCalls: [],
80882
80907
  origin: { kind: "compaction_summary" }
80883
80908
  }, ...this._history.slice(summary.compactedCount)];
80884
- this.openSteps.clear();
80909
+ const survivingMessages = new Set(this._history);
80910
+ for (const [uuid, message] of this.openSteps) if (!survivingMessages.has(message)) this.openSteps.delete(uuid);
80885
80911
  this.flushDeferredMessagesIfToolExchangeClosed();
80886
80912
  this._tokenCount = summary.tokensAfter;
80887
80913
  this.tokenCountCoveredMessageCount = this._history.length;
@@ -92274,8 +92300,7 @@ function parseToolCallArguments(raw) {
92274
92300
  */
92275
92301
  function repairTruncatedJson(raw) {
92276
92302
  let repaired = raw.trimEnd();
92277
- let braceOpen = 0;
92278
- let bracketOpen = 0;
92303
+ const openStack = [];
92279
92304
  let inString = false;
92280
92305
  let stringHasEscape = false;
92281
92306
  let lastNonWhitespace = "";
@@ -92293,24 +92318,16 @@ function repairTruncatedJson(raw) {
92293
92318
  }
92294
92319
  stringHasEscape = false;
92295
92320
  if (inString) continue;
92296
- if (ch === "{") braceOpen++;
92297
- if (ch === "}") braceOpen--;
92298
- if (ch === "[") bracketOpen++;
92299
- if (ch === "]") bracketOpen--;
92321
+ if (ch === "{") openStack.push("}");
92322
+ else if (ch === "[") openStack.push("]");
92323
+ else if (ch === "}" || ch === "]") {
92324
+ if (openStack.length > 0 && openStack[openStack.length - 1] === ch) openStack.pop();
92325
+ }
92300
92326
  if (!/\s/.test(ch)) lastNonWhitespace = ch;
92301
92327
  }
92302
92328
  if (inString) repaired += "\"";
92303
92329
  if (lastNonWhitespace === ",") repaired += "null";
92304
- const closers = [];
92305
- while (bracketOpen > 0) {
92306
- closers.push("]");
92307
- bracketOpen--;
92308
- }
92309
- while (braceOpen > 0) {
92310
- closers.push("}");
92311
- braceOpen--;
92312
- }
92313
- if (closers.length > 0) repaired += closers.join("");
92330
+ for (let i = openStack.length - 1; i >= 0; i--) repaired += openStack[i];
92314
92331
  return repaired;
92315
92332
  }
92316
92333
  function validateExecutableToolArgs(tool, args) {
@@ -97920,7 +97937,7 @@ var TurnFlow = class {
97920
97937
  });
97921
97938
  return null;
97922
97939
  }
97923
- if (this.turnId === -1) this.agent.dreamTracker.init().then(() => this.agent.dreamTracker.recordNewSession());
97940
+ if (this.turnId === -1) this.agent.dreamTracker.init().then(() => this.agent.dreamTracker.recordNewSession()).catch(() => {});
97924
97941
  const turnId = this.allocateTurnId();
97925
97942
  const controller = new AbortController();
97926
97943
  const promise = this.turnWorker(turnId, input, origin, controller.signal);
@@ -104227,7 +104244,7 @@ var Session$1 = class {
104227
104244
  });
104228
104245
  await this.options.jian.writeText(this.metadataPath, text);
104229
104246
  };
104230
- this.writeMetadataPromise = this.writeMetadataPromise.then(() => write());
104247
+ this.writeMetadataPromise = this.writeMetadataPromise.then(() => write(), () => write());
104231
104248
  return this.writeMetadataPromise;
104232
104249
  }
104233
104250
  async readMetadata() {
@@ -117201,6 +117218,7 @@ setPrototypeOf(Document, Document$1).prototype = Document$1.prototype;
117201
117218
  */
117202
117219
  const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36";
117203
117220
  const DEFAULT_MAX_BYTES = 10 * 1024 * 1024;
117221
+ const FETCH_TIMEOUT_MS = 3e4;
117204
117222
  const parseHTML = parseHTML$1;
117205
117223
  /**
117206
117224
  * SSRF guard — reject non-http(s) schemes and (by default) any hostname
@@ -117296,7 +117314,8 @@ var LocalFetchURLProvider = class {
117296
117314
  async fetchFresh(url) {
117297
117315
  const response = await this.fetchImpl(url, {
117298
117316
  method: "GET",
117299
- headers: { "User-Agent": this.userAgent }
117317
+ headers: { "User-Agent": this.userAgent },
117318
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
117300
117319
  });
117301
117320
  if (response.status >= 400) {
117302
117321
  await response.body?.cancel().catch(() => {});
@@ -117305,7 +117324,10 @@ var LocalFetchURLProvider = class {
117305
117324
  const contentLengthRaw = response.headers.get("content-length");
117306
117325
  if (contentLengthRaw !== null) {
117307
117326
  const cl = Number(contentLengthRaw);
117308
- if (Number.isFinite(cl) && cl > this.maxBytes) throw new Error(`Response body too large: ${String(cl)} bytes exceeds maxBytes (${String(this.maxBytes)}).`);
117327
+ if (Number.isFinite(cl) && cl > this.maxBytes) {
117328
+ await response.body?.cancel().catch(() => {});
117329
+ throw new Error(`Response body too large: ${String(cl)} bytes exceeds maxBytes (${String(this.maxBytes)}).`);
117330
+ }
117309
117331
  }
117310
117332
  const body = await response.text();
117311
117333
  const actualBytes = Buffer.byteLength(body, "utf8");
@@ -122505,7 +122527,7 @@ function optionalBuildString(value) {
122505
122527
  return typeof value === "string" && value.length > 0 ? value : void 0;
122506
122528
  }
122507
122529
  const SCREAM_BUILD_INFO = {
122508
- version: optionalBuildString("0.10.0"),
122530
+ version: optionalBuildString("0.10.1"),
122509
122531
  channel: optionalBuildString(""),
122510
122532
  commit: optionalBuildString(""),
122511
122533
  buildTarget: optionalBuildString("darwin-arm64")
@@ -122991,7 +123013,7 @@ async function resolvePromptSession(harness, opts, workDir, defaultModel, stderr
122991
123013
  workDir
122992
123014
  }))[0];
122993
123015
  if (target === void 0) throw new Error(`未找到会话 "${opts.session}"。`);
122994
- if (target.workDir !== workDir) {
123016
+ if (!sameWorkDir(target.workDir, workDir)) {
122995
123017
  stderr.write(`${chalk.yellow(`会话 "${opts.session}" 是在其他目录下创建的。\n cd "${target.workDir}" && scream -r ${opts.session}`)}\n\n`);
122996
123018
  throw new Error(`会话 "${opts.session}" 是在其他目录下创建的。`);
122997
123019
  }
@@ -123380,6 +123402,23 @@ function formatTurnEndedFailure(event) {
123380
123402
  if (event.error !== void 0) return `${event.error.code}: ${event.error.message}`;
123381
123403
  return `提示回合结束,原因:${event.reason}`;
123382
123404
  }
123405
+ /**
123406
+ * Compare two working directories tolerating symlink aliases (/tmp vs
123407
+ * /private/tmp on macOS), trailing separators, and Windows case
123408
+ * differences. Falls back to resolved-string compare when either path
123409
+ * doesn't exist on disk.
123410
+ */
123411
+ function sameWorkDir(a, b) {
123412
+ const normalize = (p) => {
123413
+ let out = resolve(p);
123414
+ try {
123415
+ out = realpathSync(out);
123416
+ } catch {}
123417
+ out = out.replace(/[/\\]+$/, "");
123418
+ return process.platform === "win32" ? out.toLowerCase() : out;
123419
+ };
123420
+ return normalize(a) === normalize(b);
123421
+ }
123383
123422
  //#endregion
123384
123423
  //#region src/tui/constant/rendering.ts
123385
123424
  const MAX_SHELL_OUTPUT_BYTES = 128 * 1024;
@@ -127788,7 +127827,7 @@ async function createGoal(host, parsed) {
127788
127827
  await showGoalConfigWizard(host, session, parsed.objective, parsed.replace);
127789
127828
  }
127790
127829
  async function showGoalConfigWizard(host, session, objective, replace) {
127791
- const { TextInputDialogComponent } = await import("./text-input-dialog-Yyssn7t3.mjs");
127830
+ const { TextInputDialogComponent } = await import("./text-input-dialog-Dk5xzj4F.mjs");
127792
127831
  const turnInput = await promptNumber(host, TextInputDialogComponent, {
127793
127832
  title: t("goal.wizard_title", { objective }),
127794
127833
  subtitle: t("goal.budget_turns_hint"),
@@ -132341,35 +132380,39 @@ async function handleUpdateCommand(host) {
132341
132380
  * /mcp — MCP 服务器管理面板。
132342
132381
  *
132343
132382
  * 查看已安装的 MCP 服务器状态,一键安装推荐服务器。 */
132344
- const RECOMMENDED = [{
132345
- name: "peekaboo",
132346
- displayName: "Peekaboo",
132347
- description: t("mcp.desktop_desc"),
132348
- command: "npx",
132349
- args: [
132350
- "-y",
132351
- "@steipete/peekaboo",
132352
- "mcp"
132353
- ],
132354
- macOnly: true
132355
- }, {
132356
- name: "chrome-devtools",
132357
- displayName: "Chrome DevTools",
132358
- description: t("mcp.browser_desc"),
132359
- command: "npx",
132360
- args: [
132361
- "-y",
132362
- "chrome-devtools-mcp@latest",
132363
- "--no-usage-statistics"
132364
- ]
132365
- }];
132366
- const STATUS_LABELS = {
132367
- pending: t("mcp.connecting"),
132368
- connected: t("mcp.connected"),
132369
- failed: t("mcp.failed"),
132370
- disabled: t("mcp.disabled"),
132371
- "needs-auth": t("mcp.auth_required")
132372
- };
132383
+ function getRecommended() {
132384
+ return [{
132385
+ name: "peekaboo",
132386
+ displayName: "Peekaboo",
132387
+ description: t("mcp.desktop_desc"),
132388
+ command: "npx",
132389
+ args: [
132390
+ "-y",
132391
+ "@steipete/peekaboo",
132392
+ "mcp"
132393
+ ],
132394
+ macOnly: true
132395
+ }, {
132396
+ name: "chrome-devtools",
132397
+ displayName: "Chrome DevTools",
132398
+ description: t("mcp.browser_desc"),
132399
+ command: "npx",
132400
+ args: [
132401
+ "-y",
132402
+ "chrome-devtools-mcp@latest",
132403
+ "--no-usage-statistics"
132404
+ ]
132405
+ }];
132406
+ }
132407
+ function getStatusLabels() {
132408
+ return {
132409
+ pending: t("mcp.connecting"),
132410
+ connected: t("mcp.connected"),
132411
+ failed: t("mcp.failed"),
132412
+ disabled: t("mcp.disabled"),
132413
+ "needs-auth": t("mcp.auth_required")
132414
+ };
132415
+ }
132373
132416
  async function handleMcpCommand(host, _args) {
132374
132417
  if (!host.session) {
132375
132418
  host.showError(t("mcp.no_session"));
@@ -132438,7 +132481,7 @@ function buildRows(servers) {
132438
132481
  status: "__section"
132439
132482
  });
132440
132483
  for (const s of servers) {
132441
- const statusLabel = STATUS_LABELS[s.status] ?? s.status;
132484
+ const statusLabel = getStatusLabels()[s.status] ?? s.status;
132442
132485
  const toolInfo = s.status === "connected" ? `${s.toolCount} tools` : "";
132443
132486
  const errorInfo = s.error ? ` — ${sanitizeDesc(s.error)}` : "";
132444
132487
  rows.push({
@@ -132467,7 +132510,7 @@ function buildRows(servers) {
132467
132510
  label: "── " + t("mcp.recommended") + " ──",
132468
132511
  status: "__section"
132469
132512
  });
132470
- for (const rec of RECOMMENDED) {
132513
+ for (const rec of getRecommended()) {
132471
132514
  const alreadyInstalled = installedNames.has(rec.name);
132472
132515
  const platformUnavailable = rec.macOnly && process.platform !== "darwin";
132473
132516
  rows.push({
@@ -132486,7 +132529,7 @@ async function handleEnter(host, row) {
132486
132529
  host.showStatus(t("mcp.already_installed", { name: row.label }));
132487
132530
  return;
132488
132531
  }
132489
- const rec = RECOMMENDED.find((r) => r.name === row.name);
132532
+ const rec = getRecommended().find((r) => r.name === row.name);
132490
132533
  if (!rec) return;
132491
132534
  await installMcp(host, rec);
132492
132535
  } else if (row.kind === "installed" && row.status && row.status !== "__section" && row.status !== "__empty") if (row.status === "connected") await disableMcp(host, row.name);
@@ -136492,6 +136535,9 @@ out.join('\n');
136492
136535
  function isWaylandSession(env) {
136493
136536
  return Boolean(env["WAYLAND_DISPLAY"]) || env["XDG_SESSION_TYPE"] === "wayland";
136494
136537
  }
136538
+ function isX11Session(env) {
136539
+ return Boolean(env["DISPLAY"]);
136540
+ }
136495
136541
  function isWSL(env) {
136496
136542
  if (env["WSL_DISTRO_NAME"] !== void 0 || env["WSLENV"] !== void 0) return true;
136497
136543
  try {
@@ -136829,7 +136875,7 @@ async function readClipboardMedia(options) {
136829
136875
  if (platform === "linux") {
136830
136876
  const wayland = isWaylandSession(env);
136831
136877
  const wsl = isWSL(env);
136832
- if (wayland || wsl) {
136878
+ if (wayland || wsl || isX11Session(env)) {
136833
136879
  const fileMedia = readClipboardFileMediaViaWlPaste() ?? readClipboardFileMediaViaXclip();
136834
136880
  if (fileMedia !== null) return fileMedia;
136835
136881
  image = readClipboardImageViaWlPaste() ?? readClipboardImageViaXclip();
@@ -138122,9 +138168,9 @@ var SessionEventHandler = class {
138122
138168
  const { streamingUI } = this.host;
138123
138169
  const backgroundMeta = this.backgroundAgentMetadata.get(event.subagentId);
138124
138170
  if (backgroundMeta !== void 0) {
138171
+ const taskId = this.findAgentTaskId(event.subagentId);
138125
138172
  this.backgroundAgentMetadata.delete(event.subagentId);
138126
138173
  this.syncBackgroundAgentBadge();
138127
- const taskId = this.findAgentTaskId(event.subagentId);
138128
138174
  if (taskId !== void 0 && this.backgroundTaskTranscriptedTerminal.has(taskId)) return;
138129
138175
  if (taskId !== void 0) this.backgroundTaskTranscriptedTerminal.add(taskId);
138130
138176
  const extras = event.resultSummary === void 0 ? void 0 : { resultSummary: event.resultSummary };
@@ -138145,6 +138191,7 @@ var SessionEventHandler = class {
138145
138191
  const { streamingUI } = this.host;
138146
138192
  const backgroundMeta = this.backgroundAgentMetadata.get(event.subagentId);
138147
138193
  if (backgroundMeta !== void 0) {
138194
+ const taskId = this.findAgentTaskId(event.subagentId);
138148
138195
  this.backgroundAgentMetadata.delete(event.subagentId);
138149
138196
  this.syncBackgroundAgentBadge();
138150
138197
  streamingUI.applyBackgroundTaskTerminalStatus({
@@ -138153,7 +138200,6 @@ var SessionEventHandler = class {
138153
138200
  status: "failed",
138154
138201
  errorText: event.error
138155
138202
  });
138156
- const taskId = this.findAgentTaskId(event.subagentId);
138157
138203
  if (taskId !== void 0 && this.backgroundTaskTranscriptedTerminal.has(taskId)) return;
138158
138204
  if (taskId !== void 0) this.backgroundTaskTranscriptedTerminal.add(taskId);
138159
138205
  this.appendBackgroundAgentEntry("failed", backgroundMeta, { error: event.error });
@@ -138297,18 +138343,21 @@ var SessionEventHandler = class {
138297
138343
  };
138298
138344
  //#endregion
138299
138345
  //#region src/tui/utils/media-url.ts
138300
- const MEDIA_KIND_LABELS = {
138301
- audio: t("mediaurl.audio"),
138302
- image: t("mediaurl.image"),
138303
- video: t("mediaurl.video")
138304
- };
138346
+ function getMediaKindLabels() {
138347
+ return {
138348
+ audio: t("mediaurl.audio"),
138349
+ image: t("mediaurl.image"),
138350
+ video: t("mediaurl.video")
138351
+ };
138352
+ }
138305
138353
  function mediaUrlPartToText(kind, url) {
138354
+ const labels = getMediaKindLabels();
138306
138355
  const summary = summarizeDataUrl(url);
138307
138356
  if (summary !== void 0) {
138308
138357
  const size = summary.bytes !== void 0 ? `, ${formatByteSize(summary.bytes)}` : "";
138309
- return `[${MEDIA_KIND_LABELS[kind]} ${summary.mime}${size}]`;
138358
+ return `[${labels[kind]} ${summary.mime}${size}]`;
138310
138359
  }
138311
- return `<${MEDIA_KIND_LABELS[kind]} url="${escapeAttribute$1(url)}">`;
138360
+ return `<${labels[kind]} url="${escapeAttribute$1(url)}">`;
138312
138361
  }
138313
138362
  function summarizeDataUrl(url) {
138314
138363
  if (!url.startsWith("data:")) return void 0;
@@ -142194,7 +142243,7 @@ function hslToHex(h, s, l) {
142194
142243
  const toHex = (v) => Math.round((v + m) * 255).toString(16).padStart(2, "0");
142195
142244
  return `#${toHex(rf)}${toHex(gf)}${toHex(bf)}`;
142196
142245
  }
142197
- var InputController = class {
142246
+ var InputController = class InputController {
142198
142247
  host;
142199
142248
  lastHistoryContent;
142200
142249
  breatheTimer = null;
@@ -142215,12 +142264,38 @@ var InputController = class {
142215
142264
  };
142216
142265
  if (this.host.state.appState.planMode === "off") this.#startBreathing();
142217
142266
  }
142267
+ pendingLargeInput = null;
142268
+ static LARGE_INPUT_THRESHOLD = 5e3;
142218
142269
  handleInput(text) {
142270
+ if (this.pendingLargeInput !== null) {
142271
+ const answer = text.trim().toLowerCase();
142272
+ if (answer === "y" || answer === "yes") {
142273
+ const pending = this.pendingLargeInput;
142274
+ this.pendingLargeInput = null;
142275
+ this.persistInputHistory(pending);
142276
+ dispatchInput(this.host, pending);
142277
+ this.host.stopMemoryIdleTimer();
142278
+ } else {
142279
+ const pending = this.pendingLargeInput;
142280
+ this.pendingLargeInput = null;
142281
+ this.host.showStatus(t("input.large_cancelled"), this.host.state.theme.colors.textDim);
142282
+ if (answer !== "n" && answer !== "no" && text.trim().length > 0) {
142283
+ this.host.state.editor.setText(pending);
142284
+ this.handleInput(text);
142285
+ } else this.host.state.editor.setText(pending);
142286
+ }
142287
+ return;
142288
+ }
142219
142289
  if (text.trim().length === 0) return;
142220
142290
  if (this.host.state.appState.isReplaying) {
142221
142291
  this.host.showError(t("input.replay_blocked"));
142222
142292
  return;
142223
142293
  }
142294
+ if (text.length > InputController.LARGE_INPUT_THRESHOLD) {
142295
+ this.pendingLargeInput = text;
142296
+ this.host.showStatus(t("input.large_confirm", { count: text.length }), this.host.state.theme.colors.warning);
142297
+ return;
142298
+ }
142224
142299
  this.persistInputHistory(text);
142225
142300
  dispatchInput(this.host, text);
142226
142301
  this.host.stopMemoryIdleTimer();
@@ -144110,11 +144185,17 @@ var SessionManager = class {
144110
144185
  }
144111
144186
  if (isBusy(this.host.state.appState)) {
144112
144187
  this.host.showError(t("session.switch_streaming"));
144113
- return { switched: false };
144188
+ return {
144189
+ switched: false,
144190
+ blocked: true
144191
+ };
144114
144192
  }
144115
144193
  if (this.host.state.appState.isReplaying) {
144116
144194
  this.host.showError(t("session.switch_replaying"));
144117
- return { switched: false };
144195
+ return {
144196
+ switched: false,
144197
+ blocked: true
144198
+ };
144118
144199
  }
144119
144200
  let session;
144120
144201
  try {
@@ -146025,12 +146106,12 @@ var DialogManager = class {
146025
146106
  const row = this.host.getSessions().find((s) => s.id === pickerId);
146026
146107
  const isCc = row?.metadata?.["source"] === "cc-connect";
146027
146108
  const realId = isCc ? row.metadata["agentSessionId"] : pickerId;
146028
- this.host.resumeSession(realId).then(async (switched) => {
146109
+ this.host.resumeSession(realId).then(async ({ switched, blocked }) => {
146029
146110
  if (switched) {
146030
146111
  this.hideSessionPicker();
146031
146112
  return;
146032
146113
  }
146033
- if (isCc) try {
146114
+ if (isCc && blocked !== true) try {
146034
146115
  const session = await this.host.harness.createSession({
146035
146116
  id: realId,
146036
146117
  workDir: this.host.getCurrentWorkDir(),
@@ -146044,6 +146125,8 @@ var DialogManager = class {
146044
146125
  } catch {
146045
146126
  this.host.showError(t("dialog.create_session_failed"));
146046
146127
  }
146128
+ }).catch((error) => {
146129
+ this.host.showError(error instanceof Error ? error.message : String(error));
146047
146130
  });
146048
146131
  },
146049
146132
  onCancel,
@@ -146056,6 +146139,8 @@ var DialogManager = class {
146056
146139
  await this.host.fetchSessions();
146057
146140
  if (this.host.getSessions().length === 0) this.hideSessionPicker();
146058
146141
  else if (this.host.state.activeDialog === "session-picker") this.mountSessionPicker(onCancel);
146142
+ }).catch((error) => {
146143
+ this.host.showError(error instanceof Error ? error.message : String(error));
146059
146144
  });
146060
146145
  }
146061
146146
  }));
@@ -146630,7 +146715,11 @@ var ScreamTUI = class {
146630
146715
  this.sessionManager.resetSessionRuntime();
146631
146716
  }
146632
146717
  async resumeSession(targetSessionId) {
146633
- return (await this.sessionManager.resumeSession(targetSessionId)).switched;
146718
+ const result = await this.sessionManager.resumeSession(targetSessionId);
146719
+ return {
146720
+ switched: result.switched,
146721
+ blocked: result.blocked
146722
+ };
146634
146723
  }
146635
146724
  async switchToSession(session, statusMessage) {
146636
146725
  await this.sessionManager.switchToSession(session, statusMessage);
@@ -146775,6 +146864,8 @@ const SHADOW_CHARS = new Set([
146775
146864
  const SHEEN_STEP = 4;
146776
146865
  const SHEEN_INTERVAL_MS = 60;
146777
146866
  const LOADING_DURATION_MS = 1500;
146867
+ const FULL_LOGO_MIN_COLS = 87;
146868
+ const COMPACT_LOGO = ["██▄▄▄██", "▐█▄▀▄█▌"];
146778
146869
  const THEME_PRIMARY = {
146779
146870
  dark: [
146780
146871
  204,
@@ -146963,7 +147054,9 @@ function supportsAnsi() {
146963
147054
  }
146964
147055
  function runLoadingAnimation(theme = "dark") {
146965
147056
  if (!supportsAnsi()) {
146966
- for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
147057
+ const { cols } = getTerminalSize();
147058
+ if (cols >= FULL_LOGO_MIN_COLS) for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
147059
+ else for (const line of COMPACT_LOGO) stdout.write(`${fg(...BLOCK_RGB)}${line}${RESET}\n`);
146967
147060
  stdout.write(`${BOLD}${fg(...THEME_PRIMARY[theme])}${t("loading.waking")}${RESET}\n`);
146968
147061
  return Promise.resolve();
146969
147062
  }
@@ -146981,15 +147074,20 @@ function runLoadingAnimation(theme = "dark") {
146981
147074
  function render() {
146982
147075
  const { cols, rows } = getTerminalSize();
146983
147076
  const lines = [];
146984
- const contentHeight = LOGO.length + 5;
147077
+ const useFullLogo = cols >= FULL_LOGO_MIN_COLS;
147078
+ const contentHeight = useFullLogo ? LOGO.length + 5 : COMPACT_LOGO.length + 5;
146985
147079
  const topPad = Math.max(0, Math.floor((rows - contentHeight) / 2));
146986
147080
  for (let i = 0; i < topPad; i++) lines.push("");
146987
147081
  const breatheColor = breathePalette[breatheFrame] ?? primary;
146988
- for (const line of LOGO) {
147082
+ if (useFullLogo) for (const line of LOGO) {
146989
147083
  let colored = "";
146990
147084
  for (let ci = 0; ci < line.length; ci++) colored += renderSheen(line[ci], ci, sheenPos, isReversing, breatheColor);
146991
147085
  lines.push(centerPad(colored, cols));
146992
147086
  }
147087
+ else for (const line of COMPACT_LOGO) {
147088
+ const colored = `${fg(...BLOCK_RGB)}${line}${RESET}`;
147089
+ lines.push(centerPad(colored, cols));
147090
+ }
146993
147091
  lines.push("");
146994
147092
  if (phase === "loading") lines.push(centerPad(renderShimmer(shimmerPulse, breatheColor), cols));
146995
147093
  else lines.push(centerPad(`${BOLD}${fg(...breatheColor)}${t("loading.press_enter")}${RESET}`, cols));
@@ -147046,7 +147144,9 @@ function runLoadingAnimation(theme = "dark") {
147046
147144
  process$1.off("SIGTERM", interrupt);
147047
147145
  stdout.write("\x1B[?25h");
147048
147146
  if (process$1.platform !== "win32") stdout.write("\x1B[?1049l");
147049
- for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
147147
+ const { cols: fallbackCols } = getTerminalSize();
147148
+ if (fallbackCols >= FULL_LOGO_MIN_COLS) for (const line of LOGO) stdout.write(`${fg(...LOGO_RGB)}${line}${RESET}\n`);
147149
+ else for (const line of COMPACT_LOGO) stdout.write(`${fg(...BLOCK_RGB)}${line}${RESET}\n`);
147050
147150
  stdout.write(`${BOLD}${fg(...primary)}${t("loading.waking")}${RESET}\n`);
147051
147151
  resolve();
147052
147152
  return;
@@ -147700,6 +147800,27 @@ async function runStreamJson(opts) {
147700
147800
  hasAppendPrompt: appendPrompt.length > 0
147701
147801
  });
147702
147802
  }
147803
+ let cleaned = false;
147804
+ const runCleanup = async () => {
147805
+ if (cleaned) return;
147806
+ cleaned = true;
147807
+ for (const [, pending] of pendingApprovals) pending.resolve({
147808
+ decision: "rejected",
147809
+ feedback: "会话已结束"
147810
+ });
147811
+ pendingApprovals.clear();
147812
+ if (currentSessionId) writer.emitResumeHint(sessionKey);
147813
+ try {
147814
+ if (session) await session.close();
147815
+ await harness.close();
147816
+ } catch {}
147817
+ if (injectedAgentsMd) try {
147818
+ if (originalAgentsMd === void 0) {
147819
+ if (existsSync(agentsMdPath)) unlinkSync(agentsMdPath);
147820
+ } else writeFileSync(agentsMdPath, originalAgentsMd, "utf-8");
147821
+ } catch {}
147822
+ };
147823
+ const uninstallTerminationHandlers = installStreamJsonTerminationHandlers(runCleanup);
147703
147824
  try {
147704
147825
  await harness.ensureConfigFile();
147705
147826
  const config = await harness.getConfig();
@@ -147934,7 +148055,7 @@ async function runStreamJson(opts) {
147934
148055
  });
147935
148056
  session.prompt(userText).catch((error) => {
147936
148057
  const msg = error instanceof Error ? error.message : String(error);
147937
- if (msg.includes("insufficient tool messages") || msg.includes("tool_calls")) {
148058
+ if (msg.includes("insufficient tool messages") || msg.includes("tool_calls") && msg.includes("followed by tool messages")) {
147938
148059
  log.warn("stream-json: resetting session after tool call mismatch", {
147939
148060
  sessionId: session?.id,
147940
148061
  error: msg
@@ -147952,7 +148073,9 @@ async function runStreamJson(opts) {
147952
148073
  }
147953
148074
  finish(error instanceof Error ? error : new Error(msg));
147954
148075
  });
147955
- await turnPromise;
148076
+ try {
148077
+ await turnPromise;
148078
+ } catch {}
147956
148079
  }
147957
148080
  } catch (error) {
147958
148081
  if (process.exitCode === void 0) {
@@ -147961,23 +148084,35 @@ async function runStreamJson(opts) {
147961
148084
  process.exitCode = 1;
147962
148085
  } else log.error("stream-json: turn error (exitCode already set)", { error });
147963
148086
  } finally {
147964
- for (const [, pending] of pendingApprovals) pending.resolve({
147965
- decision: "rejected",
147966
- feedback: "会话已结束"
147967
- });
147968
- pendingApprovals.clear();
147969
- if (currentSessionId) writer.emitResumeHint(sessionKey);
147970
- try {
147971
- if (session) await session.close();
147972
- await harness.close();
147973
- } catch {}
147974
- if (injectedAgentsMd) try {
147975
- if (originalAgentsMd === void 0) {
147976
- if (existsSync(agentsMdPath)) unlinkSync(agentsMdPath);
147977
- } else writeFileSync(agentsMdPath, originalAgentsMd, "utf-8");
147978
- } catch {}
148087
+ uninstallTerminationHandlers();
148088
+ await runCleanup();
147979
148089
  }
147980
148090
  }
148091
+ /** Install termination handlers so we can clean up on SIGINT / SIGTERM. */
148092
+ function installStreamJsonTerminationHandlers(cleanup) {
148093
+ let terminating = false;
148094
+ const handler = async (signal) => {
148095
+ if (terminating) return;
148096
+ terminating = true;
148097
+ try {
148098
+ await cleanup();
148099
+ } finally {
148100
+ process.exit(signal === "SIGINT" ? 130 : 143);
148101
+ }
148102
+ };
148103
+ const onSigint = () => {
148104
+ handler("SIGINT");
148105
+ };
148106
+ const onSigterm = () => {
148107
+ handler("SIGTERM");
148108
+ };
148109
+ process.once("SIGINT", onSigint);
148110
+ process.once("SIGTERM", onSigterm);
148111
+ return () => {
148112
+ process.off("SIGINT", onSigint);
148113
+ process.off("SIGTERM", onSigterm);
148114
+ };
148115
+ }
147981
148116
  //#endregion
147982
148117
  //#region src/cli/startup-error.ts
147983
148118
  function formatUnknownErrorMessage(error) {
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-Bda9iKUb.mjs")).main();
9
+ (await import("./app-DVRLAZ8b.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 { n as multiSearchWithTrace } from "./src-1LHYB0xM.mjs";
6
+ import { n as multiSearchWithTrace } from "./src-oeUnRY3N.mjs";
7
7
  export { multiSearchWithTrace };
@@ -214,7 +214,10 @@ var KnowledgeStore = class {
214
214
  }
215
215
  async init() {
216
216
  if (this.initPromise) return this.initPromise;
217
- this.initPromise = this._doInit();
217
+ this.initPromise = this._doInit().catch((error) => {
218
+ this.initPromise = null;
219
+ throw error;
220
+ });
218
221
  return this.initPromise;
219
222
  }
220
223
  async _doInit() {
@@ -225,8 +228,32 @@ var KnowledgeStore = class {
225
228
  this.db.exec("PRAGMA foreign_keys = ON;");
226
229
  this.db.exec("PRAGMA busy_timeout = 5000;");
227
230
  this.createSchema();
231
+ this.migrateFtsTokenization();
228
232
  this.initialized = true;
229
233
  }
234
+ /**
235
+ * One-time migration: FTS rows written before toFtsText pre-tokenization
236
+ * indexed raw text (CJK runs became single tokens and are unsearchable).
237
+ * Rebuild all three FTS tables from the content tables with toFtsText.
238
+ * Tracked via PRAGMA user_version so it runs exactly once.
239
+ */
240
+ migrateFtsTokenization() {
241
+ if (this.db === void 0) return;
242
+ if (this.db.prepare("PRAGMA user_version").get().user_version >= 1) return;
243
+ this.db.exec("DELETE FROM knowledge_chunks_fts");
244
+ this.db.exec("DELETE FROM knowledge_events_fts");
245
+ this.db.exec("DELETE FROM knowledge_entities_fts");
246
+ const chunks = this.db.prepare("SELECT rowid, heading, content FROM knowledge_chunks").all();
247
+ const insChunk = this.db.prepare("INSERT INTO knowledge_chunks_fts (rowid, heading, content) VALUES (?, ?, ?)");
248
+ for (const c of chunks) insChunk.run(c.rowid, toFtsText(c.heading ?? ""), toFtsText(c.content));
249
+ const events = this.db.prepare("SELECT rowid, title, summary, content FROM knowledge_events").all();
250
+ const insEvent = this.db.prepare("INSERT INTO knowledge_events_fts (rowid, title, summary, content) VALUES (?, ?, ?, ?)");
251
+ for (const e of events) insEvent.run(e.rowid, toFtsText(e.title), toFtsText(e.summary ?? ""), toFtsText(e.content));
252
+ const entities = this.db.prepare("SELECT rowid, name, description FROM knowledge_entities").all();
253
+ const insEntity = this.db.prepare("INSERT INTO knowledge_entities_fts (rowid, name, description) VALUES (?, ?, ?)");
254
+ for (const e of entities) insEntity.run(e.rowid, toFtsText(e.name), toFtsText(e.description ?? ""));
255
+ this.db.exec("PRAGMA user_version = 1");
256
+ }
230
257
  close() {
231
258
  if (this.db !== void 0) {
232
259
  this.db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
@@ -392,7 +419,19 @@ var KnowledgeStore = class {
392
419
  async deleteSource(id) {
393
420
  await this.init();
394
421
  if (this.db === void 0) return false;
395
- return this.db.prepare("DELETE FROM knowledge_sources WHERE id = ?").run(id).changes > 0;
422
+ const chunkRows = this.db.prepare("SELECT rowid, heading, content FROM knowledge_chunks WHERE source_id = ?").all(id);
423
+ const eventRows = this.db.prepare("SELECT rowid, title, summary, content FROM knowledge_events WHERE source_id = ?").all(id);
424
+ const entityRows = this.db.prepare("SELECT rowid, name, description FROM knowledge_entities WHERE source_id = ?").all(id);
425
+ const result = this.db.prepare("DELETE FROM knowledge_sources WHERE id = ?").run(id);
426
+ if (result.changes > 0) {
427
+ const delChunk = this.db.prepare("INSERT INTO knowledge_chunks_fts (knowledge_chunks_fts, rowid, heading, content) VALUES ('delete', ?, ?, ?)");
428
+ for (const row of chunkRows) delChunk.run(row.rowid, toFtsText(row.heading ?? ""), toFtsText(row.content));
429
+ const delEvent = this.db.prepare("INSERT INTO knowledge_events_fts (knowledge_events_fts, rowid, title, summary, content) VALUES ('delete', ?, ?, ?, ?)");
430
+ for (const row of eventRows) delEvent.run(row.rowid, toFtsText(row.title), toFtsText(row.summary ?? ""), toFtsText(row.content));
431
+ const delEntity = this.db.prepare("INSERT INTO knowledge_entities_fts (knowledge_entities_fts, rowid, name, description) VALUES ('delete', ?, ?, ?)");
432
+ for (const row of entityRows) delEntity.run(row.rowid, toFtsText(row.name), toFtsText(row.description ?? ""));
433
+ }
434
+ return result.changes > 0;
396
435
  }
397
436
  async createDocument(params) {
398
437
  if (this.db === void 0) throw new Error("knowledge store not initialized");
@@ -440,7 +479,7 @@ var KnowledgeStore = class {
440
479
  (id, source_id, document_id, rank, heading, content, raw_content, embedding_json, created_at)
441
480
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.sourceId, params.documentId, params.rank, params.heading, params.content, params.rawContent, embeddingJson, createdAt);
442
481
  this.db.prepare(`INSERT INTO knowledge_chunks_fts (rowid, heading, content)
443
- VALUES ((SELECT rowid FROM knowledge_chunks WHERE id = ?), ?, ?)`).run(id, params.heading ?? "", params.content);
482
+ VALUES ((SELECT rowid FROM knowledge_chunks WHERE id = ?), ?, ?)`).run(id, toFtsText(params.heading ?? ""), toFtsText(params.content));
444
483
  return {
445
484
  id,
446
485
  sourceId: params.sourceId,
@@ -488,7 +527,7 @@ var KnowledgeStore = class {
488
527
  title_embedding_json, content_embedding_json, created_at)
489
528
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.sourceId, params.documentId, params.chunkId, params.rank, params.title, params.summary, params.content, params.category, JSON.stringify(params.keywords), vectorToJson(params.titleEmbedding), vectorToJson(params.contentEmbedding), createdAt);
490
529
  this.db.prepare(`INSERT INTO knowledge_events_fts (rowid, title, summary, content)
491
- VALUES ((SELECT rowid FROM knowledge_events WHERE id = ?), ?, ?, ?)`).run(id, params.title, params.summary ?? "", params.content);
530
+ VALUES ((SELECT rowid FROM knowledge_events WHERE id = ?), ?, ?, ?)`).run(id, toFtsText(params.title), toFtsText(params.summary ?? ""), toFtsText(params.content));
492
531
  return {
493
532
  id,
494
533
  sourceId: params.sourceId,
@@ -551,8 +590,24 @@ var KnowledgeStore = class {
551
590
  const normalizedName = normalizeName(params.name);
552
591
  const existing = this.db.prepare("SELECT * FROM knowledge_entities WHERE source_id = ? AND type = ? AND normalized_name = ?").get(params.sourceId, params.type, normalizedName);
553
592
  if (existing !== void 0) {
554
- if (params.description !== null && existing["description"] === null) this.db.prepare("UPDATE knowledge_entities SET description = ? WHERE id = ?").run(params.description, String(existing["id"]));
555
- if (params.embedding !== null && existing["embedding_json"] === null) this.db.prepare("UPDATE knowledge_entities SET embedding_json = ? WHERE id = ?").run(vectorToJson(params.embedding), String(existing["id"]));
593
+ const existingId = String(existing["id"]);
594
+ let descriptionChanged = false;
595
+ if (params.description !== null && existing["description"] === null) {
596
+ this.db.prepare("UPDATE knowledge_entities SET description = ? WHERE id = ?").run(params.description, existingId);
597
+ existing["description"] = params.description;
598
+ descriptionChanged = true;
599
+ }
600
+ if (params.embedding !== null && existing["embedding_json"] === null) {
601
+ this.db.prepare("UPDATE knowledge_entities SET embedding_json = ? WHERE id = ?").run(vectorToJson(params.embedding), existingId);
602
+ existing["embedding_json"] = vectorToJson(params.embedding);
603
+ }
604
+ if (descriptionChanged) {
605
+ const ftsRow = this.db.prepare("SELECT rowid, name, description FROM knowledge_entities WHERE id = ?").get(existingId);
606
+ if (ftsRow !== void 0) {
607
+ this.db.prepare("INSERT INTO knowledge_entities_fts (knowledge_entities_fts, rowid, name, description) VALUES ('delete', ?, ?, ?)").run(ftsRow.rowid, toFtsText(ftsRow.name), "");
608
+ this.db.prepare(`INSERT INTO knowledge_entities_fts (rowid, name, description) VALUES (?, ?, ?)`).run(ftsRow.rowid, toFtsText(ftsRow.name), toFtsText(ftsRow.description ?? ""));
609
+ }
610
+ }
556
611
  return rowToEntity(existing);
557
612
  }
558
613
  const id = generateId("ent");
@@ -561,7 +616,7 @@ var KnowledgeStore = class {
561
616
  (id, source_id, type, name, normalized_name, description, embedding_json, created_at)
562
617
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(id, params.sourceId, params.type, params.name, normalizedName, params.description, vectorToJson(params.embedding), createdAt);
563
618
  this.db.prepare(`INSERT INTO knowledge_entities_fts (rowid, name, description)
564
- VALUES ((SELECT rowid FROM knowledge_entities WHERE id = ?), ?, ?)`).run(id, params.name, params.description ?? "");
619
+ VALUES ((SELECT rowid FROM knowledge_entities WHERE id = ?), ?, ?)`).run(id, toFtsText(params.name), toFtsText(params.description ?? ""));
565
620
  return {
566
621
  id,
567
622
  sourceId: params.sourceId,
@@ -209,6 +209,8 @@ const dictionaries = {
209
209
  "input.replay_blocked": "会话历史正在回放时无法发送输入。",
210
210
  "input.send_failed": "发送失败:{message}",
211
211
  "input.guide_failed": "引导失败:{message}",
212
+ "input.large_confirm": "输入内容为 {count} 字符,确认发送?(y/n)",
213
+ "input.large_cancelled": "输入已取消。",
212
214
  "welcome.config": "/config 配置模型",
213
215
  "welcome.sessions": "/sessions 恢复历史会话",
214
216
  "welcome.quick_menu": "/ 输入后打开快捷菜单",
@@ -1241,6 +1243,8 @@ const dictionaries = {
1241
1243
  "input.replay_blocked": "Cannot send input while session history is replaying.",
1242
1244
  "input.send_failed": "Send failed: {message}",
1243
1245
  "input.guide_failed": "Guide failed: {message}",
1246
+ "input.large_confirm": "Input is {count} characters. Send anyway? (y/n)",
1247
+ "input.large_cancelled": "Input cancelled.",
1244
1248
  "welcome.config": "/config Configure model",
1245
1249
  "welcome.sessions": "/sessions Resume session",
1246
1250
  "welcome.quick_menu": "/ Open quick menu",
@@ -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-os5jWxGT.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-D_xK0DJH.mjs";
7
7
  export { TextInputDialogComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -57,7 +57,7 @@
57
57
  "smoke": "node dist/main.mjs --version"
58
58
  },
59
59
  "dependencies": {
60
- "@liutod-scream/pi-tui": "^0.80.10",
60
+ "@liutod-scream/pi-tui": "^0.80.13",
61
61
  "@mariozechner/clipboard": "^0.3.2",
62
62
  "chalk": "^5.4.1",
63
63
  "cli-highlight": "^2.1.11",