scream-code 0.17.8 → 0.17.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.
@@ -20,8 +20,8 @@
20
20
  }
21
21
  })();
22
22
  </script>
23
- <script type="module" crossorigin src="./assets/index-CKa_o5qA.js"></script>
24
- <link rel="stylesheet" crossorigin href="./assets/index-CVXe8l3P.css">
23
+ <script type="module" crossorigin src="./assets/index-BTL8OnOo.js"></script>
24
+ <link rel="stylesheet" crossorigin href="./assets/index-B1g39y1G.css">
25
25
  </head>
26
26
  <body>
27
27
  <div id="app"></div>
@@ -721,6 +721,8 @@ async function ensureFastembedModelSidecars(model, cacheDir) {
721
721
  const FILE_NAME = "entries.jsonl";
722
722
  const MIGRATION_MARKER = ".migrated";
723
723
  const SQLITE_MIGRATION_MARKER = ".migrated-to-sqlite";
724
+ /** Page size for read() row batches — bounds peak memory on large stores. */
725
+ const READ_BATCH_SIZE = 500;
724
726
  var MemoryMemoStore = class MemoryMemoStore {
725
727
  projectDir;
726
728
  jsonlPath;
@@ -793,9 +795,21 @@ var MemoryMemoStore = class MemoryMemoStore {
793
795
  await this.init();
794
796
  if (this.db === void 0) return;
795
797
  const projectDir = options?.projectDir;
796
- const stmt = projectDir === void 0 ? this.db.prepare("SELECT * FROM memos ORDER BY recorded_at DESC") : this.db.prepare("SELECT * FROM memos WHERE project_dir = ? OR project_dir = '' ORDER BY recorded_at DESC");
797
- const rows = projectDir === void 0 ? stmt.all() : stmt.all(projectDir);
798
- for (const row of rows) yield rowToMemo(row);
798
+ const firstPage = projectDir === void 0 ? this.db.prepare("SELECT rowid AS row_id, * FROM memos ORDER BY recorded_at DESC, rowid DESC LIMIT ?") : this.db.prepare("SELECT rowid AS row_id, * FROM memos WHERE project_dir = ? OR project_dir = '' ORDER BY recorded_at DESC, rowid DESC LIMIT ?");
799
+ const nextPage = projectDir === void 0 ? this.db.prepare("SELECT rowid AS row_id, * FROM memos WHERE recorded_at < ?1 OR (recorded_at = ?1 AND rowid < ?2) ORDER BY recorded_at DESC, rowid DESC LIMIT ?3") : this.db.prepare("SELECT rowid AS row_id, * FROM memos WHERE (project_dir = ?1 OR project_dir = '') AND (recorded_at < ?2 OR (recorded_at = ?2 AND rowid < ?3)) ORDER BY recorded_at DESC, rowid DESC LIMIT ?4");
800
+ let cursor;
801
+ for (;;) {
802
+ let rows;
803
+ if (cursor === void 0) rows = projectDir === void 0 ? firstPage.all(READ_BATCH_SIZE) : firstPage.all(projectDir, READ_BATCH_SIZE);
804
+ else rows = projectDir === void 0 ? nextPage.all(cursor.recordedAt, cursor.rowId, READ_BATCH_SIZE) : nextPage.all(projectDir, cursor.recordedAt, cursor.rowId, READ_BATCH_SIZE);
805
+ for (const row of rows) yield rowToMemo(row);
806
+ if (rows.length < READ_BATCH_SIZE) return;
807
+ const lastRow = rows.at(-1);
808
+ cursor = {
809
+ recordedAt: Number(lastRow["recorded_at"]),
810
+ rowId: Number(lastRow["row_id"])
811
+ };
812
+ }
799
813
  }
800
814
  /** Append a memo. */
801
815
  async append(entry) {
@@ -882,56 +896,60 @@ var MemoryMemoStore = class MemoryMemoStore {
882
896
  */
883
897
  static async migrateLegacyStores(screamHomeDir) {
884
898
  const target = new MemoryMemoStore(screamHomeDir);
885
- const markerPath = join$1(screamHomeDir, "memory", MIGRATION_MARKER);
886
- try {
887
- await stat(markerPath);
888
- return;
889
- } catch {}
890
- const sessionsDir = join$1(screamHomeDir, "sessions");
891
- let sessionEntries;
892
899
  try {
893
- sessionEntries = await readdir(sessionsDir, { withFileTypes: true }).then((entries) => entries.filter((e) => e.isDirectory()).map((e) => e.name));
894
- } catch {
895
- await writeFile(markerPath, "", "utf8").catch(() => {});
896
- return;
897
- }
898
- const migratedIds = /* @__PURE__ */ new Set();
899
- for await (const memo of target.read()) migratedIds.add(memo.id);
900
- let migratedCount = 0;
901
- const legacyPaths = [];
902
- for (const sessionKey of sessionEntries) {
903
- const legacyPath = join$1(sessionsDir, sessionKey, "memory", FILE_NAME);
904
- let stream;
900
+ const markerPath = join$1(screamHomeDir, "memory", MIGRATION_MARKER);
905
901
  try {
906
- stream = createReadStream(legacyPath, { encoding: "utf8" });
902
+ await stat(markerPath);
903
+ return;
907
904
  } catch {}
908
- stream.on("error", () => {});
909
- let line = "";
905
+ const sessionsDir = join$1(screamHomeDir, "sessions");
906
+ let sessionEntries;
910
907
  try {
911
- for await (const chunk of stream) {
912
- line += chunk;
913
- let newlineIndex = line.indexOf("\n");
914
- while (newlineIndex !== -1) {
915
- const rawLine = line.slice(0, newlineIndex).replace(/\r$/, "");
916
- line = line.slice(newlineIndex + 1);
917
- newlineIndex = line.indexOf("\n");
918
- const memo = target.parseLine(rawLine, 0);
919
- if (memo === void 0 || migratedIds.has(memo.id)) continue;
920
- await target.append(memo);
921
- migratedIds.add(memo.id);
922
- migratedCount++;
908
+ sessionEntries = await readdir(sessionsDir, { withFileTypes: true }).then((entries) => entries.filter((e) => e.isDirectory()).map((e) => e.name));
909
+ } catch {
910
+ await writeFile(markerPath, "", "utf8").catch(() => {});
911
+ return;
912
+ }
913
+ const migratedIds = /* @__PURE__ */ new Set();
914
+ for await (const memo of target.read()) migratedIds.add(memo.id);
915
+ let migratedCount = 0;
916
+ const legacyPaths = [];
917
+ for (const sessionKey of sessionEntries) {
918
+ const legacyPath = join$1(sessionsDir, sessionKey, "memory", FILE_NAME);
919
+ let stream;
920
+ try {
921
+ stream = createReadStream(legacyPath, { encoding: "utf8" });
922
+ } catch {}
923
+ stream.on("error", () => {});
924
+ let line = "";
925
+ try {
926
+ for await (const chunk of stream) {
927
+ line += chunk;
928
+ let newlineIndex = line.indexOf("\n");
929
+ while (newlineIndex !== -1) {
930
+ const rawLine = line.slice(0, newlineIndex).replace(/\r$/, "");
931
+ line = line.slice(newlineIndex + 1);
932
+ newlineIndex = line.indexOf("\n");
933
+ const memo = target.parseLine(rawLine, 0);
934
+ if (memo === void 0 || migratedIds.has(memo.id)) continue;
935
+ await target.append(memo);
936
+ migratedIds.add(memo.id);
937
+ migratedCount++;
938
+ }
923
939
  }
940
+ } catch {
941
+ continue;
924
942
  }
925
- } catch {
926
- continue;
943
+ legacyPaths.push(legacyPath);
927
944
  }
928
- legacyPaths.push(legacyPath);
929
- }
930
- for (const legacyPath of legacyPaths) {
931
- await unlink(legacyPath).catch(() => {});
932
- await rmdir(dirname$1(legacyPath)).catch(() => {});
945
+ for (const legacyPath of legacyPaths) {
946
+ await unlink(legacyPath).catch(() => {});
947
+ await rmdir(dirname$1(legacyPath)).catch(() => {});
948
+ }
949
+ await writeFile(markerPath, `${migratedCount}\n`, "utf8").catch(() => {});
950
+ } finally {
951
+ target.close();
933
952
  }
934
- await writeFile(markerPath, `${migratedCount}\n`, "utf8").catch(() => {});
935
953
  }
936
954
  /** @internal */
937
955
  parseLine(rawLine, _lineNumber) {
@@ -1440,6 +1458,22 @@ function buildFtsQuery$1(search) {
1440
1458
  if (tokens.length === 0) return void 0;
1441
1459
  return tokens.map((t) => `"${t.replaceAll("\"", "\"\"")}"`).join(" AND ");
1442
1460
  }
1461
+ const sharedStores = /* @__PURE__ */ new Map();
1462
+ function entryFor$1(projectDir, log) {
1463
+ const existing = sharedStores.get(projectDir);
1464
+ if (existing !== void 0) return existing;
1465
+ const store = new MemoryMemoStore(projectDir, log);
1466
+ sharedStores.set(projectDir, store);
1467
+ return store;
1468
+ }
1469
+ /**
1470
+ * Shared MemoryMemoStore for a home directory. The map key is the raw
1471
+ * projectDir string so every caller passing the same path gets the same
1472
+ * handle. `init()` is idempotent and stays the caller's job to await.
1473
+ */
1474
+ function sharedMemoStore(projectDir, log) {
1475
+ return entryFor$1(projectDir, log);
1476
+ }
1443
1477
  //#endregion
1444
1478
  //#region ../../packages/memory/src/extractor.ts
1445
1479
  /**
@@ -4138,4 +4172,4 @@ async function chunkVectorFallback(store, queryVec, topK) {
4138
4172
  return results;
4139
4173
  }
4140
4174
  //#endregion
4141
- export { classifyEmbeddingFailure as A, rankMemos as B, applyConsolidation as C, parseMemoryMemos as D, buildExitExtractionPrompt as E, createMemoryMemo as F, normalize as G, dirname$1 as H, toSummary as I, resolve$1 as J, parse as K, generateTags as L, createFastEmbedEngine as M, hasEmbeddingModelCache as N, stripMemoryTags as O, probeLocalEmbeddingSupport as P, normalizeTags as R, DreamTracker as S, EXIT_EXTRACTION_SYSTEM_PROMPT as T, isAbsolute$1 as U, basename$1 as V, join$1 as W, splitLargeSection as _, isSupportedFile as a, sharedKnowledgeStoreReady as b, buildExtractionUserPrompt as c, extractQueryEntities as d, parseExtractionResponse as f, estimateTokens as g, chunkText as h, ingestFile as i, clearEmbeddingModelCache as j, MemoryMemoStore as k, extractEventFromChunk as l, chunkMarkdown as m, multiSearchWithTrace as n, ENTITY_TYPES as o, rerankEventsWithLlm as p, relative$1 as q, ingestDirectory as r, EXTRACTION_SYSTEM_PROMPT as s, multiSearch as t, extractJsonFromText as u, stripMarkdown as v, buildConsolidationPlan as w, KnowledgeStore as x, sharedKnowledgeStore as y, buildProjectTagCloud as z };
4175
+ export { sharedMemoStore as A, buildProjectTagCloud as B, applyConsolidation as C, parseMemoryMemos as D, buildExitExtractionPrompt as E, probeLocalEmbeddingSupport as F, join$1 as G, basename$1 as H, createMemoryMemo as I, relative$1 as J, normalize as K, toSummary as L, clearEmbeddingModelCache as M, createFastEmbedEngine as N, stripMemoryTags as O, hasEmbeddingModelCache as P, generateTags as R, DreamTracker as S, EXIT_EXTRACTION_SYSTEM_PROMPT as T, dirname$1 as U, rankMemos as V, isAbsolute$1 as W, resolve$1 as Y, splitLargeSection as _, isSupportedFile as a, sharedKnowledgeStoreReady as b, buildExtractionUserPrompt as c, extractQueryEntities as d, parseExtractionResponse as f, estimateTokens as g, chunkText as h, ingestFile as i, classifyEmbeddingFailure as j, MemoryMemoStore as k, extractEventFromChunk as l, chunkMarkdown as m, multiSearchWithTrace as n, ENTITY_TYPES as o, rerankEventsWithLlm as p, parse as q, ingestDirectory as r, EXTRACTION_SYSTEM_PROMPT as s, multiSearch as t, extractJsonFromText as u, stripMarkdown as v, buildConsolidationPlan as w, KnowledgeStore as x, sharedKnowledgeStore as y, normalizeTags as z };
@@ -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-BhZujafw.mjs";
6
+ import { n as multiSearchWithTrace } from "./src-C7C9e5Ds.mjs";
7
7
  export { multiSearchWithTrace };
@@ -833,6 +833,25 @@ const dictionaries = {
833
833
  "taskbrowser.timed_out": "已超时:",
834
834
  "taskbrowser.yes": "是",
835
835
  "taskbrowser.awaiting_label": "等待中:",
836
+ "taskbrowser.agents": "子代理",
837
+ "taskbrowser.agents_title": "子代理",
838
+ "taskbrowser.agents_live": "运行中",
839
+ "taskbrowser.agents_ended": "已结束",
840
+ "taskbrowser.agents_completed": "已完成",
841
+ "taskbrowser.agents_failed": "已失败",
842
+ "taskbrowser.agents_empty": "本会话无活动子代理。",
843
+ "taskbrowser.agents_no_output": "子代理输出显示在对话卡片中 — 此处无实时输出。",
844
+ "taskbrowser.agent_detail": "代理详情",
845
+ "taskbrowser.agent_type": "类型:",
846
+ "taskbrowser.agent_source": "来源:",
847
+ "taskbrowser.agent_chain": "父链:",
848
+ "taskbrowser.agent_activity": "最新活动:",
849
+ "taskbrowser.agent_instance": "实例:",
850
+ "taskbrowser.agent_main": "主代理",
851
+ "taskbrowser.agent_source_rlm": "RLM 派生",
852
+ "taskbrowser.agent_source_main": "主代理直接派生",
853
+ "taskbrowser.agent_source_via": "由 {name} 派生",
854
+ "taskbrowser.agent_source_unknown": "独立(无父线索)",
836
855
  "subagent.desc_coder": "通用软件工程任务",
837
856
  "subagent.desc_reviewer": "代码审查,发现 bug 和 API 契约违反",
838
857
  "subagent.desc_writer": "内容生产与研究报告",
@@ -1031,7 +1050,7 @@ const dictionaries = {
1031
1050
  "editor.tip_7": "💡 Tips:按 Ctrl+E 跳到行尾,Ctrl+W 删除前一个单词 【Ctrl+B 关闭 Tips】",
1032
1051
  "editor.tip_8": "💡 Tips:输入 /compact 可手动压缩上下文,释放 token 空间 【Ctrl+B 关闭 Tips】",
1033
1052
  "editor.tip_9": "💡 Tips:按 Ctrl+O 可在外部编辑器中编写长消息 【Ctrl+B 关闭 Tips】",
1034
- "editor.tip_10": "💡 Tips:输入 /rlm 可开启运行时记忆,让 Agent 跨轮记住更多上下文 【Ctrl+B 关闭 Tips】",
1053
+ "editor.tip_10": "💡 Tips:输入 /rlm 开启持久 Python 工作环境(可派生 subagent) 【Ctrl+B 关闭 Tips】",
1035
1054
  "editor.tip_11": "💡 Tips:按 Ctrl+D(空输入时)可快速退出,再按一次确认 【Ctrl+B 关闭 Tips】",
1036
1055
  "editor.tip_12": "💡 Tips:输入 /sessions 可查看并恢复历史会话 【Ctrl+B 关闭 Tips】",
1037
1056
  "editor.tip_13": "💡 Tips:按 Ctrl+Shift+F 或输入 /search 可搜索会话历史 【Ctrl+B 关闭 Tips】",
@@ -1174,7 +1193,6 @@ const dictionaries = {
1174
1193
  "config.script_on": "脚本模式已开启 — RunScript 可在沙箱中批量调用工具",
1175
1194
  "config.script_off": "脚本模式已关闭 — RunScript 已从工具中移除",
1176
1195
  "config.rlm_off": "RLM 模式已关闭",
1177
- "config.rlm_usage": "用法: /rlm (切换) | /rlm on | /rlm off",
1178
1196
  "config.auto_already_on": "自动模式已开启",
1179
1197
  "config.auto_already_off": "自动模式已关闭",
1180
1198
  "settings.title": "设置",
@@ -2143,6 +2161,25 @@ const dictionaries = {
2143
2161
  "taskbrowser.timed_out": "Timed out: ",
2144
2162
  "taskbrowser.yes": "Yes",
2145
2163
  "taskbrowser.awaiting_label": "Awaiting: ",
2164
+ "taskbrowser.agents": "agents",
2165
+ "taskbrowser.agents_title": "Agents",
2166
+ "taskbrowser.agents_live": "live",
2167
+ "taskbrowser.agents_ended": "ended",
2168
+ "taskbrowser.agents_completed": "completed",
2169
+ "taskbrowser.agents_failed": "failed",
2170
+ "taskbrowser.agents_empty": "No active subagents in this session.",
2171
+ "taskbrowser.agents_no_output": "Subagents stream into their transcript card — no live output here.",
2172
+ "taskbrowser.agent_detail": "Agent Detail",
2173
+ "taskbrowser.agent_type": "Type: ",
2174
+ "taskbrowser.agent_source": "Source: ",
2175
+ "taskbrowser.agent_chain": "Chain: ",
2176
+ "taskbrowser.agent_activity": "Activity: ",
2177
+ "taskbrowser.agent_instance": "Instance: ",
2178
+ "taskbrowser.agent_main": "main",
2179
+ "taskbrowser.agent_source_rlm": "RLM-derived",
2180
+ "taskbrowser.agent_source_main": "spawned by main agent",
2181
+ "taskbrowser.agent_source_via": "via {name}",
2182
+ "taskbrowser.agent_source_unknown": "standalone (no parent info)",
2146
2183
  "subagent.desc_coder": "General software engineering",
2147
2184
  "subagent.desc_reviewer": "Code review, bug & API contract violations",
2148
2185
  "subagent.desc_writer": "Content production & research reports",
@@ -2341,7 +2378,7 @@ const dictionaries = {
2341
2378
  "editor.tip_7": "💡 Tips: Ctrl+E jumps to end of line, Ctrl+W deletes the previous word [Ctrl+B to hide Tips]",
2342
2379
  "editor.tip_8": "💡 Tips: Type /compact to manually compact context and free token space [Ctrl+B to hide Tips]",
2343
2380
  "editor.tip_9": "💡 Tips: Press Ctrl+O to compose long messages in an external editor [Ctrl+B to hide Tips]",
2344
- "editor.tip_10": "💡 Tips: Type /rlm to enable runtime memory for better cross-turn recall [Ctrl+B to hide Tips]",
2381
+ "editor.tip_10": "💡 Tips: Type /rlm to enable a persistent Python workspace (subagents can be spawned from it) [Ctrl+B to hide Tips]",
2345
2382
  "editor.tip_11": "💡 Tips: Press Ctrl+D (on empty input) to quit, press again to confirm [Ctrl+B to hide Tips]",
2346
2383
  "editor.tip_12": "💡 Tips: Type /sessions to browse and resume past sessions [Ctrl+B to hide Tips]",
2347
2384
  "editor.tip_13": "💡 Tips: Press Ctrl+Shift+F or type /search to search the conversation [Ctrl+B to hide Tips]",
@@ -2484,7 +2521,6 @@ const dictionaries = {
2484
2521
  "config.script_on": "Script mode enabled — RunScript can batch tool calls from a sandbox",
2485
2522
  "config.script_off": "Script mode disabled — RunScript removed from the tools",
2486
2523
  "config.rlm_off": "RLM mode disabled",
2487
- "config.rlm_usage": "Usage: /rlm (toggle) | /rlm on | /rlm off",
2488
2524
  "config.auto_already_on": "Auto mode is already on",
2489
2525
  "config.auto_already_off": "Auto mode is already off",
2490
2526
  "settings.title": "Settings",
@@ -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-CD0sakGq.mjs";
6
+ import { t as TextInputDialogComponent } from "./text-input-dialog-BlSqBYxO.mjs";
7
7
  export { TextInputDialogComponent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.17.8",
3
+ "version": "0.17.9",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",
@@ -57,10 +57,10 @@
57
57
  "bench:startup": "node scripts/startup-bench.mjs"
58
58
  },
59
59
  "dependencies": {
60
- "@earendil-works/pi-codemode": "0.99.1",
60
+ "@earendil-works/pi-codemode": "^1.0.4",
61
61
  "@fontsource-variable/inter": "^5.3.0",
62
62
  "@fontsource-variable/jetbrains-mono": "^5.3.0",
63
- "@liutod-scream/pi-tui": "^0.87.1",
63
+ "@liutod-scream/pi-tui": "^1.0.4",
64
64
  "@mariozechner/clipboard": "^0.3.2",
65
65
  "chalk": "^5.4.1",
66
66
  "cli-highlight": "^2.1.11",