scream-code 0.8.1 → 0.8.3
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.
|
@@ -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
|
|
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-DWWVKqBK.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,8 +58216,8 @@ var KnowledgeLookupTool = class {
|
|
|
58216
58216
|
const llm = { generate: async (systemPrompt, userPrompt) => {
|
|
58217
58217
|
return this.agent.generateText(systemPrompt, userPrompt);
|
|
58218
58218
|
} };
|
|
58219
|
-
const {
|
|
58220
|
-
const results = await
|
|
58219
|
+
const { multiSearchWithTrace } = await import("./src-BChzuWm-.mjs");
|
|
58220
|
+
const { results, trace } = await multiSearchWithTrace(store, llm, query, { topK });
|
|
58221
58221
|
if (results.length === 0) return {
|
|
58222
58222
|
isError: false,
|
|
58223
58223
|
output: `No knowledge base entries found for query "${query}".`
|
|
@@ -58228,6 +58228,13 @@ var KnowledgeLookupTool = class {
|
|
|
58228
58228
|
const eventLine = result.eventTitle !== null ? ` Event: ${result.eventTitle}` : void 0;
|
|
58229
58229
|
lines.push(`**${i + 1}. ${heading}** (from: ${result.sourceName})`, ` Score: ${result.score.toFixed(3)}`, ...eventLine !== void 0 ? [eventLine] : [], ` ${result.content}`, "");
|
|
58230
58230
|
}
|
|
58231
|
+
lines.push("---", "Retrieval trace:");
|
|
58232
|
+
if (trace.fallbackReason !== null) lines.push(` ⚠ Fallback: ${trace.fallbackReason}`);
|
|
58233
|
+
for (const step of trace.steps) {
|
|
58234
|
+
const payloadStr = step.payload !== void 0 ? ` ${JSON.stringify(step.payload)}` : "";
|
|
58235
|
+
lines.push(` • ${step.step} (${step.durationMs}ms) — ${step.detail}${payloadStr}`);
|
|
58236
|
+
}
|
|
58237
|
+
if (trace.rerankedEventTitles.length > 0) lines.push(` Reranked events: ${trace.rerankedEventTitles.join(" | ")}`);
|
|
58231
58238
|
return {
|
|
58232
58239
|
isError: false,
|
|
58233
58240
|
output: lines.join("\n")
|
|
@@ -78393,7 +78400,21 @@ function stripContextMetadata(message) {
|
|
|
78393
78400
|
//#region ../../packages/agent-core/src/agent/compaction/compaction-instruction.md
|
|
78394
78401
|
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
78402
|
//#endregion
|
|
78403
|
+
//#region ../../packages/agent-core/src/agent/compaction/compaction-update-instruction.md
|
|
78404
|
+
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";
|
|
78405
|
+
//#endregion
|
|
78396
78406
|
//#region ../../packages/agent-core/src/agent/compaction/render-messages.ts
|
|
78407
|
+
const TOOL_RESULT_MAX_CHARS = 2e3;
|
|
78408
|
+
const TOOL_CALL_MAX_CHARS = 2e3;
|
|
78409
|
+
const TRUNCATE_HEAD_RATIO = .6;
|
|
78410
|
+
function truncateForSummary(text, maxChars, headRatio = TRUNCATE_HEAD_RATIO) {
|
|
78411
|
+
if (text.length <= maxChars) return text;
|
|
78412
|
+
const headChars = Math.round(maxChars * Math.min(Math.max(headRatio, 0), 1));
|
|
78413
|
+
const tailChars = maxChars - headChars;
|
|
78414
|
+
const elided = text.length - maxChars;
|
|
78415
|
+
const tail = tailChars > 0 ? text.slice(-tailChars) : "";
|
|
78416
|
+
return `${text.slice(0, headChars)} […${elided}ch elided…] ${tail}`;
|
|
78417
|
+
}
|
|
78397
78418
|
function renderMessagesToText(messages) {
|
|
78398
78419
|
return messages.map((message, index) => renderMessageToText(message, index)).join("\n\n");
|
|
78399
78420
|
}
|
|
@@ -78413,8 +78434,8 @@ function renderMessageToText(message, index) {
|
|
|
78413
78434
|
}
|
|
78414
78435
|
function renderContentPartToText(part) {
|
|
78415
78436
|
switch (part.type) {
|
|
78416
|
-
case "text": return renderBlock("text", part.text);
|
|
78417
|
-
case "think": return renderBlock("think", part.think);
|
|
78437
|
+
case "text": return renderBlock("text", truncateForSummary(part.text, TOOL_RESULT_MAX_CHARS));
|
|
78438
|
+
case "think": return renderBlock("think", truncateForSummary(part.think, TOOL_RESULT_MAX_CHARS));
|
|
78418
78439
|
case "image_url": return renderMediaPart("image_url", part.imageUrl.url, part.imageUrl.id);
|
|
78419
78440
|
case "audio_url": return renderMediaPart("audio_url", part.audioUrl.url, part.audioUrl.id);
|
|
78420
78441
|
case "video_url": return renderMediaPart("video_url", part.videoUrl.url, part.videoUrl.id);
|
|
@@ -78428,11 +78449,13 @@ function renderToolCallToText(toolCall) {
|
|
|
78428
78449
|
}
|
|
78429
78450
|
function renderToolCallArguments(args) {
|
|
78430
78451
|
if (args === null) return "null";
|
|
78452
|
+
let pretty;
|
|
78431
78453
|
try {
|
|
78432
|
-
|
|
78454
|
+
pretty = stringifyJsonish(JSON.parse(args));
|
|
78433
78455
|
} catch {
|
|
78434
|
-
|
|
78456
|
+
pretty = args;
|
|
78435
78457
|
}
|
|
78458
|
+
return truncateForSummary(pretty, TOOL_CALL_MAX_CHARS);
|
|
78436
78459
|
}
|
|
78437
78460
|
function renderMediaPart(type, url, id) {
|
|
78438
78461
|
if (id === void 0) return `${type}: ${url}`;
|
|
@@ -78471,7 +78494,7 @@ const DEFAULT_COMPACTION_CONFIG = {
|
|
|
78471
78494
|
reservedContextSize: 5e4,
|
|
78472
78495
|
maxCompactionPerTurn: 3,
|
|
78473
78496
|
maxRecentMessages: 4,
|
|
78474
|
-
maxRecentUserMessages:
|
|
78497
|
+
maxRecentUserMessages: 2,
|
|
78475
78498
|
maxRecentSizeRatio: .2,
|
|
78476
78499
|
minOverflowReductionRatio: .05,
|
|
78477
78500
|
turnGrowthMultiplier: 2.5
|
|
@@ -78567,6 +78590,117 @@ function canSplitAfter(messages, index) {
|
|
|
78567
78590
|
if (messages[index + 1]?.role === "tool") return false;
|
|
78568
78591
|
return true;
|
|
78569
78592
|
}
|
|
78593
|
+
//#endregion
|
|
78594
|
+
//#region ../../packages/agent-core/src/agent/compaction/file-operations.ts
|
|
78595
|
+
function createFileOps() {
|
|
78596
|
+
return {
|
|
78597
|
+
read: /* @__PURE__ */ new Set(),
|
|
78598
|
+
written: /* @__PURE__ */ new Set(),
|
|
78599
|
+
edited: /* @__PURE__ */ new Set()
|
|
78600
|
+
};
|
|
78601
|
+
}
|
|
78602
|
+
function extractFileOpsFromMessage(message, ops) {
|
|
78603
|
+
if (message.role !== "assistant") return;
|
|
78604
|
+
for (const call of message.toolCalls) {
|
|
78605
|
+
if (call.type !== "function" || call.arguments === null) continue;
|
|
78606
|
+
let args;
|
|
78607
|
+
try {
|
|
78608
|
+
args = JSON.parse(call.arguments);
|
|
78609
|
+
} catch {
|
|
78610
|
+
continue;
|
|
78611
|
+
}
|
|
78612
|
+
const path = typeof args["path"] === "string" ? args["path"] : void 0;
|
|
78613
|
+
if (path === void 0 || path.length === 0) continue;
|
|
78614
|
+
switch (call.name) {
|
|
78615
|
+
case "Read":
|
|
78616
|
+
ops.read.add(path);
|
|
78617
|
+
break;
|
|
78618
|
+
case "Edit":
|
|
78619
|
+
ops.edited.add(path);
|
|
78620
|
+
break;
|
|
78621
|
+
case "Write":
|
|
78622
|
+
ops.written.add(path);
|
|
78623
|
+
break;
|
|
78624
|
+
}
|
|
78625
|
+
}
|
|
78626
|
+
}
|
|
78627
|
+
const FILE_LIMIT = 20;
|
|
78628
|
+
function formatFileOperations(ops) {
|
|
78629
|
+
const modified = new Set([...ops.edited, ...ops.written]);
|
|
78630
|
+
const readOnly = [...ops.read].filter((f) => !modified.has(f)).sort();
|
|
78631
|
+
const modifiedFiles = [...modified].sort();
|
|
78632
|
+
const all = [...new Set([...readOnly, ...modifiedFiles])].sort();
|
|
78633
|
+
if (all.length === 0) return "";
|
|
78634
|
+
const mode = /* @__PURE__ */ new Map();
|
|
78635
|
+
for (const f of readOnly) mode.set(f, "Read");
|
|
78636
|
+
for (const f of modifiedFiles) mode.set(f, ops.read.has(f) ? "RW" : "Write");
|
|
78637
|
+
const lines = ["<files>"];
|
|
78638
|
+
for (const f of all.slice(0, FILE_LIMIT)) lines.push(`${f} (${mode.get(f)})`);
|
|
78639
|
+
if (all.length > FILE_LIMIT) lines.push(`[…${all.length - FILE_LIMIT} files elided…]`);
|
|
78640
|
+
lines.push("</files>");
|
|
78641
|
+
return lines.join("\n");
|
|
78642
|
+
}
|
|
78643
|
+
/** Max recursion depth for re-summarize fallback. Each level halves the
|
|
78644
|
+
* input, so depth 3 means we can compress a 8x-oversized input down to a
|
|
78645
|
+
* single summary by chaining 2^3 = 8 partial summaries. */
|
|
78646
|
+
const MAX_RE_SUMMARIZE_DEPTH = 3;
|
|
78647
|
+
var TruncatedError = class extends Error {};
|
|
78648
|
+
/**
|
|
78649
|
+
* Recursive re-summarize fallback for context overflow. When even the
|
|
78650
|
+
* minimum safe split still overflows the model (typically because a single
|
|
78651
|
+
* message contains a giant tool result), split the input in half at a safe
|
|
78652
|
+
* boundary, summarize each half, then concatenate the two partial summaries
|
|
78653
|
+
* and re-summarize them into one. If a half still overflows, recurse.
|
|
78654
|
+
*
|
|
78655
|
+
* The split uses `canSplitAfter` from the strategy to make sure each half
|
|
78656
|
+
* ends at a message boundary that doesn't orphan tool results. If no safe
|
|
78657
|
+
* split exists in the half (e.g. one giant message), feed it as-is to the
|
|
78658
|
+
* model and let the outer retry loop handle the overflow.
|
|
78659
|
+
*/
|
|
78660
|
+
async function summarizeWithFallback(messages, summarizeOnce, depth = 0) {
|
|
78661
|
+
if (messages.length <= 1) return summarizeOnce(messages);
|
|
78662
|
+
let split = -1;
|
|
78663
|
+
const mid = Math.floor(messages.length / 2);
|
|
78664
|
+
for (let i = mid; i > 0; i--) if (canSplitAfterContext(messages, i - 1)) {
|
|
78665
|
+
split = i;
|
|
78666
|
+
break;
|
|
78667
|
+
}
|
|
78668
|
+
if (split === -1) {
|
|
78669
|
+
for (let i = mid + 1; i < messages.length; i++) if (canSplitAfterContext(messages, i - 1)) {
|
|
78670
|
+
split = i;
|
|
78671
|
+
break;
|
|
78672
|
+
}
|
|
78673
|
+
}
|
|
78674
|
+
if (split === -1) return summarizeOnce(messages);
|
|
78675
|
+
const firstHalf = messages.slice(0, split);
|
|
78676
|
+
const secondHalf = messages.slice(split);
|
|
78677
|
+
const summarizeHalf = async (half) => {
|
|
78678
|
+
try {
|
|
78679
|
+
return (await summarizeOnce(half)).summary;
|
|
78680
|
+
} catch (error) {
|
|
78681
|
+
if ((error instanceof APIContextOverflowError || error instanceof TruncatedError) && depth + 1 < MAX_RE_SUMMARIZE_DEPTH) return (await summarizeWithFallback(half, summarizeOnce, depth + 1)).summary;
|
|
78682
|
+
throw error;
|
|
78683
|
+
}
|
|
78684
|
+
};
|
|
78685
|
+
return summarizeOnce([{
|
|
78686
|
+
role: "user",
|
|
78687
|
+
content: [{
|
|
78688
|
+
type: "text",
|
|
78689
|
+
text: `${await summarizeHalf(firstHalf)}\n\n---\n\n${await summarizeHalf(secondHalf)}`
|
|
78690
|
+
}],
|
|
78691
|
+
toolCalls: []
|
|
78692
|
+
}]);
|
|
78693
|
+
}
|
|
78694
|
+
/** Same split-safety rule as DefaultCompactionStrategy.canSplitAfter, but
|
|
78695
|
+
* operates on ContextMessage (which carries toolCalls on assistant msgs). */
|
|
78696
|
+
function canSplitAfterContext(messages, index) {
|
|
78697
|
+
const m = messages[index];
|
|
78698
|
+
if (m === void 0) return false;
|
|
78699
|
+
if (m.role === "user") return false;
|
|
78700
|
+
if (m.role === "assistant" && m.toolCalls.length > 0) return false;
|
|
78701
|
+
if (messages[index + 1]?.role === "tool") return false;
|
|
78702
|
+
return true;
|
|
78703
|
+
}
|
|
78570
78704
|
/** Max consecutive compaction failures before auto-compaction is
|
|
78571
78705
|
* disabled for the remainder of the turn. Resets each turn. */
|
|
78572
78706
|
const MAX_CONSECUTIVE_FAILURES = 3;
|
|
@@ -78582,6 +78716,16 @@ var FullCompaction = class {
|
|
|
78582
78716
|
consecutiveCompactionFailures = 0;
|
|
78583
78717
|
_shouldInjectSessionSummary = false;
|
|
78584
78718
|
compactionTimedOut = false;
|
|
78719
|
+
/** Token count below which compaction should not re-trigger. Set after a
|
|
78720
|
+
* successful compaction to 110% of the post-compaction token count, so
|
|
78721
|
+
* that a context sitting just above triggerRatio doesn't immediately
|
|
78722
|
+
* re-trigger on every step. Reset each turn. */
|
|
78723
|
+
lowWaterMark = 0;
|
|
78724
|
+
/** Whether a reactive (overflow-triggered) compaction has already been
|
|
78725
|
+
* attempted this turn. Prevents the overflow → compact → still near
|
|
78726
|
+
* limit → overflow → compact cycle from consuming the entire
|
|
78727
|
+
* maxCompactionPerTurn budget with marginal savings. */
|
|
78728
|
+
reactiveAttempted = false;
|
|
78585
78729
|
compacting = null;
|
|
78586
78730
|
_compactedHistory = [];
|
|
78587
78731
|
strategy;
|
|
@@ -78660,8 +78804,11 @@ var FullCompaction = class {
|
|
|
78660
78804
|
resetForTurn() {
|
|
78661
78805
|
this.compactionCountInTurn = 0;
|
|
78662
78806
|
this.consecutiveCompactionFailures = 0;
|
|
78807
|
+
this.lowWaterMark = 0;
|
|
78808
|
+
this.reactiveAttempted = false;
|
|
78663
78809
|
}
|
|
78664
78810
|
async handleOverflowError(signal, error) {
|
|
78811
|
+
if (this.reactiveAttempted) throw error;
|
|
78665
78812
|
if (!this.beginAutoCompaction(false) && !this.compacting) {
|
|
78666
78813
|
if (this.consecutiveCompactionFailures >= MAX_CONSECUTIVE_FAILURES) this.agent.emitEvent({
|
|
78667
78814
|
type: "warning",
|
|
@@ -78670,11 +78817,12 @@ var FullCompaction = class {
|
|
|
78670
78817
|
});
|
|
78671
78818
|
throw error;
|
|
78672
78819
|
}
|
|
78820
|
+
this.reactiveAttempted = true;
|
|
78673
78821
|
}
|
|
78674
78822
|
async beforeStep(signal) {
|
|
78675
78823
|
this.agent.microCompaction.detect();
|
|
78676
78824
|
const effectiveTokens = this.effectiveTokenCount;
|
|
78677
|
-
const isReactiveTrigger = this.strategy.shouldCompact(effectiveTokens);
|
|
78825
|
+
const isReactiveTrigger = this.strategy.shouldCompact(effectiveTokens) && effectiveTokens >= this.lowWaterMark;
|
|
78678
78826
|
const isProactiveTrigger = !isReactiveTrigger && this.strategy.shouldCompactProactively(effectiveTokens, this.estimatedMaxOutputTokens);
|
|
78679
78827
|
if (isReactiveTrigger) this.checkAutoCompaction();
|
|
78680
78828
|
else if (isProactiveTrigger) this.beginAutoCompaction();
|
|
@@ -78697,7 +78845,9 @@ var FullCompaction = class {
|
|
|
78697
78845
|
}
|
|
78698
78846
|
checkAutoCompaction(throwOnLimit = true) {
|
|
78699
78847
|
if (this.compacting) return true;
|
|
78700
|
-
|
|
78848
|
+
const effectiveTokens = this.effectiveTokenCount;
|
|
78849
|
+
if (!this.strategy.shouldCompact(effectiveTokens)) return false;
|
|
78850
|
+
if (effectiveTokens < this.lowWaterMark) return false;
|
|
78701
78851
|
return this.beginAutoCompaction(throwOnLimit);
|
|
78702
78852
|
}
|
|
78703
78853
|
beginAutoCompaction(throwOnLimit = true) {
|
|
@@ -78744,32 +78894,52 @@ var FullCompaction = class {
|
|
|
78744
78894
|
const originalHistory = [...this.agent.context.history];
|
|
78745
78895
|
const tokensBefore = estimateTokensForMessages(originalHistory);
|
|
78746
78896
|
const model = this.agent.config.model;
|
|
78897
|
+
const isUpdate = extractPreviousSummary(originalHistory) !== null;
|
|
78747
78898
|
let retryCount = 0;
|
|
78748
78899
|
try {
|
|
78749
78900
|
await this.triggerPreCompactHook(data, tokensBefore, signal);
|
|
78750
78901
|
const delays = retryBackoffDelays(5);
|
|
78751
|
-
|
|
78752
|
-
|
|
78753
|
-
while (true) {
|
|
78754
|
-
const messagesToCompact = originalHistory.slice(0, compactedCount);
|
|
78902
|
+
const summarizeOnce = async (messagesToCompact) => {
|
|
78903
|
+
const instruction = isUpdate ? COMPACTION_UPDATE_INSTRUCTION(data.instruction) : COMPACTION_INSTRUCTION(data.instruction);
|
|
78755
78904
|
const messages = [...project(messagesToCompact), {
|
|
78756
78905
|
role: "user",
|
|
78757
78906
|
content: [{
|
|
78758
78907
|
type: "text",
|
|
78759
|
-
text:
|
|
78908
|
+
text: instruction
|
|
78760
78909
|
}],
|
|
78761
78910
|
toolCalls: []
|
|
78762
78911
|
}];
|
|
78763
|
-
|
|
78912
|
+
const response = await this.agent.generate(this.agent.config.provider, COMPACTION_SYSTEM_PROMPT, [], messages, void 0, { signal });
|
|
78913
|
+
if (response.finishReason === "truncated") throw new TruncatedError();
|
|
78914
|
+
return {
|
|
78915
|
+
summary: extractCompactionSummary(response, model),
|
|
78916
|
+
usage: response.usage
|
|
78917
|
+
};
|
|
78918
|
+
};
|
|
78919
|
+
let usage;
|
|
78920
|
+
let summary;
|
|
78921
|
+
while (true) {
|
|
78922
|
+
const messagesToCompact = originalHistory.slice(0, compactedCount);
|
|
78764
78923
|
try {
|
|
78765
|
-
const
|
|
78766
|
-
|
|
78767
|
-
|
|
78768
|
-
summary = extractCompactionSummary(response, model);
|
|
78924
|
+
const result = await summarizeOnce(messagesToCompact);
|
|
78925
|
+
usage = result.usage;
|
|
78926
|
+
summary = result.summary;
|
|
78769
78927
|
break;
|
|
78770
78928
|
} catch (error) {
|
|
78771
|
-
if (error instanceof APIContextOverflowError || error instanceof TruncatedError)
|
|
78772
|
-
|
|
78929
|
+
if (error instanceof APIContextOverflowError || error instanceof TruncatedError) {
|
|
78930
|
+
const reduced = this.strategy.reduceCompactOnOverflow(messagesToCompact);
|
|
78931
|
+
if (reduced < compactedCount) compactedCount = reduced;
|
|
78932
|
+
else {
|
|
78933
|
+
this.agent.log.warn("compaction overflow at minimum split, falling back to re-summarize", {
|
|
78934
|
+
compactedCount,
|
|
78935
|
+
tokensBefore: estimateTokensForMessages(messagesToCompact)
|
|
78936
|
+
});
|
|
78937
|
+
const result = await summarizeWithFallback(messagesToCompact, summarizeOnce);
|
|
78938
|
+
summary = result.summary;
|
|
78939
|
+
usage = result.usage;
|
|
78940
|
+
break;
|
|
78941
|
+
}
|
|
78942
|
+
} else if (!isRetryableGenerateError(error)) throw error;
|
|
78773
78943
|
if (retryCount + 1 >= 5) throw error;
|
|
78774
78944
|
await sleepForRetry(delays[retryCount], signal);
|
|
78775
78945
|
retryCount += 1;
|
|
@@ -78782,12 +78952,17 @@ var FullCompaction = class {
|
|
|
78782
78952
|
return;
|
|
78783
78953
|
}
|
|
78784
78954
|
const recent = originalHistory.slice(compactedCount);
|
|
78785
|
-
const
|
|
78955
|
+
const messagesToCompactForOps = originalHistory.slice(0, compactedCount);
|
|
78956
|
+
const fileOps = createFileOps();
|
|
78957
|
+
for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
|
|
78958
|
+
const processedSummary = this.postProcessSummary(summary, fileOps);
|
|
78959
|
+
const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
|
|
78786
78960
|
const result = {
|
|
78787
|
-
summary,
|
|
78961
|
+
summary: processedSummary,
|
|
78788
78962
|
compactedCount,
|
|
78789
78963
|
tokensBefore,
|
|
78790
|
-
tokensAfter
|
|
78964
|
+
tokensAfter,
|
|
78965
|
+
...isUpdate ? { isUpdate: true } : {}
|
|
78791
78966
|
};
|
|
78792
78967
|
this.markCompleted();
|
|
78793
78968
|
this.agent.emitEvent({
|
|
@@ -78795,7 +78970,8 @@ var FullCompaction = class {
|
|
|
78795
78970
|
result
|
|
78796
78971
|
});
|
|
78797
78972
|
this.agent.context.applyCompaction(result);
|
|
78798
|
-
|
|
78973
|
+
this.lowWaterMark = Math.floor(this.effectiveTokenCount * 1.1);
|
|
78974
|
+
await this.extractAndStoreMemos(processedSummary);
|
|
78799
78975
|
this.triggerPostCompactHook(data, result);
|
|
78800
78976
|
this.consecutiveCompactionFailures = 0;
|
|
78801
78977
|
this._shouldInjectSessionSummary = true;
|
|
@@ -78887,22 +79063,27 @@ var FullCompaction = class {
|
|
|
78887
79063
|
});
|
|
78888
79064
|
}
|
|
78889
79065
|
/**
|
|
78890
|
-
* Append the current todo list as
|
|
78891
|
-
* summary so active tasks
|
|
78892
|
-
*
|
|
78893
|
-
*
|
|
79066
|
+
* Append the current todo list and file operations as markdown sections to
|
|
79067
|
+
* the compaction summary so active tasks and file context survive
|
|
79068
|
+
* compression. Without this, both are lost after compaction because the
|
|
79069
|
+
* original messages containing them are removed from the context window.
|
|
78894
79070
|
*/
|
|
78895
|
-
postProcessSummary(summary) {
|
|
79071
|
+
postProcessSummary(summary, fileOps) {
|
|
78896
79072
|
const todos = this.agent.tools.storeData()["todo"] ?? [];
|
|
78897
|
-
|
|
78898
|
-
|
|
78899
|
-
|
|
78900
|
-
"",
|
|
78901
|
-
...todos.map((t) => {
|
|
79073
|
+
const sections = [summary.trim()];
|
|
79074
|
+
if (todos.length > 0) {
|
|
79075
|
+
const lines = todos.map((t) => {
|
|
78902
79076
|
return `- [${t.status === "done" ? "x" : t.status === "in_progress" ? "-" : " "}] ${t.title}`;
|
|
78903
|
-
})
|
|
78904
|
-
|
|
78905
|
-
|
|
79077
|
+
});
|
|
79078
|
+
sections.push([
|
|
79079
|
+
"## TODO List",
|
|
79080
|
+
"",
|
|
79081
|
+
...lines
|
|
79082
|
+
].join("\n"));
|
|
79083
|
+
}
|
|
79084
|
+
const filesSection = formatFileOperations(fileOps);
|
|
79085
|
+
if (filesSection.length > 0) sections.push(filesSection);
|
|
79086
|
+
return sections.join("\n\n");
|
|
78906
79087
|
}
|
|
78907
79088
|
};
|
|
78908
79089
|
function extractCompactionSummary(response, model) {
|
|
@@ -78911,6 +79092,18 @@ function extractCompactionSummary(response, model) {
|
|
|
78911
79092
|
return summary;
|
|
78912
79093
|
}
|
|
78913
79094
|
const COMPACTION_INSTRUCTION = (customInstruction = "") => renderPrompt(compaction_instruction_default, { customInstruction });
|
|
79095
|
+
const COMPACTION_UPDATE_INSTRUCTION = (customInstruction = "") => renderPrompt(compaction_update_instruction_default, { customInstruction });
|
|
79096
|
+
/**
|
|
79097
|
+
* If history starts with a compaction_summary message, return its text so the
|
|
79098
|
+
* next compaction can merge new content into it instead of starting fresh.
|
|
79099
|
+
* Returns null when no prior summary exists (first compaction in the session).
|
|
79100
|
+
*/
|
|
79101
|
+
function extractPreviousSummary(history) {
|
|
79102
|
+
const head = history[0];
|
|
79103
|
+
if (head?.origin?.kind !== "compaction_summary") return null;
|
|
79104
|
+
const text = head.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
79105
|
+
return text.length > 0 ? text : null;
|
|
79106
|
+
}
|
|
78914
79107
|
//#endregion
|
|
78915
79108
|
//#region ../../packages/agent-core/src/flags/registry.ts
|
|
78916
79109
|
/**
|
|
@@ -78993,11 +79186,33 @@ const flags = new FlagResolver();
|
|
|
78993
79186
|
//#region ../../packages/agent-core/src/agent/compaction/micro.ts
|
|
78994
79187
|
const DEFAULT_CONFIG = {
|
|
78995
79188
|
keepRecentMessages: 20,
|
|
79189
|
+
keepRecentTokens: 4e4,
|
|
79190
|
+
pruneMinReclaimTokens: 2e4,
|
|
78996
79191
|
minContentTokens: 100,
|
|
78997
79192
|
minContextUsageRatio: .5,
|
|
78998
|
-
truncatedMarker: "[Old tool result content cleared]"
|
|
79193
|
+
truncatedMarker: "[Old tool result content cleared]",
|
|
79194
|
+
uselessMarker: "[Uneventful result elided]"
|
|
78999
79195
|
};
|
|
79000
79196
|
/**
|
|
79197
|
+
* Compute the cutoff index: everything at index < cutoff is eligible for
|
|
79198
|
+
* truncation. The default floor is `keepRecentMessages` (the message-count
|
|
79199
|
+
* protection window). But if the trailing window exceeds `keepRecentTokens`,
|
|
79200
|
+
* the cutoff walks forward (toward the tail) until the window fits — so a
|
|
79201
|
+
* few giant tool results can't pin the cutoff behind them and starve the
|
|
79202
|
+
* prefix of reclaimable content.
|
|
79203
|
+
*/
|
|
79204
|
+
function computeCutoff(messages, config) {
|
|
79205
|
+
const messageFloor = Math.max(0, messages.length - config.keepRecentMessages);
|
|
79206
|
+
let windowTokens = estimateTokensForMessages(messages.slice(messageFloor));
|
|
79207
|
+
let cutoff = messageFloor;
|
|
79208
|
+
while (cutoff < messages.length && windowTokens > config.keepRecentTokens) {
|
|
79209
|
+
const removed = messages[cutoff];
|
|
79210
|
+
windowTokens -= estimateTokensForMessages([removed]);
|
|
79211
|
+
cutoff += 1;
|
|
79212
|
+
}
|
|
79213
|
+
return cutoff;
|
|
79214
|
+
}
|
|
79215
|
+
/**
|
|
79001
79216
|
* Walk the message list and find Read tool calls whose file paths were
|
|
79002
79217
|
* superseded by a later Read of the same path. Returns a map from the
|
|
79003
79218
|
* superseded tool call's ID to the file path (for the marker text).
|
|
@@ -79047,8 +79262,8 @@ var MicroCompaction = class {
|
|
|
79047
79262
|
};
|
|
79048
79263
|
}
|
|
79049
79264
|
/** Reset the internal cutoff line (e.g. after a full compaction). */
|
|
79050
|
-
reset(
|
|
79051
|
-
this.cutoff =
|
|
79265
|
+
reset() {
|
|
79266
|
+
this.cutoff = 0;
|
|
79052
79267
|
}
|
|
79053
79268
|
/** Advance the cutoff line and log the change. */
|
|
79054
79269
|
apply(cutoff) {
|
|
@@ -79066,14 +79281,19 @@ var MicroCompaction = class {
|
|
|
79066
79281
|
const maxContextTokens = this.agent.config.modelCapabilities.max_context_tokens;
|
|
79067
79282
|
const contextTokens = this.agent.context.tokenCountWithPending;
|
|
79068
79283
|
if ((maxContextTokens !== void 0 && maxContextTokens > 0 ? contextTokens / maxContextTokens : 0) < config.minContextUsageRatio) return;
|
|
79069
|
-
const nextCutoff =
|
|
79284
|
+
const nextCutoff = computeCutoff(history, config);
|
|
79285
|
+
if (nextCutoff <= this.cutoff) return;
|
|
79286
|
+
const { beforeTokens, afterTokens } = this.measureEffect(history, nextCutoff);
|
|
79287
|
+
if (beforeTokens - afterTokens < config.pruneMinReclaimTokens) return;
|
|
79070
79288
|
this.apply(nextCutoff);
|
|
79071
79289
|
}
|
|
79072
79290
|
/**
|
|
79073
79291
|
* Apply micro-compaction to a message list: replace old tool results
|
|
79074
79292
|
* before the cutoff line with truncated markers. Read results for files
|
|
79075
79293
|
* that were re-read later get a supersede marker so the model knows
|
|
79076
|
-
* the old content is stale.
|
|
79294
|
+
* the old content is stale. Tool results explicitly marked useless are
|
|
79295
|
+
* elided with a short notice regardless of size, since they carry no
|
|
79296
|
+
* actionable information.
|
|
79077
79297
|
*/
|
|
79078
79298
|
compact(messages) {
|
|
79079
79299
|
const config = this.config;
|
|
@@ -79081,7 +79301,16 @@ var MicroCompaction = class {
|
|
|
79081
79301
|
const result = [];
|
|
79082
79302
|
let i = 0;
|
|
79083
79303
|
for (const msg of messages) {
|
|
79084
|
-
|
|
79304
|
+
const isUseless = i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && msg.useless === true;
|
|
79305
|
+
const isOversizedTruncatable = i < this.cutoff && msg.role === "tool" && msg.toolCallId !== void 0 && estimateTokensForMessages([msg]) >= config.minContentTokens;
|
|
79306
|
+
if (isUseless) result.push({
|
|
79307
|
+
...msg,
|
|
79308
|
+
content: [{
|
|
79309
|
+
type: "text",
|
|
79310
|
+
text: config.uselessMarker
|
|
79311
|
+
}]
|
|
79312
|
+
});
|
|
79313
|
+
else if (isOversizedTruncatable) {
|
|
79085
79314
|
const marker = msg.toolCallId !== void 0 && superseded.has(msg.toolCallId) ? `[Superseded by a newer read of ${superseded.get(msg.toolCallId)}]` : config.truncatedMarker;
|
|
79086
79315
|
result.push({
|
|
79087
79316
|
...msg,
|
|
@@ -79106,6 +79335,7 @@ var MicroCompaction = class {
|
|
|
79106
79335
|
}
|
|
79107
79336
|
measureEffect(messages, cutoff) {
|
|
79108
79337
|
let markerTokenCount;
|
|
79338
|
+
let uselessMarkerTokenCount;
|
|
79109
79339
|
let truncatedToolResultCount = 0;
|
|
79110
79340
|
let beforeTokens = 0;
|
|
79111
79341
|
let afterTokens = 0;
|
|
@@ -79113,11 +79343,19 @@ var MicroCompaction = class {
|
|
|
79113
79343
|
const message = messages[i];
|
|
79114
79344
|
if (message?.role !== "tool" || message.toolCallId === void 0) continue;
|
|
79115
79345
|
const contentTokens = estimateTokensForMessages([message]);
|
|
79116
|
-
|
|
79117
|
-
|
|
79118
|
-
|
|
79119
|
-
|
|
79120
|
-
|
|
79346
|
+
const isUseless = message.useless === true;
|
|
79347
|
+
if (!isUseless && contentTokens < this.config.minContentTokens) continue;
|
|
79348
|
+
if (isUseless) {
|
|
79349
|
+
uselessMarkerTokenCount ??= estimateTokens$1(this.config.uselessMarker);
|
|
79350
|
+
truncatedToolResultCount += 1;
|
|
79351
|
+
beforeTokens += contentTokens;
|
|
79352
|
+
afterTokens += uselessMarkerTokenCount;
|
|
79353
|
+
} else {
|
|
79354
|
+
markerTokenCount ??= estimateTokens$1(this.config.truncatedMarker);
|
|
79355
|
+
truncatedToolResultCount += 1;
|
|
79356
|
+
beforeTokens += contentTokens;
|
|
79357
|
+
afterTokens += markerTokenCount;
|
|
79358
|
+
}
|
|
79121
79359
|
}
|
|
79122
79360
|
return {
|
|
79123
79361
|
truncatedToolResultCount,
|
|
@@ -80326,7 +80564,8 @@ var ContextMemory = class {
|
|
|
80326
80564
|
this.pushHistory({
|
|
80327
80565
|
...message,
|
|
80328
80566
|
role: "tool",
|
|
80329
|
-
isError: event.result.isError
|
|
80567
|
+
isError: event.result.isError,
|
|
80568
|
+
useless: event.result.isError !== true && event.result.useless === true ? true : void 0
|
|
80330
80569
|
});
|
|
80331
80570
|
this.pendingToolResultIds.delete(event.toolCallId);
|
|
80332
80571
|
this.flushDeferredMessagesIfToolExchangeClosed();
|
|
@@ -122014,7 +122253,7 @@ function optionalBuildString(value) {
|
|
|
122014
122253
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
122015
122254
|
}
|
|
122016
122255
|
const SCREAM_BUILD_INFO = {
|
|
122017
|
-
version: optionalBuildString("0.8.
|
|
122256
|
+
version: optionalBuildString("0.8.3"),
|
|
122018
122257
|
channel: optionalBuildString(""),
|
|
122019
122258
|
commit: optionalBuildString(""),
|
|
122020
122259
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -122173,7 +122412,7 @@ function errorMessage(error) {
|
|
|
122173
122412
|
//#region src/cli/commands.ts
|
|
122174
122413
|
function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}, onStreamJson = () => {}, onChannelSetup = () => {}) {
|
|
122175
122414
|
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);
|
|
122415
|
+
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
122416
|
registerExportCommand(program);
|
|
122178
122417
|
registerMigrateCommand(program, onMigrate);
|
|
122179
122418
|
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 +122443,7 @@ function createProgram(version, onMain, onMigrate, onPluginNodeRunner = () => {}
|
|
|
122204
122443
|
yolo: yoloValue,
|
|
122205
122444
|
auto: autoValue,
|
|
122206
122445
|
plan: raw["plan"],
|
|
122446
|
+
wolfpack: raw["wolfpack"],
|
|
122207
122447
|
model: raw["model"],
|
|
122208
122448
|
outputFormat: raw["outputFormat"],
|
|
122209
122449
|
prompt: raw["prompt"],
|
|
@@ -122229,12 +122469,14 @@ function validateOptions(opts) {
|
|
|
122229
122469
|
if (promptMode && opts.yolo) throw new OptionConflictError("--prompt 不能与 --yolo 同时使用。");
|
|
122230
122470
|
if (promptMode && opts.auto) throw new OptionConflictError("--prompt 不能与 --auto 同时使用。");
|
|
122231
122471
|
if (promptMode && opts.plan) throw new OptionConflictError("--prompt 不能与 --plan 同时使用。");
|
|
122472
|
+
if (promptMode && opts.wolfpack) throw new OptionConflictError("--prompt 不能与 --wolfpack 同时使用。");
|
|
122232
122473
|
if (promptMode && opts.session === "") throw new OptionConflictError("在提示模式下不能使用不带 ID 的 --session。");
|
|
122233
122474
|
if (opts.continue && opts.session !== void 0) throw new OptionConflictError("--continue 和 --session 不能同时使用。");
|
|
122234
122475
|
if (opts.yolo && opts.auto) throw new OptionConflictError("--yolo 不能与 --auto 同时使用。");
|
|
122235
122476
|
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.yolo) throw new OptionConflictError("--yolo 不能与 --continue 或 --session 同时使用。");
|
|
122236
122477
|
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.auto) throw new OptionConflictError("--auto 不能与 --continue 或 --session 同时使用。");
|
|
122237
122478
|
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.plan) throw new OptionConflictError("--plan 不能与 --continue 或 --session 同时使用。");
|
|
122479
|
+
if (!promptMode && (opts.continue || opts.session !== void 0) && opts.wolfpack) throw new OptionConflictError("--wolfpack 不能与 --continue 或 --session 同时使用。");
|
|
122238
122480
|
return {
|
|
122239
122481
|
options: opts,
|
|
122240
122482
|
uiMode: promptMode ? "print" : "shell"
|
|
@@ -132838,6 +133080,517 @@ async function handleLikeCommand(host) {
|
|
|
132838
133080
|
host.showStatus("偏好已保存(下次新会话生效)", host.state.theme.colors.success);
|
|
132839
133081
|
}
|
|
132840
133082
|
//#endregion
|
|
133083
|
+
//#region src/tui/utils/open-url.ts
|
|
133084
|
+
function openUrl(url) {
|
|
133085
|
+
const command = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", [
|
|
133086
|
+
"/c",
|
|
133087
|
+
"start",
|
|
133088
|
+
"",
|
|
133089
|
+
url
|
|
133090
|
+
]] : ["xdg-open", [url]];
|
|
133091
|
+
execFile(command[0], command[1], () => {});
|
|
133092
|
+
}
|
|
133093
|
+
//#endregion
|
|
133094
|
+
//#region src/tui/commands/knowledge-store.ts
|
|
133095
|
+
let knowledgeStoreInstance;
|
|
133096
|
+
async function getKnowledgeStore() {
|
|
133097
|
+
if (knowledgeStoreInstance === void 0) {
|
|
133098
|
+
knowledgeStoreInstance = new KnowledgeStore(getDataDir());
|
|
133099
|
+
await knowledgeStoreInstance.init();
|
|
133100
|
+
knowledgeStoreInstance.setEmbeddingEngine(createFastEmbedEngine());
|
|
133101
|
+
}
|
|
133102
|
+
return knowledgeStoreInstance;
|
|
133103
|
+
}
|
|
133104
|
+
//#endregion
|
|
133105
|
+
//#region src/tui/commands/knowledge-web.ts
|
|
133106
|
+
/**
|
|
133107
|
+
* /knowledge web — start a local HTTP server and open the browser
|
|
133108
|
+
* to display an interactive Cytoscape.js knowledge graph visualization.
|
|
133109
|
+
*/
|
|
133110
|
+
const activeServers = /* @__PURE__ */ new Set();
|
|
133111
|
+
function registerServer(server) {
|
|
133112
|
+
activeServers.add(server);
|
|
133113
|
+
server.on("close", () => {
|
|
133114
|
+
activeServers.delete(server);
|
|
133115
|
+
});
|
|
133116
|
+
}
|
|
133117
|
+
function closeAllServers() {
|
|
133118
|
+
for (const server of activeServers) server.close();
|
|
133119
|
+
}
|
|
133120
|
+
process.on("exit", closeAllServers);
|
|
133121
|
+
const HTML = `<!doctype html>
|
|
133122
|
+
<html lang="zh-CN">
|
|
133123
|
+
<head>
|
|
133124
|
+
<meta charset="utf-8">
|
|
133125
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
133126
|
+
<title>Scream 知识图谱</title>
|
|
133127
|
+
<style>
|
|
133128
|
+
*{margin:0;padding:0;box-sizing:border-box}
|
|
133129
|
+
html,body{width:100%;height:100%;overflow:hidden;font-family:Inter,"PingFang SC","Microsoft YaHei",system-ui,sans-serif;background:#fff;color:#18181b}
|
|
133130
|
+
#container{width:100%;height:100%;position:relative;overflow:hidden}
|
|
133131
|
+
svg{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:1}
|
|
133132
|
+
.node{position:absolute;border-radius:10px;display:flex;align-items:center;justify-content:center;text-align:center;cursor:pointer;z-index:2;user-select:none;transition:opacity .25s,box-shadow .25s,border-color .25s;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;padding:0 14px;font-size:12px;line-height:1.3;animation:float 6s ease-in-out infinite}
|
|
133133
|
+
.node.entity{background:#fff;border:1.5px solid #d4d4d8;font-weight:600;color:#1a1a1a;box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
|
133134
|
+
.node.entity.root{border:2px solid #15803d;background:#f0fdf4;box-shadow:0 4px 20px rgba(21,128,61,.15);font-size:14px}
|
|
133135
|
+
.node.entity.expanded{border-color:#15803d;box-shadow:0 2px 12px rgba(21,128,61,.1)}
|
|
133136
|
+
.node.event{background:#fafafa;border:1px solid #e5e7eb;font-weight:500;color:#525252;box-shadow:0 1px 4px rgba(0,0,0,.04);border-radius:8px}
|
|
133137
|
+
.node.event.expanded{border-color:#22c55e;background:#f8fef9;box-shadow:0 2px 8px rgba(21,128,61,.08)}
|
|
133138
|
+
.node.selected{border-color:#15803d !important;background:#f0fdf4 !important;box-shadow:0 0 0 3px rgba(21,128,61,.12),0 4px 16px rgba(21,128,61,.14) !important}
|
|
133139
|
+
.node.dimmed{opacity:.12;filter:grayscale(.6)}
|
|
133140
|
+
.node:hover{box-shadow:0 4px 16px rgba(0,0,0,.08) !important}
|
|
133141
|
+
.edge{stroke:#b0b0b0;stroke-width:.8;fill:none;opacity:.6}
|
|
133142
|
+
.edge.animated{stroke:#999;stroke-width:1;stroke-dasharray:6 4;animation:dash 1.2s linear infinite;opacity:.45}
|
|
133143
|
+
.edge.dimmed{stroke:#e0e0e0;stroke-width:.4;opacity:.06}
|
|
133144
|
+
.edge.highlighted{stroke:#15803d;stroke-width:1.8;opacity:1}
|
|
133145
|
+
@keyframes dash{to{stroke-dashoffset:-10}}
|
|
133146
|
+
@keyframes float{0%,100%{transform:translateY(0)}50%{transform:translateY(-5px)}}
|
|
133147
|
+
#toolbar{position:absolute;left:16px;top:16px;z-index:10;display:flex;align-items:center;gap:10px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;padding:8px 18px;box-shadow:0 2px 8px rgba(0,0,0,.04)}
|
|
133148
|
+
#toolbar button{background:#fff;color:#525252;border:1px solid #d4d4d8;padding:5px 16px;border-radius:8px;cursor:pointer;font-size:12px;font-weight:500;transition:all .15s}
|
|
133149
|
+
#toolbar button:hover{background:#f0fdf4;border-color:#15803d;color:#15803d}
|
|
133150
|
+
#toolbar .sep{width:1px;height:16px;background:#e5e7eb}
|
|
133151
|
+
#toolbar .chip{color:#a1a1aa;font-size:11px;letter-spacing:.3px}
|
|
133152
|
+
#toolbar .chip b{color:#15803d;font-size:13px;font-weight:700;margin-left:4px}
|
|
133153
|
+
#hint{position:absolute;left:16px;bottom:16px;z-index:10;color:#c4c4c4;font-size:11px;letter-spacing:.2px}
|
|
133154
|
+
#detail{position:fixed;right:0;top:0;width:380px;height:100vh;background:#fff;border-left:1px solid #e5e7eb;transform:translateX(100%);transition:transform .3s cubic-bezier(.4,0,.2,1);z-index:20;overflow-y:auto;padding:32px 28px;box-shadow:-4px 0 24px rgba(0,0,0,.04)}
|
|
133155
|
+
#detail.open{transform:translateX(0)}
|
|
133156
|
+
#detail h3{font-size:17px;font-weight:700;color:#18181b;margin-bottom:20px;padding-right:32px;line-height:1.4}
|
|
133157
|
+
#detail .field{margin-bottom:18px}
|
|
133158
|
+
#detail .field .label{font-size:10px;color:#15803d;text-transform:uppercase;letter-spacing:1px;font-weight:700;margin-bottom:5px}
|
|
133159
|
+
#detail .field .value{font-size:13px;color:#525252;line-height:1.6}
|
|
133160
|
+
#detail .close{position:absolute;top:20px;right:20px;background:none;border:none;color:#c4c4c4;cursor:pointer;font-size:18px;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:8px;transition:all .15s}
|
|
133161
|
+
#detail .close:hover{color:#18181b;background:#f4f4f5}
|
|
133162
|
+
#detail .conn-item{font-size:13px;color:#15803d;cursor:pointer;padding:7px 10px;border-radius:8px;transition:all .15s;border:1px solid transparent}
|
|
133163
|
+
#detail .conn-item:hover{background:#f0fdf4;border-color:#bbf7d0}
|
|
133164
|
+
#detail .badge{font-size:10px;color:#15803d;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:5px;padding:1px 7px;margin-left:8px;font-weight:500}
|
|
133165
|
+
#detail .back-btn{display:inline-flex;align-items:center;gap:5px;background:none;border:1px solid #d4d4d8;color:#525252;padding:6px 14px;border-radius:8px;cursor:pointer;font-size:12px;font-weight:500;margin-bottom:16px;transition:all .15s}
|
|
133166
|
+
#detail .back-btn:hover{background:#f0fdf4;border-color:#15803d;color:#15803d}
|
|
133167
|
+
#detail .type-tag{display:inline-block;font-size:10px;color:#15803d;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:5px;padding:2px 10px;font-weight:600;margin-bottom:16px;letter-spacing:.3px}
|
|
133168
|
+
#detail .divider{height:1px;background:#f0f0f0;margin:18px 0}
|
|
133169
|
+
#loading{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:30;color:#15803d;font-size:14px;font-weight:500;display:flex;align-items:center;gap:8px}
|
|
133170
|
+
#loading .dot{width:6px;height:6px;background:#15803d;border-radius:50%;animation:blink 1.2s ease-in-out infinite}
|
|
133171
|
+
#loading .dot:nth-child(2){animation-delay:.2s}
|
|
133172
|
+
#loading .dot:nth-child(3){animation-delay:.4s}
|
|
133173
|
+
@keyframes blink{0%,80%,100%{opacity:.2}40%{opacity:1}}
|
|
133174
|
+
</style>
|
|
133175
|
+
</head>
|
|
133176
|
+
<body>
|
|
133177
|
+
<div id="container"><svg id="edges-svg"></svg></div>
|
|
133178
|
+
<div id="toolbar">
|
|
133179
|
+
<span class="chip">实体<b id="stat-ent">0</b></span>
|
|
133180
|
+
<span class="chip">事件<b id="stat-evt">0</b></span>
|
|
133181
|
+
<span class="chip">关系<b id="stat-edg">0</b></span>
|
|
133182
|
+
<span class="sep"></span>
|
|
133183
|
+
<button id="btn-reset">重置</button>
|
|
133184
|
+
<button id="btn-expand">展开全部</button>
|
|
133185
|
+
</div>
|
|
133186
|
+
<div id="hint">单击展开/收起 · 双击查看详情 · 滚轮缩放 · 拖拽平移</div>
|
|
133187
|
+
<div id="detail"><button class="close" id="btn-close">×</button><div id="detail-body"></div></div>
|
|
133188
|
+
<div id="loading"><span class="dot"></span><span class="dot"></span><span class="dot"></span>加载知识图谱...</div>
|
|
133189
|
+
<script>
|
|
133190
|
+
(function(){
|
|
133191
|
+
var graphData;
|
|
133192
|
+
var container = document.getElementById('container');
|
|
133193
|
+
var svg = document.getElementById('edges-svg');
|
|
133194
|
+
|
|
133195
|
+
// camera
|
|
133196
|
+
var cam = {x:0,y:0,zoom:1};
|
|
133197
|
+
var W=window.innerWidth, H=window.innerHeight;
|
|
133198
|
+
|
|
133199
|
+
function w2s(wx,wy){return{x:(wx-cam.x)*cam.zoom+W/2,y:(wy-cam.y)*cam.zoom+H/2}}
|
|
133200
|
+
function s2w(sx,sy){return{x:(sx-W/2)/cam.zoom+cam.x,y:(sy-H/2)/cam.zoom+cam.y}}
|
|
133201
|
+
|
|
133202
|
+
// pan & zoom
|
|
133203
|
+
var drag=false,moved=false,dragX,dragY;
|
|
133204
|
+
container.addEventListener('mousedown',function(e){if(e.target!==container&&e.target!==svg)return;drag=true;moved=false;dragX=e.clientX;dragY=e.clientY});
|
|
133205
|
+
window.addEventListener('mousemove',function(e){if(!drag)return;var dx=e.clientX-dragX,dy=e.clientY-dragY;if(Math.abs(dx)>2||Math.abs(dy)>2)moved=true;cam.x-=dx/cam.zoom;cam.y-=dy/cam.zoom;dragX=e.clientX;dragY=e.clientY;render()});
|
|
133206
|
+
window.addEventListener('mouseup',function(){drag=false});
|
|
133207
|
+
container.addEventListener('wheel',function(e){e.preventDefault();var pt=s2w(e.clientX,e.clientY);var f=e.deltaY<0?1.12:1/1.12;cam.zoom=Math.max(.15,Math.min(4,cam.zoom*f));cam.x=pt.x-(e.clientX-W/2)/cam.zoom;cam.y=pt.y-(e.clientY-H/2)/cam.zoom;render()},{passive:false});
|
|
133208
|
+
window.addEventListener('resize',function(){W=window.innerWidth;H=window.innerHeight;render()});
|
|
133209
|
+
|
|
133210
|
+
// layout constants (SAG)
|
|
133211
|
+
var EW=160,EH=40,EVW=170,EVH=40;
|
|
133212
|
+
var ER_START=220,ER_GAP=190,ER_SLOT=200;
|
|
133213
|
+
var VR_START=500,VR_GAP=180,VR_SLOT=220;
|
|
133214
|
+
var ROOT_LIMIT=8;
|
|
133215
|
+
var GA=Math.PI*(3-Math.sqrt(5));
|
|
133216
|
+
|
|
133217
|
+
// state
|
|
133218
|
+
var eById={},evById={};
|
|
133219
|
+
var evByEnt={},entByEv={};
|
|
133220
|
+
var expEnt=new Set(),expEv=new Set();
|
|
133221
|
+
var selId=null;
|
|
133222
|
+
var pos={};
|
|
133223
|
+
var navStack=[];
|
|
133224
|
+
|
|
133225
|
+
// node/edge DOM elements
|
|
133226
|
+
var nodeEls={},edgeEls=[];
|
|
133227
|
+
|
|
133228
|
+
fetch('/api/graph').then(function(r){return r.json()}).then(function(data){
|
|
133229
|
+
graphData=data;
|
|
133230
|
+
document.getElementById('stat-ent').textContent=data.entities.length;
|
|
133231
|
+
document.getElementById('stat-evt').textContent=data.events.length;
|
|
133232
|
+
document.getElementById('stat-edg').textContent=data.edges.length;
|
|
133233
|
+
document.getElementById('loading').style.display='none';
|
|
133234
|
+
|
|
133235
|
+
data.entities.sort(function(a,b){return(b.eventCount||0)-(a.eventCount||0)||a.name.localeCompare(b.name)});
|
|
133236
|
+
data.events.sort(function(a,b){return(a.rank||0)-(b.rank||0)||a.title.localeCompare(b.title)});
|
|
133237
|
+
|
|
133238
|
+
data.entities.forEach(function(e){eById[e.id]=e});
|
|
133239
|
+
data.events.forEach(function(e){evById[e.id]=e});
|
|
133240
|
+
|
|
133241
|
+
data.edges.forEach(function(e){
|
|
133242
|
+
if(!evByEnt[e.entityId])evByEnt[e.entityId]=[];
|
|
133243
|
+
evByEnt[e.entityId].push(e.eventId);
|
|
133244
|
+
if(!entByEv[e.eventId])entByEv[e.eventId]=[];
|
|
133245
|
+
entByEv[e.eventId].push(e.entityId);
|
|
133246
|
+
});
|
|
133247
|
+
|
|
133248
|
+
// default: show all entities, no events expanded
|
|
133249
|
+
graphData.entities.forEach(function(e){expEnt.add(e.id)});
|
|
133250
|
+
|
|
133251
|
+
buildPositions();
|
|
133252
|
+
fitView();
|
|
133253
|
+
rebuildDOM();
|
|
133254
|
+
});
|
|
133255
|
+
|
|
133256
|
+
function buildPositions(){
|
|
133257
|
+
pos={};
|
|
133258
|
+
var root=graphData.entities[0];
|
|
133259
|
+
if(root)pos[root.id]={x:-EW/2,y:-EH/2,root:true};
|
|
133260
|
+
var sec=graphData.entities.slice(1);
|
|
133261
|
+
placeRings(sec.map(function(e){return e.id}),ER_START,ER_GAP,ER_SLOT,EW,EH,-Math.PI/2);
|
|
133262
|
+
placeEvents(graphData.events,-Math.PI/2+Math.PI/12);
|
|
133263
|
+
}
|
|
133264
|
+
|
|
133265
|
+
function placeRings(ids,sr,gap,slot,nw,nh,aoff){
|
|
133266
|
+
var idx=0,ring=0;
|
|
133267
|
+
while(idx<ids.length){
|
|
133268
|
+
var r=sr+ring*gap;
|
|
133269
|
+
var cap=Math.max(6,Math.floor(2*Math.PI*r/slot));
|
|
133270
|
+
for(var s=0;s<cap&&idx<ids.length;s++){
|
|
133271
|
+
var a=aoff+(2*Math.PI*s)/cap;
|
|
133272
|
+
pos[ids[idx]]={x:Math.cos(a)*r-nw/2,y:Math.sin(a)*r-nh/2};
|
|
133273
|
+
idx++;
|
|
133274
|
+
}
|
|
133275
|
+
ring++;
|
|
133276
|
+
}
|
|
133277
|
+
}
|
|
133278
|
+
|
|
133279
|
+
function placeEvents(events,aoff){
|
|
133280
|
+
var occ={};
|
|
133281
|
+
for(var i=0;i<events.length;i++){
|
|
133282
|
+
var da=i*GA;
|
|
133283
|
+
var sl=findSlot(da,aoff,occ);
|
|
133284
|
+
var r=VR_START+sl.ring*VR_GAP;
|
|
133285
|
+
var a=aoff+(2*Math.PI*sl.idx)/sl.cap;
|
|
133286
|
+
pos[events[i].id]={x:Math.cos(a)*r-EVW/2,y:Math.sin(a)*r-EVH/2};
|
|
133287
|
+
}
|
|
133288
|
+
}
|
|
133289
|
+
|
|
133290
|
+
function findSlot(da,aoff,occ){
|
|
133291
|
+
var ring=0;
|
|
133292
|
+
while(true){
|
|
133293
|
+
var r=VR_START+ring*VR_GAP;
|
|
133294
|
+
var cap=Math.max(8,Math.floor(2*Math.PI*r/VR_SLOT));
|
|
133295
|
+
var o=occ[ring]||{};
|
|
133296
|
+
var ds=((Math.round(((da-aoff)/(2*Math.PI))*cap)%cap)+cap)%cap;
|
|
133297
|
+
var f=nearFree(ds,cap,o);
|
|
133298
|
+
if(f!=null){o[f]=true;occ[ring]=o;return{ring:ring,idx:f,cap:cap}}
|
|
133299
|
+
ring++;
|
|
133300
|
+
}
|
|
133301
|
+
}
|
|
133302
|
+
|
|
133303
|
+
function nearFree(d,cap,o){
|
|
133304
|
+
for(var i=0;i<cap;i++){
|
|
133305
|
+
var cs=i===0?[d]:[((d-i)%cap+cap)%cap,((d+i)%cap+cap)%cap];
|
|
133306
|
+
for(var j=0;j<cs.length;j++)if(!o[cs[j]])return cs[j];
|
|
133307
|
+
}
|
|
133308
|
+
return null;
|
|
133309
|
+
}
|
|
133310
|
+
|
|
133311
|
+
function fitView(){
|
|
133312
|
+
var vis=getVisible();
|
|
133313
|
+
var ids=[];
|
|
133314
|
+
vis.entities.forEach(function(id){ids.push(id)});
|
|
133315
|
+
vis.events.forEach(function(id){ids.push(id)});
|
|
133316
|
+
if(!ids.length){cam.x=0;cam.y=0;cam.zoom=1;return}
|
|
133317
|
+
var x0=Infinity,x1=-Infinity,y0=Infinity,y1=-Infinity;
|
|
133318
|
+
ids.forEach(function(id){var p=pos[id];if(!p)return;var w=eById[id]?EW:EVW,h=eById[id]?EH:EVH;if(p.x<x0)x0=p.x;if(p.x+w>x1)x1=p.x+w;if(p.y<y0)y0=p.y;if(p.y+h>y1)y1=p.y+h});
|
|
133319
|
+
cam.x=(x0+x1)/2;cam.y=(y0+y1)/2;
|
|
133320
|
+
cam.zoom=Math.min((W-120)/(x1-x0),(H-120)/(y1-y0),1.2);
|
|
133321
|
+
}
|
|
133322
|
+
|
|
133323
|
+
function getVisible(){
|
|
133324
|
+
var ve=new Set(),vv=new Set();
|
|
133325
|
+
graphData.entities.slice(0,ROOT_LIMIT).forEach(function(e){ve.add(e.id)});
|
|
133326
|
+
expEnt.forEach(function(eid){ve.add(eid);(evByEnt[eid]||[]).forEach(function(v){vv.add(v)})});
|
|
133327
|
+
expEv.forEach(function(eid){vv.add(eid);(entByEv[eid]||[]).forEach(function(e){ve.add(e)})});
|
|
133328
|
+
return{entities:ve,events:vv};
|
|
133329
|
+
}
|
|
133330
|
+
|
|
133331
|
+
function rebuildDOM(){
|
|
133332
|
+
// clear old
|
|
133333
|
+
Object.keys(nodeEls).forEach(function(id){if(nodeEls[id].parentNode)nodeEls[id].parentNode.removeChild(nodeEls[id])});
|
|
133334
|
+
while(svg.firstChild)svg.removeChild(svg.firstChild);
|
|
133335
|
+
nodeEls={};edgeEls=[];
|
|
133336
|
+
|
|
133337
|
+
var vis=getVisible();
|
|
133338
|
+
var visAll=new Set();vis.entities.forEach(function(id){visAll.add(id)});vis.events.forEach(function(id){visAll.add(id)});
|
|
133339
|
+
|
|
133340
|
+
// connection set for highlighting
|
|
133341
|
+
var conn=null;
|
|
133342
|
+
if(selId){
|
|
133343
|
+
conn=new Set();conn.add(selId);
|
|
133344
|
+
graphData.edges.forEach(function(e){
|
|
133345
|
+
if(!visAll.has(e.entityId)||!visAll.has(e.eventId))return;
|
|
133346
|
+
if(e.entityId===selId||e.eventId===selId){conn.add(e.entityId);conn.add(e.eventId)}
|
|
133347
|
+
});
|
|
133348
|
+
}
|
|
133349
|
+
|
|
133350
|
+
// edges
|
|
133351
|
+
graphData.edges.forEach(function(e){
|
|
133352
|
+
if(!visAll.has(e.entityId)||!visAll.has(e.eventId))return;
|
|
133353
|
+
var line=document.createElementNS('http://www.w3.org/2000/svg','line');
|
|
133354
|
+
var isDir=selId&&(e.entityId===selId||e.eventId===selId);
|
|
133355
|
+
var isRel=conn&&conn.has(e.entityId)&&conn.has(e.eventId);
|
|
133356
|
+
var isExp=expEnt.has(e.entityId)||expEv.has(e.eventId);
|
|
133357
|
+
if(isDir)line.setAttribute('class','edge highlighted');
|
|
133358
|
+
else if(isRel)line.setAttribute('class','edge');
|
|
133359
|
+
else if(conn)line.setAttribute('class','edge dimmed');
|
|
133360
|
+
else line.setAttribute('class',isExp?'edge animated':'edge');
|
|
133361
|
+
line.setAttribute('data-src',e.entityId);
|
|
133362
|
+
line.setAttribute('data-tgt',e.eventId);
|
|
133363
|
+
svg.appendChild(line);
|
|
133364
|
+
edgeEls.push(line);
|
|
133365
|
+
});
|
|
133366
|
+
|
|
133367
|
+
// nodes
|
|
133368
|
+
graphData.entities.forEach(function(ent){
|
|
133369
|
+
if(!vis.entities.has(ent.id))return;
|
|
133370
|
+
var el=mkNode(ent.id,ent.name,'entity',pos[ent.id].root,expEnt.has(ent.id));
|
|
133371
|
+
container.appendChild(el);nodeEls[ent.id]=el;
|
|
133372
|
+
});
|
|
133373
|
+
graphData.events.forEach(function(ev){
|
|
133374
|
+
if(!vis.events.has(ev.id))return;
|
|
133375
|
+
var el=mkNode(ev.id,ev.title,'event',false,expEv.has(ev.id));
|
|
133376
|
+
container.appendChild(el);nodeEls[ev.id]=el;
|
|
133377
|
+
});
|
|
133378
|
+
|
|
133379
|
+
// apply dimming
|
|
133380
|
+
if(conn){
|
|
133381
|
+
Object.keys(nodeEls).forEach(function(id){
|
|
133382
|
+
if(conn.has(id))nodeEls[id].classList.remove('dimmed');
|
|
133383
|
+
else nodeEls[id].classList.add('dimmed');
|
|
133384
|
+
});
|
|
133385
|
+
if(selId&&nodeEls[selId])nodeEls[selId].classList.add('selected');
|
|
133386
|
+
}
|
|
133387
|
+
|
|
133388
|
+
render();
|
|
133389
|
+
}
|
|
133390
|
+
|
|
133391
|
+
function mkNode(id,label,kind,root,expanded){
|
|
133392
|
+
var el=document.createElement('div');
|
|
133393
|
+
el.className='node '+kind;
|
|
133394
|
+
if(root)el.classList.add('root');
|
|
133395
|
+
if(expanded)el.classList.add('expanded');
|
|
133396
|
+
el.textContent=label;
|
|
133397
|
+
el.setAttribute('data-id',id);
|
|
133398
|
+
el.setAttribute('data-kind',kind);
|
|
133399
|
+
var dur=5+Math.random()*4;
|
|
133400
|
+
var del=Math.random()*dur;
|
|
133401
|
+
el.style.animationDuration=dur+'s';
|
|
133402
|
+
el.style.animationDelay='-'+del+'s';
|
|
133403
|
+
|
|
133404
|
+
var clickTimer=null;
|
|
133405
|
+
el.addEventListener('click',function(e){
|
|
133406
|
+
e.stopPropagation();
|
|
133407
|
+
if(clickTimer){clearTimeout(clickTimer);clickTimer=null;return}
|
|
133408
|
+
clickTimer=setTimeout(function(){
|
|
133409
|
+
clickTimer=null;
|
|
133410
|
+
toggleNode(id,kind);
|
|
133411
|
+
selId=id;
|
|
133412
|
+
rebuildDOM();
|
|
133413
|
+
},200);
|
|
133414
|
+
});
|
|
133415
|
+
el.addEventListener('dblclick',function(e){
|
|
133416
|
+
e.stopPropagation();
|
|
133417
|
+
if(clickTimer){clearTimeout(clickTimer);clickTimer=null}
|
|
133418
|
+
navStack=[];
|
|
133419
|
+
showDetail(id,kind);
|
|
133420
|
+
});
|
|
133421
|
+
return el;
|
|
133422
|
+
}
|
|
133423
|
+
|
|
133424
|
+
function toggleNode(id,kind){
|
|
133425
|
+
if(kind==='entity'){
|
|
133426
|
+
if(expEnt.has(id)){
|
|
133427
|
+
expEnt.delete(id);
|
|
133428
|
+
var related=new Set(evByEnt[id]||[]);
|
|
133429
|
+
expEv.forEach(function(eid){if(related.has(eid))expEv.delete(eid)});
|
|
133430
|
+
}else expEnt.add(id);
|
|
133431
|
+
}else{
|
|
133432
|
+
if(expEv.has(id))expEv.delete(id);
|
|
133433
|
+
else expEv.add(id);
|
|
133434
|
+
}
|
|
133435
|
+
}
|
|
133436
|
+
|
|
133437
|
+
function render(){
|
|
133438
|
+
// position nodes
|
|
133439
|
+
Object.keys(nodeEls).forEach(function(id){
|
|
133440
|
+
var p=pos[id];if(!p)return;
|
|
133441
|
+
var sp=w2s(p.x,p.y);
|
|
133442
|
+
var w=eById[id]?EW:EVW,h=eById[id]?EH:EVH;
|
|
133443
|
+
var el=nodeEls[id];
|
|
133444
|
+
el.style.left=sp.x+'px';
|
|
133445
|
+
el.style.top=sp.y+'px';
|
|
133446
|
+
el.style.width=(w*cam.zoom)+'px';
|
|
133447
|
+
el.style.height=(h*cam.zoom)+'px';
|
|
133448
|
+
el.style.fontSize=Math.max(8,12*cam.zoom)+'px';
|
|
133449
|
+
});
|
|
133450
|
+
|
|
133451
|
+
// position edges
|
|
133452
|
+
edgeEls.forEach(function(line){
|
|
133453
|
+
var srcId=line.getAttribute('data-src');
|
|
133454
|
+
var tgtId=line.getAttribute('data-tgt');
|
|
133455
|
+
var sp=pos[srcId],tp=pos[tgtId];
|
|
133456
|
+
if(!sp||!tp)return;
|
|
133457
|
+
var s=w2s(sp.x+EW/2,sp.y+EH/2);
|
|
133458
|
+
var t=w2s(tp.x+EVW/2,tp.y+EVH/2);
|
|
133459
|
+
line.setAttribute('x1',s.x);line.setAttribute('y1',s.y);
|
|
133460
|
+
line.setAttribute('x2',t.x);line.setAttribute('y2',t.y);
|
|
133461
|
+
});
|
|
133462
|
+
}
|
|
133463
|
+
|
|
133464
|
+
// click background to deselect
|
|
133465
|
+
container.addEventListener('click',function(e){
|
|
133466
|
+
if(e.target===container||e.target===svg){selId=null;rebuildDOM()}
|
|
133467
|
+
});
|
|
133468
|
+
|
|
133469
|
+
// detail panel
|
|
133470
|
+
function showDetail(id,kind,pushNav){
|
|
133471
|
+
if(pushNav!==false)navStack.push({id:id,kind:kind});
|
|
133472
|
+
var panel=document.getElementById('detail');
|
|
133473
|
+
var body=document.getElementById('detail-body');
|
|
133474
|
+
var html='';
|
|
133475
|
+
if(navStack.length>1)html+='<button class="back-btn" id="btn-back">← 返回</button>';
|
|
133476
|
+
if(kind==='entity'){
|
|
133477
|
+
var e=eById[id];
|
|
133478
|
+
html+='<div class="type-tag">'+esc(e.type)+'</div>';
|
|
133479
|
+
html+='<h3>'+esc(e.name)+'</h3>';
|
|
133480
|
+
html+='<div class="field"><div class="label">关联事件</div><div class="value">'+(e.eventCount||0)+' 个</div></div>';
|
|
133481
|
+
var ce=evByEnt[id]||[];
|
|
133482
|
+
if(ce.length){html+='<div class="divider"></div><div class="field"><div class="label">事件列表</div>';ce.forEach(function(eid){var ev=evById[eid];if(ev)html+='<div class="conn-item" data-id="'+eid+'" data-kind="event">'+esc(ev.title)+'</div>'});html+='</div>'}
|
|
133483
|
+
}else{
|
|
133484
|
+
var ev=evById[id];
|
|
133485
|
+
html+='<h3>'+esc(ev.title)+'</h3>';
|
|
133486
|
+
var ce=entByEv[id]||[];
|
|
133487
|
+
if(ce.length){html+='<div class="divider"></div><div class="field"><div class="label">关联实体</div>';ce.forEach(function(eid){var e=eById[eid];if(e)html+='<div class="conn-item" data-id="'+eid+'" data-kind="entity">'+esc(e.name)+'<span class="badge">'+esc(e.type)+'</span></div>'});html+='</div>'}
|
|
133488
|
+
}
|
|
133489
|
+
body.innerHTML=html;panel.classList.add('open');
|
|
133490
|
+
var backBtn=document.getElementById('btn-back');
|
|
133491
|
+
if(backBtn)backBtn.addEventListener('click',function(){
|
|
133492
|
+
navStack.pop();
|
|
133493
|
+
var prev=navStack[navStack.length-1];
|
|
133494
|
+
if(prev){selId=prev.id;rebuildDOM();showDetail(prev.id,prev.kind,false)}
|
|
133495
|
+
else{panel.classList.remove('open')}
|
|
133496
|
+
});
|
|
133497
|
+
body.querySelectorAll('.conn-item').forEach(function(el){
|
|
133498
|
+
el.addEventListener('click',function(){
|
|
133499
|
+
var nid=el.getAttribute('data-id'),nk=el.getAttribute('data-kind');
|
|
133500
|
+
if(nk==='entity'&&!expEnt.has(nid))expEnt.add(nid);
|
|
133501
|
+
if(nk==='event'&&!expEv.has(nid))expEv.add(nid);
|
|
133502
|
+
selId=nid;rebuildDOM();showDetail(nid,nk);
|
|
133503
|
+
});
|
|
133504
|
+
});
|
|
133505
|
+
}
|
|
133506
|
+
|
|
133507
|
+
function esc(s){if(!s)return'';return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"')}
|
|
133508
|
+
|
|
133509
|
+
document.getElementById('btn-reset').onclick=function(){expEnt.clear();expEv.clear();selId=null;graphData.entities.forEach(function(e){expEnt.add(e.id)});document.getElementById('detail').classList.remove('open');navStack=[];fitView();rebuildDOM()};
|
|
133510
|
+
document.getElementById('btn-expand').onclick=function(){graphData.entities.forEach(function(e){expEnt.add(e.id)});graphData.events.forEach(function(e){expEv.add(e.id)});selId=null;document.getElementById('detail').classList.remove('open');fitView();rebuildDOM()};
|
|
133511
|
+
document.getElementById('btn-close').onclick=function(){document.getElementById('detail').classList.remove('open');navStack=[]};
|
|
133512
|
+
})();
|
|
133513
|
+
<\/script>
|
|
133514
|
+
</body>
|
|
133515
|
+
</html>`;
|
|
133516
|
+
async function serveGraphJSON(store, res) {
|
|
133517
|
+
try {
|
|
133518
|
+
const [entities, events, edges, sources] = await Promise.all([
|
|
133519
|
+
store.listEntities(),
|
|
133520
|
+
store.listEvents(),
|
|
133521
|
+
store.listEventEntities(),
|
|
133522
|
+
store.listSources()
|
|
133523
|
+
]);
|
|
133524
|
+
const eventEntityMap = /* @__PURE__ */ new Map();
|
|
133525
|
+
for (const edge of edges) {
|
|
133526
|
+
const list = eventEntityMap.get(edge.eventId) ?? [];
|
|
133527
|
+
list.push(edge.entityId);
|
|
133528
|
+
eventEntityMap.set(edge.eventId, list);
|
|
133529
|
+
}
|
|
133530
|
+
const data = {
|
|
133531
|
+
entities: entities.map(({ id, sourceId, type, name, normalizedName, eventCount }) => ({
|
|
133532
|
+
id,
|
|
133533
|
+
sourceId,
|
|
133534
|
+
type,
|
|
133535
|
+
name,
|
|
133536
|
+
normalizedName,
|
|
133537
|
+
eventCount
|
|
133538
|
+
})),
|
|
133539
|
+
events: events.map(({ id, sourceId, documentId, title, rank }) => ({
|
|
133540
|
+
id,
|
|
133541
|
+
sourceId,
|
|
133542
|
+
documentId,
|
|
133543
|
+
title,
|
|
133544
|
+
rank,
|
|
133545
|
+
entityIds: eventEntityMap.get(id) ?? []
|
|
133546
|
+
})),
|
|
133547
|
+
edges,
|
|
133548
|
+
sources: sources.map(({ id, name }) => ({
|
|
133549
|
+
id,
|
|
133550
|
+
name
|
|
133551
|
+
}))
|
|
133552
|
+
};
|
|
133553
|
+
res.writeHead(200, {
|
|
133554
|
+
"Content-Type": "application/json",
|
|
133555
|
+
"Cache-Control": "no-store"
|
|
133556
|
+
});
|
|
133557
|
+
res.end(JSON.stringify(data));
|
|
133558
|
+
} catch (error) {
|
|
133559
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
133560
|
+
res.end(JSON.stringify({ error: String(error) }));
|
|
133561
|
+
}
|
|
133562
|
+
}
|
|
133563
|
+
function serveHTML(res) {
|
|
133564
|
+
res.writeHead(200, {
|
|
133565
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
133566
|
+
"Cache-Control": "no-store"
|
|
133567
|
+
});
|
|
133568
|
+
res.end(HTML);
|
|
133569
|
+
}
|
|
133570
|
+
async function handleWeb() {
|
|
133571
|
+
const store = await getKnowledgeStore();
|
|
133572
|
+
const s = await store.stats();
|
|
133573
|
+
if (s.entities === 0 && s.events === 0) throw new Error("知识库为空,请先用 /knowledge 摄入文档");
|
|
133574
|
+
const server = createServer((req, res) => {
|
|
133575
|
+
if (req.url === "/api/graph") {
|
|
133576
|
+
serveGraphJSON(store, res);
|
|
133577
|
+
return;
|
|
133578
|
+
}
|
|
133579
|
+
serveHTML(res);
|
|
133580
|
+
});
|
|
133581
|
+
registerServer(server);
|
|
133582
|
+
await new Promise((resolve, reject) => {
|
|
133583
|
+
server.once("error", reject);
|
|
133584
|
+
server.listen(0, "127.0.0.1", () => {
|
|
133585
|
+
server.off("error", reject);
|
|
133586
|
+
resolve();
|
|
133587
|
+
});
|
|
133588
|
+
});
|
|
133589
|
+
const url = `http://127.0.0.1:${server.address().port}`;
|
|
133590
|
+
openUrl(url);
|
|
133591
|
+
console.log(`\n 知识图谱: ${url}\n 按 Ctrl+C 停止服务器\n`);
|
|
133592
|
+
}
|
|
133593
|
+
//#endregion
|
|
132841
133594
|
//#region src/tui/components/dialogs/knowledge-result-viewer.ts
|
|
132842
133595
|
/**
|
|
132843
133596
|
* KnowledgeResultViewer — full-screen scrollable text viewer for /knowledge
|
|
@@ -133192,15 +133945,6 @@ function fitExactly$3(line, width) {
|
|
|
133192
133945
|
}
|
|
133193
133946
|
//#endregion
|
|
133194
133947
|
//#region src/tui/commands/knowledge.ts
|
|
133195
|
-
let knowledgeStoreInstance;
|
|
133196
|
-
async function getKnowledgeStore() {
|
|
133197
|
-
if (knowledgeStoreInstance === void 0) {
|
|
133198
|
-
knowledgeStoreInstance = new KnowledgeStore(getDataDir());
|
|
133199
|
-
await knowledgeStoreInstance.init();
|
|
133200
|
-
knowledgeStoreInstance.setEmbeddingEngine(createFastEmbedEngine());
|
|
133201
|
-
}
|
|
133202
|
-
return knowledgeStoreInstance;
|
|
133203
|
-
}
|
|
133204
133948
|
function promptTextInput(host, title, opts) {
|
|
133205
133949
|
const { promise, resolve } = Promise.withResolvers();
|
|
133206
133950
|
const dialog = new TextInputDialogComponent((result) => {
|
|
@@ -133505,6 +134249,11 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
133505
134249
|
value: "stats",
|
|
133506
134250
|
label: "📊 统计信息",
|
|
133507
134251
|
description: "查看知识库整体统计"
|
|
134252
|
+
},
|
|
134253
|
+
{
|
|
134254
|
+
value: "web",
|
|
134255
|
+
label: "🌐 知识图谱",
|
|
134256
|
+
description: "在浏览器中查看交互式知识图谱"
|
|
133508
134257
|
}
|
|
133509
134258
|
];
|
|
133510
134259
|
const showMenu = () => {
|
|
@@ -133522,6 +134271,7 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
133522
134271
|
else if (value === "search") await handleSearch(host);
|
|
133523
134272
|
else if (value === "delete") await handleDelete(host);
|
|
133524
134273
|
else if (value === "stats") await handleStats(host);
|
|
134274
|
+
else if (value === "web") await handleWeb();
|
|
133525
134275
|
} catch (error) {
|
|
133526
134276
|
const msg = error instanceof Error ? error.message : String(error);
|
|
133527
134277
|
host.showError(`操作失败: ${msg}`);
|
|
@@ -134863,17 +135613,6 @@ function mcpServerStatusKey(server) {
|
|
|
134863
135613
|
]);
|
|
134864
135614
|
}
|
|
134865
135615
|
//#endregion
|
|
134866
|
-
//#region src/tui/utils/open-url.ts
|
|
134867
|
-
function openUrl(url) {
|
|
134868
|
-
const command = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", [
|
|
134869
|
-
"/c",
|
|
134870
|
-
"start",
|
|
134871
|
-
"",
|
|
134872
|
-
url
|
|
134873
|
-
]] : ["xdg-open", [url]];
|
|
134874
|
-
execFile(command[0], command[1], () => {});
|
|
134875
|
-
}
|
|
134876
|
-
//#endregion
|
|
134877
135616
|
//#region src/tui/utils/mcp-oauth.ts
|
|
134878
135617
|
const MAX_AUTH_URL_SET_SIZE = 200;
|
|
134879
135618
|
var McpOAuthAuthorizationUrlOpener = class {
|
|
@@ -142940,6 +143679,7 @@ var SessionManager = class {
|
|
|
142940
143679
|
if (session === void 0) throw new Error("启动会话未初始化。");
|
|
142941
143680
|
await this.setSession(session);
|
|
142942
143681
|
await this.syncRuntimeState(session);
|
|
143682
|
+
if (startup.wolfpack && !isResumeStartup) await session.setWolfpackMode(true);
|
|
142943
143683
|
this.host.state.startupState = "ready";
|
|
142944
143684
|
this.host.sessionEventHandler.startSubscription();
|
|
142945
143685
|
return {
|
|
@@ -145057,7 +145797,7 @@ function createInitialAppState(input) {
|
|
|
145057
145797
|
goalActive: false,
|
|
145058
145798
|
goalContinuationCount: 0,
|
|
145059
145799
|
ccConnectActive: false,
|
|
145060
|
-
wolfpackMode:
|
|
145800
|
+
wolfpackMode: input.cliOptions.wolfpack === true,
|
|
145061
145801
|
loopModeEnabled: false,
|
|
145062
145802
|
loopPrompt: void 0,
|
|
145063
145803
|
loopLimit: void 0,
|
|
@@ -145117,6 +145857,7 @@ var ScreamTUI = class {
|
|
|
145117
145857
|
yolo: startupInput.cliOptions.yolo,
|
|
145118
145858
|
auto: startupInput.cliOptions.auto,
|
|
145119
145859
|
plan: startupInput.cliOptions.plan,
|
|
145860
|
+
wolfpack: startupInput.cliOptions.wolfpack,
|
|
145120
145861
|
model: startupInput.cliOptions.model,
|
|
145121
145862
|
startupNotice: startupInput.startupNotice
|
|
145122
145863
|
},
|
|
@@ -146391,6 +147132,14 @@ var ClaudeStreamJsonWriter = class {
|
|
|
146391
147132
|
if (input > 0) this.tokenUsage.input += input;
|
|
146392
147133
|
if (output > 0) this.tokenUsage.output += output;
|
|
146393
147134
|
}
|
|
147135
|
+
/** Returns the accumulated token usage for the current turn. Used by
|
|
147136
|
+
* the result event emitter so the final `result` event carries the
|
|
147137
|
+
* turn's total token usage. Without this, usage was accumulated but
|
|
147138
|
+
* never emitted — consumers (cc-connect, mobile clients) saw empty
|
|
147139
|
+
* usage fields. */
|
|
147140
|
+
getTokenUsage() {
|
|
147141
|
+
return { ...this.tokenUsage };
|
|
147142
|
+
}
|
|
146394
147143
|
nextMsgId() {
|
|
146395
147144
|
this.msgCounter += 1;
|
|
146396
147145
|
return `msg_${this.msgCounter.toString(36)}`;
|
|
@@ -146637,22 +147386,26 @@ async function runStreamJson(opts) {
|
|
|
146637
147386
|
let activeAgentId;
|
|
146638
147387
|
let settled = false;
|
|
146639
147388
|
let unsubscribe;
|
|
146640
|
-
const finish = (error) => {
|
|
147389
|
+
const finish = (error, exitCode) => {
|
|
146641
147390
|
if (settled) return;
|
|
146642
147391
|
settled = true;
|
|
146643
147392
|
unsubscribe?.();
|
|
146644
147393
|
if (error) {
|
|
146645
|
-
writer.emitResult("error", error.message);
|
|
147394
|
+
writer.emitResult("error", error.message, writer.getTokenUsage());
|
|
147395
|
+
if (exitCode !== void 0) process.exitCode = exitCode;
|
|
146646
147396
|
reject(error);
|
|
146647
147397
|
} else {
|
|
146648
|
-
writer.emitResult("success", "");
|
|
147398
|
+
writer.emitResult("success", "", writer.getTokenUsage());
|
|
146649
147399
|
resolve();
|
|
146650
147400
|
}
|
|
146651
147401
|
};
|
|
146652
147402
|
unsubscribe = session.onEvent((event) => {
|
|
146653
147403
|
if (event.type === "error") {
|
|
146654
147404
|
if (event.agentId !== "main") return;
|
|
146655
|
-
|
|
147405
|
+
const errCode = event.code;
|
|
147406
|
+
let exitCode = 1;
|
|
147407
|
+
if (errCode === "provider.auth_error" || errCode === "provider.rate_limit" || errCode === "provider.api_error" || errCode === "provider.connection_error") exitCode = 3;
|
|
147408
|
+
finish(/* @__PURE__ */ new Error(`${event.code}: ${event.message}`), exitCode);
|
|
146656
147409
|
return;
|
|
146657
147410
|
}
|
|
146658
147411
|
if (event.type === "subagent.spawned") {
|
|
@@ -146689,9 +147442,14 @@ async function runStreamJson(opts) {
|
|
|
146689
147442
|
writer.updateUsage(inputTotal, event.usage.output ?? 0);
|
|
146690
147443
|
}
|
|
146691
147444
|
} else if (type === "turn.ended") if (event.reason === "completed") finish();
|
|
147445
|
+
else if (event.reason === "cancelled") finish(/* @__PURE__ */ new Error("Turn cancelled"));
|
|
146692
147446
|
else {
|
|
147447
|
+
const errCode = event.error?.code;
|
|
146693
147448
|
const errMsg = event.error !== void 0 ? `${event.error.code}: ${event.error.message}` : `Turn ended: ${event.reason}`;
|
|
146694
|
-
|
|
147449
|
+
let code = 1;
|
|
147450
|
+
if (errCode === "loop.max_steps_exceeded") code = 4;
|
|
147451
|
+
else if (errCode === "provider.auth_error" || errCode === "provider.rate_limit" || errCode === "provider.api_error" || errCode === "provider.connection_error") code = 3;
|
|
147452
|
+
finish(new Error(errMsg), code);
|
|
146695
147453
|
}
|
|
146696
147454
|
});
|
|
146697
147455
|
session.prompt(userText).catch((error) => {
|
|
@@ -146712,9 +147470,11 @@ async function runStreamJson(opts) {
|
|
|
146712
147470
|
await turnPromise;
|
|
146713
147471
|
}
|
|
146714
147472
|
} catch (error) {
|
|
146715
|
-
|
|
146716
|
-
|
|
146717
|
-
|
|
147473
|
+
if (process.exitCode === void 0) {
|
|
147474
|
+
log.error("stream-json: fatal", { error });
|
|
147475
|
+
writer.emitResult("error", error instanceof Error ? error.message : String(error), writer.getTokenUsage());
|
|
147476
|
+
process.exitCode = 1;
|
|
147477
|
+
} else log.error("stream-json: turn error (exitCode already set)", { error });
|
|
146718
147478
|
} finally {
|
|
146719
147479
|
for (const [, pending] of pendingApprovals) pending.resolve({
|
|
146720
147480
|
decision: "rejected",
|