scream-code 0.8.1 → 0.8.2

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
@@ -39,8 +39,8 @@ Scream Code 是一款省心的本地中文 AI Agent 助手。无需硬记代码
39
39
  </tr>
40
40
  <tr>
41
41
  <td width="50%">
42
- <h3>🧠 永久记忆备忘录</h3>
43
- <p><strong>痛点记忆结构化SQL提取</strong>,FTS5全文+Tag语义+向量三重检索不漂移。跨会话共享,越用越懂你。</p>
42
+ <h3>🧠 永久记忆备忘录+SAG知识库</h3>
43
+ <p><strong>痛点记忆结构化SQL提取</strong>,FTS5全文+Tag语义+向量三重检索不漂移。跨会话共享,越用越懂你。Ai时代-SAG知识库,随时导入你的专属知识,让Agent更懂你</p>
44
44
  </td>
45
45
  <td width="50%">
46
46
  <h3>🛡️ 效率级轻量底层</h3>
@@ -74,7 +74,9 @@ npm install -g scream-code
74
74
  ```
75
75
 
76
76
  安装完成后,`scream` 命令自动加入 PATH。首次安装约需 2-5 分钟。
77
-
77
+ TUI启动命令`scream`
78
+ 自动权限模式启动`scream --auto`
79
+ 自动批准模式启动`scream -y`
78
80
 
79
81
  ### 第二步:启动并配置 AI 服务
80
82
 
@@ -5,7 +5,7 @@ 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 parse$7, S as normalize, T as resolve$1, _ as KnowledgeStore, b as isAbsolute$1, i as isSupportedFile, n as ingestDirectory, r as ingestFile, t as multiSearch, v as basename$1, w as relative$1, x as join$1, y as dirname$2 } from "./src-CFaXGblk.mjs";
8
+ import { C as parse$7, S as normalize, T as resolve$1, _ as KnowledgeStore, b as isAbsolute$1, i as isSupportedFile, n as ingestDirectory, r as ingestFile, t as multiSearch, v as basename$1, w as relative$1, x as join$1, y as dirname$2 } from "./src-aqNCeRzD.mjs";
9
9
  import { createRequire } from "node:module";
10
10
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
11
11
  import Jn, { access, appendFile, chmod, copyFile, cp, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, writeFile } from "node:fs/promises";
@@ -58216,7 +58216,7 @@ var KnowledgeLookupTool = class {
58216
58216
  const llm = { generate: async (systemPrompt, userPrompt) => {
58217
58217
  return this.agent.generateText(systemPrompt, userPrompt);
58218
58218
  } };
58219
- const { multiSearch } = await import("./src-BJO7qNnY.mjs");
58219
+ const { multiSearch } = await import("./src-S0QPScxL.mjs");
58220
58220
  const results = await multiSearch(store, llm, query, { topK });
58221
58221
  if (results.length === 0) return {
58222
58222
  isError: false,
@@ -78393,7 +78393,21 @@ function stripContextMetadata(message) {
78393
78393
  //#region ../../packages/agent-core/src/agent/compaction/compaction-instruction.md
78394
78394
  var compaction_instruction_default = "\n--- This message is a direct task, not part of the above conversation ---\n\nYou are now given a task to compact this conversation context according to specific priorities and output requirements.\n\nOutput text only. DO NOT CALL ANY TOOLS. Calling tools will be rejected and fails the task. You already have all the information you need in the conversation history. You have only one chance.\n\nThe goal of compaction is to keep essential code patterns, technical details, and architectural decisions for continuing development without losing context after the above messages are cleared work.\n\n{{ customInstruction }}\n\n<!-- Memory Memo Extraction (PRIORITY — do not skip) -->\n\n## 任务经验提取\n\nAFTER completing the compaction summary below, scan the messages being compacted for **completed task loops**. A task loop is \"completed\" when:\n- The user made a clear request or asked a specific question\n- You provided a solution or answer\n- The outcome is clear (success, partial success, or failure)\n\nFor each completed task loop found, output a structured experience record **at the very end of your response**:\n\n```memory-memo\n{\n \"userNeed\": \"<the user's need or goal, one sentence>\",\n \"approach\": \"<what was done — the approach taken, 2-4 sentences>\",\n \"outcome\": \"<final result, e.g. '完成', '部分完成', '失败: reason'>\",\n \"whatFailed\": \"<dead ends tried — things that didn't work, or 'none'>\",\n \"whatWorked\": \"<key actions that ultimately worked, or 'none'>\",\n \"tags\": [\"<tag1>\", \"<tag2>\", \"<tag3>\"]\n}\n```\n\nGuidelines:\n- Record important failed attempts in \"whatFailed\" to help avoid repeating mistakes.\n- Record key successful actions in \"whatWorked\" to help reuse effective approaches.\n- Include 3-5 semantic \"tags\" summarizing the task domain, tech stack, or action type (e.g. [\"react\", \"auth\", \"部署\"]).\n- Skip in-progress work unless it contains a valuable error+fix experience.\n- Merge closely related sub-tasks into a single record.\n- Use the exact field names and JSON format shown above.\n\nIf no completed task loops are found in the compacted messages, output:\n```memory-memo\n{\"none\": true}\n```\n\n<!-- Compression Priorities (in order) -->\n\n1. **Current Task State**: What is being worked on RIGHT NOW\n2. **Errors & Solutions**: All encountered errors and their resolutions\n3. **Code Evolution**: Final working versions only (remove intermediate attempts)\n4. **System Context**: Project structure, dependencies, environment setup\n5. **Design Decisions**: Architectural choices and their rationale\n6. **TODO Items**: Unfinished tasks and known issues\n\n<!-- Required Output Structure -->\n\n## Current Focus\n\n[What we're working on now]\n\n## Environment\n\n- [Key setup/config points]\n- ...\n\n## Completed Tasks\n\n- [Task]: [Brief outcome]\n- ...\n\n## Active Issues\n\n- [Issue]: [Status/Next steps]\n- ...\n\n## Code State\n\n### [Critical file name]\n\n[Brief description of the file's purpose and current state]\n\n```\n[The latest version of critical code snippets in this file, <20 lines]\n```\n\n### [Critical file name]\n\n- [Useful classes/methods/functions]: [Brief description/usage]\n- ...\n\n<!-- Omit non-critical code, intermediate attempts, and resolved errors -->\n\n## Important Context\n\n- [Any crucial information not covered above]\n- ...\n\n## All User Messages\n\n- [Detailed non tool use user message]\n- ...\n";
78395
78395
  //#endregion
78396
+ //#region ../../packages/agent-core/src/agent/compaction/compaction-update-instruction.md
78397
+ var compaction_update_instruction_default = "\n--- This message is a direct task, not part of the above conversation ---\n\nYou are now given a task to UPDATE an existing compaction summary with new messages that came in after the last compaction.\n\nA previous compaction summary already exists at the top of the conversation (the first assistant message). You must NOT discard it. Instead, merge the new information into the existing structure, keeping all previously captured state intact unless explicitly contradicted or resolved by the new messages.\n\nOutput text only. DO NOT CALL ANY TOOLS. Calling tools will be rejected and fails the task. You have only one chance.\n\n{{ customInstruction }}\n\n<!-- Previous Summary Reference -->\n\nThe first assistant message above is the PREVIOUS summary. Treat it as the source of truth for everything that happened before the new messages. Do not restate it verbatim — produce an updated, merged summary.\n\n<!-- Memory Memo Extraction (PRIORITY — do not skip) -->\n\n## 任务经验提取\n\nAFTER completing the updated summary below, scan ONLY the new messages being compacted (not those already covered by the previous summary) for **completed task loops**. A task loop is \"completed\" when:\n- The user made a clear request or asked a specific question\n- You provided a solution or answer\n- The outcome is clear (success, partial success, or failure)\n\nFor each completed task loop found, output a structured experience record **at the very end of your response**:\n\n```memory-memo\n{\n \"userNeed\": \"<the user's need or goal, one sentence>\",\n \"approach\": \"<what was done — the approach taken, 2-4 sentences>\",\n \"outcome\": \"<final result, e.g. '完成', '部分完成', '失败: reason'>\",\n \"whatFailed\": \"<dead ends tried — things that didn't work, or 'none'>\",\n \"whatWorked\": \"<key actions that ultimately worked, or 'none'>\",\n \"tags\": [\"<tag1>\", \"<tag2>\", \"<tag3>\"]\n}\n```\n\nGuidelines:\n- Record important failed attempts in \"whatFailed\" to help avoid repeating mistakes.\n- Record key successful actions in \"whatWorked\" to help reuse effective approaches.\n- Include 3-5 semantic \"tags\" summarizing the task domain, tech stack, or action type.\n- Skip in-progress work unless it contains a valuable error+fix experience.\n- Merge closely related sub-tasks into a single record.\n- Use the exact field names and JSON format shown above.\n\nIf no completed task loops are found in the new compacted messages, output:\n```memory-memo\n{\"none\": true}\n```\n\n<!-- Update Rules -->\n\n1. Preserve the section structure of the previous summary (Current Focus, Environment, Completed Tasks, Active Issues, Code State, Important Context, All User Messages).\n2. Move newly-completed tasks from \"Current Focus\" / \"Active Issues\" into \"Completed Tasks\".\n3. Update \"Current Focus\" to reflect what is being worked on RIGHT NOW.\n4. Append new user messages to \"All User Messages\" — do not repeat those already captured.\n5. Refresh \"Code State\" code snippets only if newer versions exist in the new messages.\n6. Drop resolved issues from \"Active Issues\"; add newly-discovered ones.\n7. Do not invent new information. If the new messages say nothing about a section, carry the previous content forward unchanged.\n\n<!-- Required Output Structure -->\n\n## Current Focus\n\n[What we're working on now]\n\n## Environment\n\n- [Key setup/config points]\n- ...\n\n## Completed Tasks\n\n- [Task]: [Brief outcome]\n- ...\n\n## Active Issues\n\n- [Issue]: [Status/Next steps]\n- ...\n\n## Code State\n\n### [Critical file name]\n\n[Brief description of the file's purpose and current state]\n\n```\n[The latest version of critical code snippets in this file, <20 lines]\n```\n\n## Important Context\n\n- [Any crucial information not covered above]\n- ...\n\n## All User Messages\n\n- [Detailed non tool use user message]\n- ...\n";
78398
+ //#endregion
78396
78399
  //#region ../../packages/agent-core/src/agent/compaction/render-messages.ts
78400
+ const TOOL_RESULT_MAX_CHARS = 2e3;
78401
+ const TOOL_CALL_MAX_CHARS = 2e3;
78402
+ const TRUNCATE_HEAD_RATIO = .6;
78403
+ function truncateForSummary(text, maxChars, headRatio = TRUNCATE_HEAD_RATIO) {
78404
+ if (text.length <= maxChars) return text;
78405
+ const headChars = Math.round(maxChars * Math.min(Math.max(headRatio, 0), 1));
78406
+ const tailChars = maxChars - headChars;
78407
+ const elided = text.length - maxChars;
78408
+ const tail = tailChars > 0 ? text.slice(-tailChars) : "";
78409
+ return `${text.slice(0, headChars)} […${elided}ch elided…] ${tail}`;
78410
+ }
78397
78411
  function renderMessagesToText(messages) {
78398
78412
  return messages.map((message, index) => renderMessageToText(message, index)).join("\n\n");
78399
78413
  }
@@ -78413,8 +78427,8 @@ function renderMessageToText(message, index) {
78413
78427
  }
78414
78428
  function renderContentPartToText(part) {
78415
78429
  switch (part.type) {
78416
- case "text": return renderBlock("text", part.text);
78417
- case "think": return renderBlock("think", part.think);
78430
+ case "text": return renderBlock("text", truncateForSummary(part.text, TOOL_RESULT_MAX_CHARS));
78431
+ case "think": return renderBlock("think", truncateForSummary(part.think, TOOL_RESULT_MAX_CHARS));
78418
78432
  case "image_url": return renderMediaPart("image_url", part.imageUrl.url, part.imageUrl.id);
78419
78433
  case "audio_url": return renderMediaPart("audio_url", part.audioUrl.url, part.audioUrl.id);
78420
78434
  case "video_url": return renderMediaPart("video_url", part.videoUrl.url, part.videoUrl.id);
@@ -78428,11 +78442,13 @@ function renderToolCallToText(toolCall) {
78428
78442
  }
78429
78443
  function renderToolCallArguments(args) {
78430
78444
  if (args === null) return "null";
78445
+ let pretty;
78431
78446
  try {
78432
- return stringifyJsonish(JSON.parse(args));
78447
+ pretty = stringifyJsonish(JSON.parse(args));
78433
78448
  } catch {
78434
- return args;
78449
+ pretty = args;
78435
78450
  }
78451
+ return truncateForSummary(pretty, TOOL_CALL_MAX_CHARS);
78436
78452
  }
78437
78453
  function renderMediaPart(type, url, id) {
78438
78454
  if (id === void 0) return `${type}: ${url}`;
@@ -78471,7 +78487,7 @@ const DEFAULT_COMPACTION_CONFIG = {
78471
78487
  reservedContextSize: 5e4,
78472
78488
  maxCompactionPerTurn: 3,
78473
78489
  maxRecentMessages: 4,
78474
- maxRecentUserMessages: Infinity,
78490
+ maxRecentUserMessages: 2,
78475
78491
  maxRecentSizeRatio: .2,
78476
78492
  minOverflowReductionRatio: .05,
78477
78493
  turnGrowthMultiplier: 2.5
@@ -78567,6 +78583,56 @@ function canSplitAfter(messages, index) {
78567
78583
  if (messages[index + 1]?.role === "tool") return false;
78568
78584
  return true;
78569
78585
  }
78586
+ //#endregion
78587
+ //#region ../../packages/agent-core/src/agent/compaction/file-operations.ts
78588
+ function createFileOps() {
78589
+ return {
78590
+ read: /* @__PURE__ */ new Set(),
78591
+ written: /* @__PURE__ */ new Set(),
78592
+ edited: /* @__PURE__ */ new Set()
78593
+ };
78594
+ }
78595
+ function extractFileOpsFromMessage(message, ops) {
78596
+ if (message.role !== "assistant") return;
78597
+ for (const call of message.toolCalls) {
78598
+ if (call.type !== "function" || call.arguments === null) continue;
78599
+ let args;
78600
+ try {
78601
+ args = JSON.parse(call.arguments);
78602
+ } catch {
78603
+ continue;
78604
+ }
78605
+ const path = typeof args["path"] === "string" ? args["path"] : void 0;
78606
+ if (path === void 0 || path.length === 0) continue;
78607
+ switch (call.name) {
78608
+ case "Read":
78609
+ ops.read.add(path);
78610
+ break;
78611
+ case "Edit":
78612
+ ops.edited.add(path);
78613
+ break;
78614
+ case "Write":
78615
+ ops.written.add(path);
78616
+ break;
78617
+ }
78618
+ }
78619
+ }
78620
+ const FILE_LIMIT = 20;
78621
+ function formatFileOperations(ops) {
78622
+ const modified = new Set([...ops.edited, ...ops.written]);
78623
+ const readOnly = [...ops.read].filter((f) => !modified.has(f)).sort();
78624
+ const modifiedFiles = [...modified].sort();
78625
+ const all = [...new Set([...readOnly, ...modifiedFiles])].sort();
78626
+ if (all.length === 0) return "";
78627
+ const mode = /* @__PURE__ */ new Map();
78628
+ for (const f of readOnly) mode.set(f, "Read");
78629
+ for (const f of modifiedFiles) mode.set(f, ops.read.has(f) ? "RW" : "Write");
78630
+ const lines = ["<files>"];
78631
+ for (const f of all.slice(0, FILE_LIMIT)) lines.push(`${f} (${mode.get(f)})`);
78632
+ if (all.length > FILE_LIMIT) lines.push(`[…${all.length - FILE_LIMIT} files elided…]`);
78633
+ lines.push("</files>");
78634
+ return lines.join("\n");
78635
+ }
78570
78636
  /** Max consecutive compaction failures before auto-compaction is
78571
78637
  * disabled for the remainder of the turn. Resets each turn. */
78572
78638
  const MAX_CONSECUTIVE_FAILURES = 3;
@@ -78697,7 +78763,7 @@ var FullCompaction = class {
78697
78763
  }
78698
78764
  checkAutoCompaction(throwOnLimit = true) {
78699
78765
  if (this.compacting) return true;
78700
- if (!this.strategy.shouldCompact(this.tokenCountWithPending)) return false;
78766
+ if (!this.strategy.shouldCompact(this.effectiveTokenCount)) return false;
78701
78767
  return this.beginAutoCompaction(throwOnLimit);
78702
78768
  }
78703
78769
  beginAutoCompaction(throwOnLimit = true) {
@@ -78744,6 +78810,7 @@ var FullCompaction = class {
78744
78810
  const originalHistory = [...this.agent.context.history];
78745
78811
  const tokensBefore = estimateTokensForMessages(originalHistory);
78746
78812
  const model = this.agent.config.model;
78813
+ const isUpdate = extractPreviousSummary(originalHistory) !== null;
78747
78814
  let retryCount = 0;
78748
78815
  try {
78749
78816
  await this.triggerPreCompactHook(data, tokensBefore, signal);
@@ -78752,11 +78819,12 @@ var FullCompaction = class {
78752
78819
  let summary;
78753
78820
  while (true) {
78754
78821
  const messagesToCompact = originalHistory.slice(0, compactedCount);
78822
+ const instruction = isUpdate ? COMPACTION_UPDATE_INSTRUCTION(data.instruction) : COMPACTION_INSTRUCTION(data.instruction);
78755
78823
  const messages = [...project(messagesToCompact), {
78756
78824
  role: "user",
78757
78825
  content: [{
78758
78826
  type: "text",
78759
- text: COMPACTION_INSTRUCTION(data.instruction)
78827
+ text: instruction
78760
78828
  }],
78761
78829
  toolCalls: []
78762
78830
  }];
@@ -78782,12 +78850,17 @@ var FullCompaction = class {
78782
78850
  return;
78783
78851
  }
78784
78852
  const recent = originalHistory.slice(compactedCount);
78785
- const tokensAfter = estimateTokens$1(summary) + estimateTokensForMessages(recent);
78853
+ const messagesToCompactForOps = originalHistory.slice(0, compactedCount);
78854
+ const fileOps = createFileOps();
78855
+ for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
78856
+ const processedSummary = this.postProcessSummary(summary, fileOps);
78857
+ const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
78786
78858
  const result = {
78787
- summary,
78859
+ summary: processedSummary,
78788
78860
  compactedCount,
78789
78861
  tokensBefore,
78790
- tokensAfter
78862
+ tokensAfter,
78863
+ ...isUpdate ? { isUpdate: true } : {}
78791
78864
  };
78792
78865
  this.markCompleted();
78793
78866
  this.agent.emitEvent({
@@ -78795,7 +78868,7 @@ var FullCompaction = class {
78795
78868
  result
78796
78869
  });
78797
78870
  this.agent.context.applyCompaction(result);
78798
- await this.extractAndStoreMemos(summary);
78871
+ await this.extractAndStoreMemos(processedSummary);
78799
78872
  this.triggerPostCompactHook(data, result);
78800
78873
  this.consecutiveCompactionFailures = 0;
78801
78874
  this._shouldInjectSessionSummary = true;
@@ -78887,22 +78960,27 @@ var FullCompaction = class {
78887
78960
  });
78888
78961
  }
78889
78962
  /**
78890
- * Append the current todo list as a markdown section to the compaction
78891
- * summary so active tasks survive compression. This mirrors kimi-code's
78892
- * approach: without it, the todo list is lost after compaction because
78893
- * the original messages containing it are removed from the context window.
78963
+ * Append the current todo list and file operations as markdown sections to
78964
+ * the compaction summary so active tasks and file context survive
78965
+ * compression. Without this, both are lost after compaction because the
78966
+ * original messages containing them are removed from the context window.
78894
78967
  */
78895
- postProcessSummary(summary) {
78968
+ postProcessSummary(summary, fileOps) {
78896
78969
  const todos = this.agent.tools.storeData()["todo"] ?? [];
78897
- if (todos.length === 0) return summary;
78898
- const todoMarkdown = [
78899
- "## TODO List",
78900
- "",
78901
- ...todos.map((t) => {
78970
+ const sections = [summary.trim()];
78971
+ if (todos.length > 0) {
78972
+ const lines = todos.map((t) => {
78902
78973
  return `- [${t.status === "done" ? "x" : t.status === "in_progress" ? "-" : " "}] ${t.title}`;
78903
- })
78904
- ].join("\n");
78905
- return `${summary.trim()}\n\n${todoMarkdown}`;
78974
+ });
78975
+ sections.push([
78976
+ "## TODO List",
78977
+ "",
78978
+ ...lines
78979
+ ].join("\n"));
78980
+ }
78981
+ const filesSection = formatFileOperations(fileOps);
78982
+ if (filesSection.length > 0) sections.push(filesSection);
78983
+ return sections.join("\n\n");
78906
78984
  }
78907
78985
  };
78908
78986
  function extractCompactionSummary(response, model) {
@@ -78911,6 +78989,18 @@ function extractCompactionSummary(response, model) {
78911
78989
  return summary;
78912
78990
  }
78913
78991
  const COMPACTION_INSTRUCTION = (customInstruction = "") => renderPrompt(compaction_instruction_default, { customInstruction });
78992
+ const COMPACTION_UPDATE_INSTRUCTION = (customInstruction = "") => renderPrompt(compaction_update_instruction_default, { customInstruction });
78993
+ /**
78994
+ * If history starts with a compaction_summary message, return its text so the
78995
+ * next compaction can merge new content into it instead of starting fresh.
78996
+ * Returns null when no prior summary exists (first compaction in the session).
78997
+ */
78998
+ function extractPreviousSummary(history) {
78999
+ const head = history[0];
79000
+ if (head?.origin?.kind !== "compaction_summary") return null;
79001
+ const text = head.content.filter((p) => p.type === "text").map((p) => p.text).join("");
79002
+ return text.length > 0 ? text : null;
79003
+ }
78914
79004
  //#endregion
78915
79005
  //#region ../../packages/agent-core/src/flags/registry.ts
78916
79006
  /**
@@ -78995,7 +79085,8 @@ const DEFAULT_CONFIG = {
78995
79085
  keepRecentMessages: 20,
78996
79086
  minContentTokens: 100,
78997
79087
  minContextUsageRatio: .5,
78998
- truncatedMarker: "[Old tool result content cleared]"
79088
+ truncatedMarker: "[Old tool result content cleared]",
79089
+ uselessMarker: "[Uneventful result elided]"
78999
79090
  };
79000
79091
  /**
79001
79092
  * Walk the message list and find Read tool calls whose file paths were
@@ -79047,8 +79138,8 @@ var MicroCompaction = class {
79047
79138
  };
79048
79139
  }
79049
79140
  /** Reset the internal cutoff line (e.g. after a full compaction). */
79050
- reset(maxCutoff = 0) {
79051
- this.cutoff = Math.min(this.cutoff, maxCutoff);
79141
+ reset() {
79142
+ this.cutoff = 0;
79052
79143
  }
79053
79144
  /** Advance the cutoff line and log the change. */
79054
79145
  apply(cutoff) {
@@ -79073,7 +79164,9 @@ var MicroCompaction = class {
79073
79164
  * Apply micro-compaction to a message list: replace old tool results
79074
79165
  * before the cutoff line with truncated markers. Read results for files
79075
79166
  * that were re-read later get a supersede marker so the model knows
79076
- * the old content is stale.
79167
+ * the old content is stale. Tool results explicitly marked useless are
79168
+ * elided with a short notice regardless of size, since they carry no
79169
+ * actionable information.
79077
79170
  */
79078
79171
  compact(messages) {
79079
79172
  const config = this.config;
@@ -79081,7 +79174,16 @@ var MicroCompaction = class {
79081
79174
  const result = [];
79082
79175
  let i = 0;
79083
79176
  for (const msg of messages) {
79084
- if (i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && estimateTokensForMessages([msg]) >= config.minContentTokens) {
79177
+ const isUseless = i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && msg.useless === true;
79178
+ const isOversizedTruncatable = i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && estimateTokensForMessages([msg]) >= config.minContentTokens;
79179
+ if (isUseless) result.push({
79180
+ ...msg,
79181
+ content: [{
79182
+ type: "text",
79183
+ text: config.uselessMarker
79184
+ }]
79185
+ });
79186
+ else if (isOversizedTruncatable) {
79085
79187
  const marker = msg.toolCallId !== void 0 && superseded.has(msg.toolCallId) ? `[Superseded by a newer read of ${superseded.get(msg.toolCallId)}]` : config.truncatedMarker;
79086
79188
  result.push({
79087
79189
  ...msg,
@@ -79106,6 +79208,7 @@ var MicroCompaction = class {
79106
79208
  }
79107
79209
  measureEffect(messages, cutoff) {
79108
79210
  let markerTokenCount;
79211
+ let uselessMarkerTokenCount;
79109
79212
  let truncatedToolResultCount = 0;
79110
79213
  let beforeTokens = 0;
79111
79214
  let afterTokens = 0;
@@ -79113,11 +79216,19 @@ var MicroCompaction = class {
79113
79216
  const message = messages[i];
79114
79217
  if (message?.role !== "tool" || message.toolCallId === void 0) continue;
79115
79218
  const contentTokens = estimateTokensForMessages([message]);
79116
- if (contentTokens < this.config.minContentTokens) continue;
79117
- markerTokenCount ??= estimateTokens$1(this.config.truncatedMarker);
79118
- truncatedToolResultCount += 1;
79119
- beforeTokens += contentTokens;
79120
- afterTokens += markerTokenCount;
79219
+ const isUseless = message.useless === true;
79220
+ if (!isUseless && contentTokens < this.config.minContentTokens) continue;
79221
+ if (isUseless) {
79222
+ uselessMarkerTokenCount ??= estimateTokens$1(this.config.uselessMarker);
79223
+ truncatedToolResultCount += 1;
79224
+ beforeTokens += contentTokens;
79225
+ afterTokens += uselessMarkerTokenCount;
79226
+ } else {
79227
+ markerTokenCount ??= estimateTokens$1(this.config.truncatedMarker);
79228
+ truncatedToolResultCount += 1;
79229
+ beforeTokens += contentTokens;
79230
+ afterTokens += markerTokenCount;
79231
+ }
79121
79232
  }
79122
79233
  return {
79123
79234
  truncatedToolResultCount,
@@ -80326,7 +80437,8 @@ var ContextMemory = class {
80326
80437
  this.pushHistory({
80327
80438
  ...message,
80328
80439
  role: "tool",
80329
- isError: event.result.isError
80440
+ isError: event.result.isError,
80441
+ useless: event.result.isError !== true && event.result.useless === true ? true : void 0
80330
80442
  });
80331
80443
  this.pendingToolResultIds.delete(event.toolCallId);
80332
80444
  this.flushDeferredMessagesIfToolExchangeClosed();
@@ -122014,7 +122126,7 @@ function optionalBuildString(value) {
122014
122126
  return typeof value === "string" && value.length > 0 ? value : void 0;
122015
122127
  }
122016
122128
  const SCREAM_BUILD_INFO = {
122017
- version: optionalBuildString("0.8.1"),
122129
+ version: optionalBuildString("0.8.2"),
122018
122130
  channel: optionalBuildString(""),
122019
122131
  commit: optionalBuildString(""),
122020
122132
  buildTarget: optionalBuildString("darwin-arm64")
@@ -122173,7 +122285,7 @@ function errorMessage(error) {
122173
122285
  //#region src/cli/commands.ts
122174
122286
  function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}, onStreamJson = () => {}, onChannelSetup = () => {}) {
122175
122287
  const program = new Command(CLI_COMMAND_NAME).description("下一代智能体的起点").version(version, "-V, --version").allowUnknownOption(false).configureHelp({ helpWidth: 100 }).helpOption("-h, --help", "显示帮助。").addHelpText("after", "\n文档: https://scream-cli.github.io/scream-code/\n");
122176
- program.addOption(new Option("-S, --session [id]", "恢复会话。带 ID:恢复该会话。不带 ID:交互式选择。").argParser((val) => val === true ? "" : val)).addOption(new Option("-r, --resume [id]").hideHelp().argParser((val) => val === true ? "" : val)).option("-C, --continue", "继续当前工作目录的上一个会话。", false).option("-y, --yolo", "自动批准所有操作。", false).option("--auto", "以自动权限模式启动。", false).addOption(new Option("-m, --model <model>", "本次调用使用的 LLM 模型别名。默认使用 config.toml 中的 default_model。")).addOption(new Option("-p, --prompt <prompt>", "非交互式运行一条提示并打印响应。")).addOption(new Option("--output-format <format>", "提示模式的输出格式。默认为 text。").choices(["text", "stream-json"])).addOption(new Option("--skills-dir <dir>", "从该目录加载技能,而不是自动发现的用户和项目目录。可多次指定。").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--yes").hideHelp().default(false)).addOption(new Option("--auto-approve").hideHelp().default(false)).option("--plan", "以计划模式启动。", false);
122288
+ program.addOption(new Option("-S, --session [id]", "恢复会话。带 ID:恢复该会话。不带 ID:交互式选择。").argParser((val) => val === true ? "" : val)).addOption(new Option("-r, --resume [id]").hideHelp().argParser((val) => val === true ? "" : val)).option("-C, --continue", "继续当前工作目录的上一个会话。", false).option("-y, --yolo", "自动批准所有操作。", false).option("--auto", "以自动权限模式启动。", false).addOption(new Option("-m, --model <model>", "本次调用使用的 LLM 模型别名。默认使用 config.toml 中的 default_model。")).addOption(new Option("-p, --prompt <prompt>", "非交互式运行一条提示并打印响应。")).addOption(new Option("--output-format <format>", "提示模式的输出格式。默认为 text。").choices(["text", "stream-json"])).addOption(new Option("--skills-dir <dir>", "从该目录加载技能,而不是自动发现的用户和项目目录。可多次指定。").argParser((value, previous) => [...previous ?? [], value]).default([])).addOption(new Option("--yes").hideHelp().default(false)).addOption(new Option("--auto-approve").hideHelp().default(false)).option("--plan", "以计划模式启动。", false).option("--wolfpack", "启动时默认开启 WolfPack 批量并发模式。", false);
122177
122289
  registerExportCommand(program);
122178
122290
  registerMigrateCommand(program, onMigrate);
122179
122291
  program.command("stream-json", { hidden: true }).option("--input-format <fmt>", "stream-json").option("--output-format <fmt>", "stream-json").option("--resume <id>", "resume a previous session").option("--model <model>", "model to use").option("--permission-mode <mode>", "permission mode").option("--permission-prompt-tool <mode>", "(ignored, cc-connect compat)").option("--replay-user-messages", "(ignored, cc-connect compat)").option("--verbose", "(ignored, cc-connect compat)").option("--system-prompt <text>", "(ignored, cc-connect compat)").option("--append-system-prompt <text>", "(passed through to agent)").option("--append-system-prompt-file <path>", "(passed through to agent; file contents are read and merged)").option("--allowedTools <list>", "(ignored, cc-connect compat)").option("--disallowedTools <list>", "(ignored, cc-connect compat)").option("--effort <value>", "(ignored, cc-connect compat)").option("--max-context-tokens <N>", "(ignored, cc-connect compat)").option("--skills-dir <dir>", "additional skills directory (repeatable)", (value, previous) => [...previous ?? [], value], []).option("--plugin-dir <dir>", "(ignored, cc-connect compat; repeatable)", (value, previous) => [...previous ?? [], value], []).action((subOpts) => {
@@ -122204,6 +122316,7 @@ function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}
122204
122316
  yolo: yoloValue,
122205
122317
  auto: autoValue,
122206
122318
  plan: raw["plan"],
122319
+ wolfpack: raw["wolfpack"],
122207
122320
  model: raw["model"],
122208
122321
  outputFormat: raw["outputFormat"],
122209
122322
  prompt: raw["prompt"],
@@ -122229,12 +122342,14 @@ function validateOptions(opts) {
122229
122342
  if (promptMode && opts.yolo) throw new OptionConflictError("--prompt 不能与 --yolo 同时使用。");
122230
122343
  if (promptMode && opts.auto) throw new OptionConflictError("--prompt 不能与 --auto 同时使用。");
122231
122344
  if (promptMode && opts.plan) throw new OptionConflictError("--prompt 不能与 --plan 同时使用。");
122345
+ if (promptMode && opts.wolfpack) throw new OptionConflictError("--prompt 不能与 --wolfpack 同时使用。");
122232
122346
  if (promptMode && opts.session === "") throw new OptionConflictError("在提示模式下不能使用不带 ID 的 --session。");
122233
122347
  if (opts.continue && opts.session !== void 0) throw new OptionConflictError("--continue 和 --session 不能同时使用。");
122234
122348
  if (opts.yolo && opts.auto) throw new OptionConflictError("--yolo 不能与 --auto 同时使用。");
122235
122349
  if (!promptMode && (opts.continue || opts.session !== void 0) && opts.yolo) throw new OptionConflictError("--yolo 不能与 --continue 或 --session 同时使用。");
122236
122350
  if (!promptMode && (opts.continue || opts.session !== void 0) && opts.auto) throw new OptionConflictError("--auto 不能与 --continue 或 --session 同时使用。");
122237
122351
  if (!promptMode && (opts.continue || opts.session !== void 0) && opts.plan) throw new OptionConflictError("--plan 不能与 --continue 或 --session 同时使用。");
122352
+ if (!promptMode && (opts.continue || opts.session !== void 0) && opts.wolfpack) throw new OptionConflictError("--wolfpack 不能与 --continue 或 --session 同时使用。");
122238
122353
  return {
122239
122354
  options: opts,
122240
122355
  uiMode: promptMode ? "print" : "shell"
@@ -142940,6 +143055,7 @@ var SessionManager = class {
142940
143055
  if (session === void 0) throw new Error("启动会话未初始化。");
142941
143056
  await this.setSession(session);
142942
143057
  await this.syncRuntimeState(session);
143058
+ if (startup.wolfpack && !isResumeStartup) await session.setWolfpackMode(true);
142943
143059
  this.host.state.startupState = "ready";
142944
143060
  this.host.sessionEventHandler.startSubscription();
142945
143061
  return {
@@ -145057,7 +145173,7 @@ function createInitialAppState(input) {
145057
145173
  goalActive: false,
145058
145174
  goalContinuationCount: 0,
145059
145175
  ccConnectActive: false,
145060
- wolfpackMode: false,
145176
+ wolfpackMode: input.cliOptions.wolfpack === true,
145061
145177
  loopModeEnabled: false,
145062
145178
  loopPrompt: void 0,
145063
145179
  loopLimit: void 0,
@@ -145117,6 +145233,7 @@ var ScreamTUI = class {
145117
145233
  yolo: startupInput.cliOptions.yolo,
145118
145234
  auto: startupInput.cliOptions.auto,
145119
145235
  plan: startupInput.cliOptions.plan,
145236
+ wolfpack: startupInput.cliOptions.wolfpack,
145120
145237
  model: startupInput.cliOptions.model,
145121
145238
  startupNotice: startupInput.startupNotice
145122
145239
  },
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-BkxFc8Y9.mjs")).main();
9
+ (await import("./app-C4TYfyrt.mjs")).main();
10
10
  } catch (error) {
11
11
  process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
12
12
  process.exit(1);
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
3
3
  import { dirname as __cjsShimDirname } from 'node:path';
4
4
  const __filename = __cjsShimFileURLToPath(import.meta.url);
5
5
  const __dirname = __cjsShimDirname(__filename);
6
- import { t as multiSearch } from "./src-CFaXGblk.mjs";
6
+ import { t as multiSearch } from "./src-aqNCeRzD.mjs";
7
7
  export { multiSearch };
@@ -1188,7 +1188,7 @@ Only include entities that explicitly appear in the query. If no clear entities,
1188
1188
  }
1189
1189
  //#endregion
1190
1190
  //#region ../../packages/knowledge/src/ingest.ts
1191
- const LLM_CONCURRENCY = 3;
1191
+ const LLM_CONCURRENCY = 5;
1192
1192
  const SUPPORTED_EXTENSIONS = new Set([
1193
1193
  ".md",
1194
1194
  ".markdown",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scream-code",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "A terminal-native AI agent for builders",
5
5
  "license": "MIT",
6
6
  "author": "ScreamCli",