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>🧠
|
|
43
|
-
<p><strong>痛点记忆结构化SQL提取</strong>,FTS5全文+Tag
|
|
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-
|
|
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-
|
|
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
|
-
|
|
78447
|
+
pretty = stringifyJsonish(JSON.parse(args));
|
|
78433
78448
|
} catch {
|
|
78434
|
-
|
|
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:
|
|
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.
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
78891
|
-
* summary so active tasks
|
|
78892
|
-
*
|
|
78893
|
-
*
|
|
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
|
-
|
|
78898
|
-
|
|
78899
|
-
|
|
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
|
-
|
|
78905
|
-
|
|
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(
|
|
79051
|
-
this.cutoff =
|
|
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
|
-
|
|
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
|
-
|
|
79117
|
-
|
|
79118
|
-
|
|
79119
|
-
|
|
79120
|
-
|
|
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.
|
|
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:
|
|
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-
|
|
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-
|
|
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 =
|
|
1191
|
+
const LLM_CONCURRENCY = 5;
|
|
1192
1192
|
const SUPPORTED_EXTENSIONS = new Set([
|
|
1193
1193
|
".md",
|
|
1194
1194
|
".markdown",
|