scream-code 0.12.7 → 0.12.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,7 +7,7 @@ import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } fr
|
|
|
7
7
|
import "./suppress-sqlite-warning-C2VB0doZ.mjs";
|
|
8
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-BH9W5k24.mjs";
|
|
9
9
|
import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
|
|
10
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
10
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-CsPSlTrQ.mjs";
|
|
11
11
|
import { createRequire } from "node:module";
|
|
12
12
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
13
13
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -3033,7 +3033,7 @@ const buildHeaders$2 = (newHeaders) => {
|
|
|
3033
3033
|
function encodeURIPath$2(str) {
|
|
3034
3034
|
return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
3035
3035
|
}
|
|
3036
|
-
const EMPTY$
|
|
3036
|
+
const EMPTY$4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
3037
3037
|
const createPathTagFunction$2 = (pathEncoder = encodeURIPath$2) => function path(statics, ...params) {
|
|
3038
3038
|
if (statics.length === 1) return statics[0];
|
|
3039
3039
|
let postPath = false;
|
|
@@ -3042,7 +3042,7 @@ const createPathTagFunction$2 = (pathEncoder = encodeURIPath$2) => function path
|
|
|
3042
3042
|
if (/[?#]/.test(currentValue)) postPath = true;
|
|
3043
3043
|
const value = params[index];
|
|
3044
3044
|
let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
|
|
3045
|
-
if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$
|
|
3045
|
+
if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$4) ?? EMPTY$4)?.toString)) {
|
|
3046
3046
|
encoded = value + "";
|
|
3047
3047
|
invalidSegments.push({
|
|
3048
3048
|
start: previousValue.length + currentValue.length,
|
|
@@ -31529,7 +31529,7 @@ APIResource$1._key = [];
|
|
|
31529
31529
|
function encodeURIPath$1(str) {
|
|
31530
31530
|
return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
31531
31531
|
}
|
|
31532
|
-
const EMPTY$
|
|
31532
|
+
const EMPTY$3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
31533
31533
|
const createPathTagFunction$1 = (pathEncoder = encodeURIPath$1) => (function path(statics, ...params) {
|
|
31534
31534
|
if (statics.length === 1) return statics[0];
|
|
31535
31535
|
let postPath = false;
|
|
@@ -31539,7 +31539,7 @@ const createPathTagFunction$1 = (pathEncoder = encodeURIPath$1) => (function pat
|
|
|
31539
31539
|
if (/[?#]/.test(currentValue)) postPath = true;
|
|
31540
31540
|
const value = params[index];
|
|
31541
31541
|
let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
|
|
31542
|
-
if (index !== params.length && (value == null || typeof value === "object" && value.toString === ((_c = Object.getPrototypeOf((_b = Object.getPrototypeOf((_a = value.hasOwnProperty) !== null && _a !== void 0 ? _a : EMPTY$
|
|
31542
|
+
if (index !== params.length && (value == null || typeof value === "object" && value.toString === ((_c = Object.getPrototypeOf((_b = Object.getPrototypeOf((_a = value.hasOwnProperty) !== null && _a !== void 0 ? _a : EMPTY$3)) !== null && _b !== void 0 ? _b : EMPTY$3)) === null || _c === void 0 ? void 0 : _c.toString))) {
|
|
31543
31543
|
encoded = value + "";
|
|
31544
31544
|
invalidSegments.push({
|
|
31545
31545
|
start: previousValue.length + currentValue.length,
|
|
@@ -36516,7 +36516,7 @@ var APIResource = class {
|
|
|
36516
36516
|
function encodeURIPath(str) {
|
|
36517
36517
|
return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
|
|
36518
36518
|
}
|
|
36519
|
-
const EMPTY$
|
|
36519
|
+
const EMPTY$2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.create(null));
|
|
36520
36520
|
const createPathTagFunction = (pathEncoder = encodeURIPath) => function path(statics, ...params) {
|
|
36521
36521
|
if (statics.length === 1) return statics[0];
|
|
36522
36522
|
let postPath = false;
|
|
@@ -36525,7 +36525,7 @@ const createPathTagFunction = (pathEncoder = encodeURIPath) => function path(sta
|
|
|
36525
36525
|
if (/[?#]/.test(currentValue)) postPath = true;
|
|
36526
36526
|
const value = params[index];
|
|
36527
36527
|
let encoded = (postPath ? encodeURIComponent : pathEncoder)("" + value);
|
|
36528
|
-
if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$
|
|
36528
|
+
if (index !== params.length && (value == null || typeof value === "object" && value.toString === Object.getPrototypeOf(Object.getPrototypeOf(value.hasOwnProperty ?? EMPTY$2) ?? EMPTY$2)?.toString)) {
|
|
36529
36529
|
encoded = value + "";
|
|
36530
36530
|
invalidSegments.push({
|
|
36531
36531
|
start: previousValue.length + currentValue.length,
|
|
@@ -66924,7 +66924,7 @@ async function isDir(p) {
|
|
|
66924
66924
|
//#region ../../packages/agent-core/src/plugin/store.ts
|
|
66925
66925
|
const PLUGINS_DIR_REL = path.join("plugins");
|
|
66926
66926
|
const INSTALLED_REL = path.join(PLUGINS_DIR_REL, "installed.json");
|
|
66927
|
-
const EMPTY = {
|
|
66927
|
+
const EMPTY$1 = {
|
|
66928
66928
|
version: 1,
|
|
66929
66929
|
plugins: []
|
|
66930
66930
|
};
|
|
@@ -66934,7 +66934,7 @@ async function readInstalled(screamHomeDir) {
|
|
|
66934
66934
|
try {
|
|
66935
66935
|
text = await readFile(filePath, "utf8");
|
|
66936
66936
|
} catch (error) {
|
|
66937
|
-
if (error.code === "ENOENT") return EMPTY;
|
|
66937
|
+
if (error.code === "ENOENT") return EMPTY$1;
|
|
66938
66938
|
throw error;
|
|
66939
66939
|
}
|
|
66940
66940
|
try {
|
|
@@ -76465,10 +76465,10 @@ function stripContextMetadata(message) {
|
|
|
76465
76465
|
}
|
|
76466
76466
|
//#endregion
|
|
76467
76467
|
//#region ../../packages/agent-core/src/agent/compaction/compaction-instruction.md
|
|
76468
|
-
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 **
|
|
76468
|
+
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 **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\n**You MUST output at least one memory-memo block** (or the `{\"none\": true}` marker) at the very end of your response — omitting the section entirely is not allowed. Record completed task loops as full experience records; record **in-progress work** as a lower-priority record whose \"outcome\" is \"进行中\" (so the ongoing task survives compaction and can be resumed later).\n\nFor each 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', or '进行中' for in-progress work>\",\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 \"note\": \"<optional: a soft, AI-readable note/suggestion that makes the record easier for future turns to understand — e.g. '这类任务先查接口可用性再写代码'. Omit if none>\"\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- \"note\" is optional: a one-sentence advisory note that helps future AI understand/reuse the record faster. It is a soft suggestion, not a user-enforced rule.\n- For in-progress work: record it with \"outcome\": \"进行中\" and describe where the task stands and what remains — this is what lets the agent resume seamlessly after compaction.\n- Merge closely related sub-tasks into a single record.\n- Use the exact field names and JSON format shown above (no extra fields beyond \"note\").\n\nIf no task loops (completed or in-progress) 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. **Next Steps**: The ordered, actionable plan going forward\n7. **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## Key Decisions\n\n- [Decision]: [rationale] — preserve architectural choices and why they were made\n- ...\n\n## Next Steps\n\n1. [The very next actionable step]\n2. [Then this]\n3. ...\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\n## Skill candidates\n\nScan the compressed messages for reusable processes (project-specific build\nsteps, recurring debugging patterns, or tool workflows). If any is genuinely\nworth capturing as a reusable skill, output at the very end, at most one:\n\n[[skill-candidate: <name>|<one-line purpose>|<evidence>]]\n\n- The evidence must be concrete and verifiable: exact file paths, commands, or\n step sequences from the conversation (e.g. \"run `pnpm vitest run -t compaction`\n in packages/agent-core to reproduce the compaction snapshots\"). Vague evidence\n like \"used a script\" is not acceptable.\n- Only emit a candidate when the process is genuinely reusable beyond this one\n task; do not emit for one-off actions.\n\nOmit if none.\n";
|
|
76469
76469
|
//#endregion
|
|
76470
76470
|
//#region ../../packages/agent-core/src/agent/compaction/compaction-update-instruction.md
|
|
76471
|
-
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 **
|
|
76471
|
+
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 **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\n**You MUST output at least one memory-memo block** (or the `{\"none\": true}` marker) at the very end of your response — omitting the section entirely is not allowed. Record completed task loops as full experience records; record **in-progress work** as a lower-priority record whose \"outcome\" is \"进行中\" (so the ongoing task survives compaction and can be resumed later).\n\nFor each 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', or '进行中' for in-progress work>\",\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 \"note\": \"<optional: a soft, AI-readable note/suggestion that makes the record easier for future turns to understand — e.g. '这类任务先查接口可用性再写代码'. Omit if none>\"\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- For in-progress work: record it with \"outcome\": \"进行中\" and describe where the task stands and what remains — this is what lets the agent resume seamlessly after compaction.\n- Merge closely related sub-tasks into a single record.\n- Use the exact field names and JSON format shown above.\n\nIf no task loops (completed or in-progress) 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, Key Decisions, Next Steps, 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. Merge new decisions into \"Key Decisions\" (keep prior decisions unless explicitly overturned); merge new steps into \"Next Steps\" (reorder if priorities changed).\n8. 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## Key Decisions\n\n- [Decision]: [rationale] — preserve architectural choices and why they were made\n- ...\n\n## Next Steps\n\n1. [The very next actionable step]\n2. [Then this]\n3. ...\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";
|
|
76472
76472
|
//#endregion
|
|
76473
76473
|
//#region ../../packages/agent-core/src/agent/compaction/render-messages.ts
|
|
76474
76474
|
const TOOL_RESULT_MAX_CHARS = 2e3;
|
|
@@ -76556,13 +76556,14 @@ function stringifyJsonish(value) {
|
|
|
76556
76556
|
//#endregion
|
|
76557
76557
|
//#region ../../packages/agent-core/src/agent/compaction/strategy.ts
|
|
76558
76558
|
const DEFAULT_COMPACTION_CONFIG = {
|
|
76559
|
-
triggerRatio: .
|
|
76560
|
-
blockRatio: .
|
|
76561
|
-
reservedContextSize:
|
|
76559
|
+
triggerRatio: .85,
|
|
76560
|
+
blockRatio: .9,
|
|
76561
|
+
reservedContextSize: 2e4,
|
|
76562
76562
|
maxCompactionPerTurn: 3,
|
|
76563
|
-
maxRecentMessages:
|
|
76564
|
-
maxRecentUserMessages:
|
|
76565
|
-
|
|
76563
|
+
maxRecentMessages: 24,
|
|
76564
|
+
maxRecentUserMessages: 12,
|
|
76565
|
+
maxRecentTokens: 3e4,
|
|
76566
|
+
maxRecentSizeRatio: .25,
|
|
76566
76567
|
minOverflowReductionRatio: .05,
|
|
76567
76568
|
turnGrowthMultiplier: 2.5
|
|
76568
76569
|
};
|
|
@@ -76603,7 +76604,7 @@ var DefaultCompactionStrategy = class {
|
|
|
76603
76604
|
if (m2.role === "user") recentUserMessages++;
|
|
76604
76605
|
recentSize += estimateTokensForMessage(m2);
|
|
76605
76606
|
if (canSplitAfter(messages, splitIndex)) bestN = splitIndex + 1;
|
|
76606
|
-
if ((recentMessages >= this.config.maxRecentMessages || recentUserMessages >= this.config.maxRecentUserMessages || recentSize >= this.maxSize * this.config.maxRecentSizeRatio) && bestN !== void 0) break;
|
|
76607
|
+
if ((recentMessages >= this.config.maxRecentMessages || recentUserMessages >= this.config.maxRecentUserMessages || recentSize >= this.config.maxRecentTokens || recentSize >= this.maxSize * this.config.maxRecentSizeRatio) && bestN !== void 0) break;
|
|
76607
76608
|
}
|
|
76608
76609
|
return bestN ?? 0;
|
|
76609
76610
|
}
|
|
@@ -77091,7 +77092,7 @@ var FullCompaction = class {
|
|
|
77091
77092
|
} else this.lastCompactionFiles = void 0;
|
|
77092
77093
|
for (const msg of messagesToCompactForOps) extractFileOpsFromMessage(msg, fileOps);
|
|
77093
77094
|
const toolCallHistory = formatToolCallHistory(messagesToCompactForOps);
|
|
77094
|
-
const processedSummary = this.postProcessSummary(summary, fileOps, toolCallHistory);
|
|
77095
|
+
const processedSummary = this.postProcessSummary(summary, fileOps, toolCallHistory, compactedCount);
|
|
77095
77096
|
const tokensAfter = estimateTokens$1(processedSummary) + estimateTokensForMessages(recent);
|
|
77096
77097
|
const fileLists = computeFileLists(fileOps);
|
|
77097
77098
|
const MAX_PERSISTED_FILES = 100;
|
|
@@ -77114,7 +77115,7 @@ var FullCompaction = class {
|
|
|
77114
77115
|
this.agent.context.applyCompaction(result);
|
|
77115
77116
|
this.lastCompactionFiles = fileLists;
|
|
77116
77117
|
this.lowWaterMark = Math.floor(this.effectiveTokenCount * 1.1);
|
|
77117
|
-
await this.extractAndStoreMemos(processedSummary);
|
|
77118
|
+
await this.extractAndStoreMemos(processedSummary, messagesToCompactForOps);
|
|
77118
77119
|
this.triggerPostCompactHook(data, result);
|
|
77119
77120
|
this.detectSkillCandidates(processedSummary);
|
|
77120
77121
|
this.consecutiveCompactionFailures = 0;
|
|
@@ -77178,16 +77179,27 @@ var FullCompaction = class {
|
|
|
77178
77179
|
}
|
|
77179
77180
|
});
|
|
77180
77181
|
}
|
|
77181
|
-
/** Extract memory memos from compaction summary and store them.
|
|
77182
|
-
|
|
77182
|
+
/** Extract memory memos from compaction summary and store them. When the
|
|
77183
|
+
* summary carries no memory-memo block, fall back to recording the most
|
|
77184
|
+
* recent real user message from the compacted history as a low-confidence
|
|
77185
|
+
* in-progress memo, so an ongoing task survives compaction even if the
|
|
77186
|
+
* model omitted the extraction section. */
|
|
77187
|
+
async extractAndStoreMemos(summary, messagesToCompact) {
|
|
77183
77188
|
const memoStore = this.agent.memoStore;
|
|
77184
77189
|
if (!memoStore) {
|
|
77185
77190
|
this.agent.log.info("Memory memo store not available, skipping extraction");
|
|
77186
77191
|
return;
|
|
77187
77192
|
}
|
|
77188
77193
|
this.agent.log.info("Scanning compaction summary for memory memos", { summaryLen: summary.length });
|
|
77189
|
-
|
|
77194
|
+
let memos = parseMemoryMemos(summary);
|
|
77190
77195
|
this.agent.log.info("Memory memo parse result", { memoCount: memos.length });
|
|
77196
|
+
if (memos.length === 0 && messagesToCompact !== void 0) {
|
|
77197
|
+
const fallback = /```memory-memo[\s\S]*?"none"\s*:\s*true[\s\S]*?```/.test(summary) ? void 0 : this.buildFallbackMemo(messagesToCompact);
|
|
77198
|
+
if (fallback !== void 0) {
|
|
77199
|
+
memos = [fallback];
|
|
77200
|
+
this.agent.log.info("Compaction summary carried no memory-memo; stored fallback memo", { userNeed: fallback.userNeed.slice(0, 120) });
|
|
77201
|
+
}
|
|
77202
|
+
}
|
|
77191
77203
|
if (memos.length === 0) return;
|
|
77192
77204
|
const sessionId = this.agent.homedir ? basename$1(dirname$2(dirname$2(this.agent.homedir))) : "unknown";
|
|
77193
77205
|
const sessionTitle = await this.agent.getSessionTitle();
|
|
@@ -77206,6 +77218,28 @@ var FullCompaction = class {
|
|
|
77206
77218
|
sessionId
|
|
77207
77219
|
});
|
|
77208
77220
|
}
|
|
77221
|
+
/** Build a low-confidence fallback memo from the most recent real user
|
|
77222
|
+
* message in the compacted history (origin.kind === 'user'), so an ongoing
|
|
77223
|
+
* task is not lost when the summary omits the memory-memo section. */
|
|
77224
|
+
buildFallbackMemo(messages) {
|
|
77225
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
77226
|
+
const msg = messages[i];
|
|
77227
|
+
if (msg === void 0 || msg.role !== "user" || msg.origin?.kind !== "user") continue;
|
|
77228
|
+
const text = msg.content.filter((p) => p.type === "text").map((p) => p.text).join("").trim();
|
|
77229
|
+
if (text.length === 0) continue;
|
|
77230
|
+
return createMemoryMemo({
|
|
77231
|
+
sourceSessionId: "",
|
|
77232
|
+
userNeed: text.slice(0, 300),
|
|
77233
|
+
approach: "(压缩时自动兜底记录:压缩摘要未生成完整任务经验提取,此处记录最近一条用户请求,用于跨压缩恢复进行中任务。请结合会话上下文核实。)",
|
|
77234
|
+
outcome: "进行中",
|
|
77235
|
+
whatFailed: "none",
|
|
77236
|
+
whatWorked: "none",
|
|
77237
|
+
tags: ["compaction-fallback"],
|
|
77238
|
+
note: "低置信度自动记录:建议后续在上下文恢复后核实并补充完整经验。",
|
|
77239
|
+
extractionSource: "compaction"
|
|
77240
|
+
});
|
|
77241
|
+
}
|
|
77242
|
+
}
|
|
77209
77243
|
/**
|
|
77210
77244
|
* Detects [[skill-candidate: <name>|<purpose>|<evidence>]] markers in the
|
|
77211
77245
|
* compaction summary and emits a `skill_candidate` event for each one (at
|
|
@@ -77238,11 +77272,17 @@ var FullCompaction = class {
|
|
|
77238
77272
|
* the compaction summary so active tasks and file context survive
|
|
77239
77273
|
* compression. Without this, both are lost after compaction because the
|
|
77240
77274
|
* original messages containing them are removed from the context window.
|
|
77275
|
+
*
|
|
77276
|
+
* When messages were actually compacted (`compactedCount > 0`), a leading
|
|
77277
|
+
* elision note tells the model how much of the conversation is missing and
|
|
77278
|
+
* that the summary is working notes, not verbatim history — so the tail
|
|
77279
|
+
* messages after the summary are not mistaken for a continuous conversation.
|
|
77241
77280
|
*/
|
|
77242
|
-
postProcessSummary(summary, fileOps, toolCallHistory) {
|
|
77281
|
+
postProcessSummary(summary, fileOps, toolCallHistory, compactedCount) {
|
|
77243
77282
|
const todos = this.agent.tools.storeData()["todo"] ?? [];
|
|
77244
|
-
const base = summary.trim().replaceAll(/<files>[\s\S]*?<\/files>\s*/g, "").trimEnd();
|
|
77283
|
+
const base = summary.trim().replaceAll(/<files>[\s\S]*?<\/files>\s*/g, "").replace(/^> The conversation before this point was compacted:[\s\S]*?continue from here\.\s*\n+/m, "").trimEnd();
|
|
77245
77284
|
const sections = [];
|
|
77285
|
+
if (compactedCount > 0) sections.push(`> The conversation before this point was compacted: ${String(compactedCount)} earlier message(s) were omitted and are covered by this summary. Treat the summary below as working notes, not verbatim history — the messages after it continue from here.`);
|
|
77246
77286
|
if (base.length > 0) sections.push(base);
|
|
77247
77287
|
if (todos.length > 0) {
|
|
77248
77288
|
const lines = todos.map((t) => {
|
|
@@ -121699,6 +121739,7 @@ const SCREAM_CODE_LOG_DIR_NAME = "logs";
|
|
|
121699
121739
|
const SCREAM_CODE_UPDATE_DIR_NAME = "updates";
|
|
121700
121740
|
const SCREAM_CODE_UPDATE_STATE_FILE_NAME = "latest.json";
|
|
121701
121741
|
const SCREAM_CODE_INPUT_HISTORY_DIR_NAME = "user-history";
|
|
121742
|
+
const SCREAM_CODE_UI_PREFERENCES_FILE_NAME = "ui-preferences.json";
|
|
121702
121743
|
const DEFAULT_OAUTH_PROVIDER_NAME = "managed:scream-code";
|
|
121703
121744
|
ErrorCodes.AUTH_LOGIN_REQUIRED;
|
|
121704
121745
|
const SCREAM_CODE_PLUGIN_MARKETPLACE_URL_ENV = "SCREAM_CODE_PLUGIN_MARKETPLACE_URL";
|
|
@@ -121775,7 +121816,7 @@ function optionalBuildString(value) {
|
|
|
121775
121816
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
121776
121817
|
}
|
|
121777
121818
|
const SCREAM_BUILD_INFO = {
|
|
121778
|
-
version: optionalBuildString("0.12.
|
|
121819
|
+
version: optionalBuildString("0.12.9"),
|
|
121779
121820
|
channel: optionalBuildString(""),
|
|
121780
121821
|
commit: optionalBuildString(""),
|
|
121781
121822
|
buildTarget: optionalBuildString("darwin-arm64")
|
|
@@ -122762,28 +122803,46 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
122762
122803
|
name: "auto",
|
|
122763
122804
|
aliases: [],
|
|
122764
122805
|
description: "registry.auto_desc",
|
|
122765
|
-
priority:
|
|
122806
|
+
priority: 220,
|
|
122766
122807
|
availability: "always"
|
|
122767
122808
|
},
|
|
122768
122809
|
{
|
|
122769
122810
|
name: "yes",
|
|
122770
122811
|
aliases: ["yolo"],
|
|
122771
122812
|
description: "registry.yolo_desc",
|
|
122772
|
-
priority:
|
|
122813
|
+
priority: 219,
|
|
122773
122814
|
availability: "always"
|
|
122774
122815
|
},
|
|
122775
122816
|
{
|
|
122776
122817
|
name: "ask",
|
|
122777
122818
|
aliases: ["ask"],
|
|
122778
122819
|
description: "registry.ask_desc",
|
|
122779
|
-
priority:
|
|
122820
|
+
priority: 218,
|
|
122821
|
+
availability: "always"
|
|
122822
|
+
},
|
|
122823
|
+
{
|
|
122824
|
+
name: "goal",
|
|
122825
|
+
aliases: ["goaloff"],
|
|
122826
|
+
description: "registry.goal_desc",
|
|
122827
|
+
argumentHint: "[objective]",
|
|
122828
|
+
priority: 217,
|
|
122829
|
+
availability: (args) => {
|
|
122830
|
+
const trimmed = args.trim();
|
|
122831
|
+
return trimmed === "" || trimmed === "status" || trimmed === "pause" || trimmed === "off" ? "always" : "idle-only";
|
|
122832
|
+
}
|
|
122833
|
+
},
|
|
122834
|
+
{
|
|
122835
|
+
name: "wolfpack",
|
|
122836
|
+
aliases: ["wp"],
|
|
122837
|
+
description: "registry.wolfpack_desc",
|
|
122838
|
+
priority: 216,
|
|
122780
122839
|
availability: "always"
|
|
122781
122840
|
},
|
|
122782
122841
|
{
|
|
122783
122842
|
name: "rlm",
|
|
122784
122843
|
aliases: ["rlm"],
|
|
122785
122844
|
description: "registry.rlm_desc",
|
|
122786
|
-
priority:
|
|
122845
|
+
priority: 215,
|
|
122787
122846
|
availability: "always"
|
|
122788
122847
|
},
|
|
122789
122848
|
{
|
|
@@ -122791,39 +122850,21 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
122791
122850
|
aliases: [],
|
|
122792
122851
|
description: "registry.rlm_max_depth_desc",
|
|
122793
122852
|
argumentHint: "[N]",
|
|
122794
|
-
priority:
|
|
122795
|
-
availability: "always"
|
|
122796
|
-
},
|
|
122797
|
-
{
|
|
122798
|
-
name: "wolfpack",
|
|
122799
|
-
aliases: ["wp"],
|
|
122800
|
-
description: "registry.wolfpack_desc",
|
|
122801
|
-
priority: 122,
|
|
122853
|
+
priority: 214,
|
|
122802
122854
|
availability: "always"
|
|
122803
122855
|
},
|
|
122804
122856
|
{
|
|
122805
122857
|
name: "sessions",
|
|
122806
122858
|
aliases: ["resume"],
|
|
122807
122859
|
description: "registry.sessions_desc",
|
|
122808
|
-
priority:
|
|
122809
|
-
},
|
|
122810
|
-
{
|
|
122811
|
-
name: "goal",
|
|
122812
|
-
aliases: ["goaloff"],
|
|
122813
|
-
description: "registry.goal_desc",
|
|
122814
|
-
argumentHint: "[objective]",
|
|
122815
|
-
priority: 120,
|
|
122816
|
-
availability: (args) => {
|
|
122817
|
-
const trimmed = args.trim();
|
|
122818
|
-
return trimmed === "" || trimmed === "status" || trimmed === "pause" || trimmed === "off" ? "always" : "idle-only";
|
|
122819
|
-
}
|
|
122860
|
+
priority: 213
|
|
122820
122861
|
},
|
|
122821
122862
|
{
|
|
122822
122863
|
name: "memory",
|
|
122823
122864
|
aliases: ["memo", "mem"],
|
|
122824
122865
|
description: "registry.memory_desc",
|
|
122825
122866
|
argumentHint: "[query]",
|
|
122826
|
-
priority:
|
|
122867
|
+
priority: 212,
|
|
122827
122868
|
availability: "always"
|
|
122828
122869
|
},
|
|
122829
122870
|
{
|
|
@@ -122831,96 +122872,61 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
122831
122872
|
aliases: ["know"],
|
|
122832
122873
|
description: "registry.knowledge_desc",
|
|
122833
122874
|
argumentHint: "[query]",
|
|
122834
|
-
priority:
|
|
122875
|
+
priority: 211,
|
|
122835
122876
|
availability: "always"
|
|
122836
122877
|
},
|
|
122837
|
-
{
|
|
122838
|
-
name: "new",
|
|
122839
|
-
aliases: ["clear"],
|
|
122840
|
-
description: "registry.new_desc",
|
|
122841
|
-
priority: 120
|
|
122842
|
-
},
|
|
122843
122878
|
{
|
|
122844
122879
|
name: "model",
|
|
122845
122880
|
aliases: [],
|
|
122846
122881
|
description: "registry.model_desc",
|
|
122847
122882
|
argumentHint: "[alias]",
|
|
122848
|
-
priority:
|
|
122883
|
+
priority: 210
|
|
122884
|
+
},
|
|
122885
|
+
{
|
|
122886
|
+
name: "new",
|
|
122887
|
+
aliases: ["clear"],
|
|
122888
|
+
description: "registry.new_desc",
|
|
122889
|
+
priority: 209
|
|
122849
122890
|
},
|
|
122850
122891
|
{
|
|
122851
122892
|
name: "compact",
|
|
122852
122893
|
aliases: [],
|
|
122853
122894
|
description: "registry.compact_desc",
|
|
122854
|
-
priority:
|
|
122895
|
+
priority: 208
|
|
122855
122896
|
},
|
|
122856
122897
|
{
|
|
122857
|
-
name: "
|
|
122858
|
-
aliases: ["
|
|
122859
|
-
description: "registry.
|
|
122860
|
-
priority:
|
|
122861
|
-
availability: "idle-only"
|
|
122898
|
+
name: "fusionplan",
|
|
122899
|
+
aliases: ["fp"],
|
|
122900
|
+
description: "registry.fusionplan_desc",
|
|
122901
|
+
priority: 207,
|
|
122902
|
+
availability: (args) => args.trim().toLowerCase() === "clear" ? "idle-only" : "always"
|
|
122862
122903
|
},
|
|
122863
122904
|
{
|
|
122864
122905
|
name: "plan",
|
|
122865
122906
|
aliases: [],
|
|
122866
122907
|
description: "registry.plan_desc",
|
|
122867
|
-
priority:
|
|
122868
|
-
availability: (args) => args.trim().toLowerCase() === "clear" ? "idle-only" : "always"
|
|
122869
|
-
},
|
|
122870
|
-
{
|
|
122871
|
-
name: "fusionplan",
|
|
122872
|
-
aliases: ["fp"],
|
|
122873
|
-
description: "registry.fusionplan_desc",
|
|
122874
|
-
priority: 118,
|
|
122908
|
+
priority: 206,
|
|
122875
122909
|
availability: (args) => args.trim().toLowerCase() === "clear" ? "idle-only" : "always"
|
|
122876
122910
|
},
|
|
122877
122911
|
{
|
|
122878
122912
|
name: "tasks",
|
|
122879
122913
|
aliases: ["task"],
|
|
122880
122914
|
description: "registry.tasks_desc",
|
|
122881
|
-
priority:
|
|
122882
|
-
availability: "always"
|
|
122883
|
-
},
|
|
122884
|
-
{
|
|
122885
|
-
name: "help",
|
|
122886
|
-
aliases: ["h", "?"],
|
|
122887
|
-
description: "registry.help_desc",
|
|
122888
|
-
priority: 116,
|
|
122889
|
-
availability: "always"
|
|
122890
|
-
},
|
|
122891
|
-
{
|
|
122892
|
-
name: "status",
|
|
122893
|
-
aliases: [],
|
|
122894
|
-
description: "registry.status_desc",
|
|
122895
|
-
priority: 115,
|
|
122896
|
-
availability: "always"
|
|
122897
|
-
},
|
|
122898
|
-
{
|
|
122899
|
-
name: "usage",
|
|
122900
|
-
aliases: [],
|
|
122901
|
-
description: "registry.usage_desc",
|
|
122902
|
-
priority: 114,
|
|
122915
|
+
priority: 205,
|
|
122903
122916
|
availability: "always"
|
|
122904
122917
|
},
|
|
122905
122918
|
{
|
|
122906
122919
|
name: "btw",
|
|
122907
122920
|
aliases: [],
|
|
122908
122921
|
description: "registry.btw_desc",
|
|
122909
|
-
priority:
|
|
122922
|
+
priority: 204,
|
|
122910
122923
|
availability: "always"
|
|
122911
122924
|
},
|
|
122912
122925
|
{
|
|
122913
122926
|
name: "like",
|
|
122914
122927
|
aliases: [],
|
|
122915
122928
|
description: "registry.like_desc",
|
|
122916
|
-
priority:
|
|
122917
|
-
availability: "always"
|
|
122918
|
-
},
|
|
122919
|
-
{
|
|
122920
|
-
name: "mcp",
|
|
122921
|
-
aliases: [],
|
|
122922
|
-
description: "registry.mcp_desc",
|
|
122923
|
-
priority: 112,
|
|
122929
|
+
priority: 203,
|
|
122924
122930
|
availability: "always"
|
|
122925
122931
|
},
|
|
122926
122932
|
{
|
|
@@ -122931,127 +122937,162 @@ const BUILTIN_SLASH_COMMANDS = [
|
|
|
122931
122937
|
"plugins"
|
|
122932
122938
|
],
|
|
122933
122939
|
description: "registry.skill_desc",
|
|
122934
|
-
priority:
|
|
122940
|
+
priority: 202,
|
|
122935
122941
|
availability: "always"
|
|
122936
122942
|
},
|
|
122937
122943
|
{
|
|
122938
|
-
name: "
|
|
122944
|
+
name: "fork",
|
|
122939
122945
|
aliases: [],
|
|
122940
|
-
description: "registry.
|
|
122941
|
-
priority:
|
|
122946
|
+
description: "registry.fork_desc",
|
|
122947
|
+
priority: 201
|
|
122948
|
+
},
|
|
122949
|
+
{
|
|
122950
|
+
name: "title",
|
|
122951
|
+
aliases: ["rename"],
|
|
122952
|
+
description: "registry.title_desc",
|
|
122953
|
+
priority: 200,
|
|
122942
122954
|
availability: "always"
|
|
122943
122955
|
},
|
|
122944
122956
|
{
|
|
122945
|
-
name: "
|
|
122957
|
+
name: "config",
|
|
122946
122958
|
aliases: [],
|
|
122947
|
-
description: "registry.
|
|
122948
|
-
priority:
|
|
122959
|
+
description: "registry.config_desc",
|
|
122960
|
+
priority: 199
|
|
122961
|
+
},
|
|
122962
|
+
{
|
|
122963
|
+
name: "help",
|
|
122964
|
+
aliases: ["h", "?"],
|
|
122965
|
+
description: "registry.help_desc",
|
|
122966
|
+
priority: 198,
|
|
122949
122967
|
availability: "always"
|
|
122950
122968
|
},
|
|
122951
122969
|
{
|
|
122952
|
-
name: "
|
|
122953
|
-
aliases: [],
|
|
122954
|
-
description: "registry.
|
|
122955
|
-
priority:
|
|
122970
|
+
name: "make-skill",
|
|
122971
|
+
aliases: ["makeskill", "craftskill"],
|
|
122972
|
+
description: "registry.make_skill_desc",
|
|
122973
|
+
priority: 197,
|
|
122956
122974
|
availability: "idle-only"
|
|
122957
122975
|
},
|
|
122958
122976
|
{
|
|
122959
|
-
name: "
|
|
122977
|
+
name: "mcp",
|
|
122960
122978
|
aliases: [],
|
|
122961
|
-
description: "registry.
|
|
122962
|
-
priority:
|
|
122979
|
+
description: "registry.mcp_desc",
|
|
122980
|
+
priority: 196,
|
|
122981
|
+
availability: "always"
|
|
122963
122982
|
},
|
|
122964
122983
|
{
|
|
122965
|
-
name: "
|
|
122966
|
-
aliases: [
|
|
122967
|
-
description: "registry.
|
|
122968
|
-
priority:
|
|
122984
|
+
name: "status",
|
|
122985
|
+
aliases: [],
|
|
122986
|
+
description: "registry.status_desc",
|
|
122987
|
+
priority: 195,
|
|
122969
122988
|
availability: "always"
|
|
122970
122989
|
},
|
|
122971
122990
|
{
|
|
122972
|
-
name: "
|
|
122991
|
+
name: "usage",
|
|
122973
122992
|
aliases: [],
|
|
122974
|
-
description: "registry.
|
|
122975
|
-
priority:
|
|
122993
|
+
description: "registry.usage_desc",
|
|
122994
|
+
priority: 194,
|
|
122995
|
+
availability: "always"
|
|
122976
122996
|
},
|
|
122977
122997
|
{
|
|
122978
|
-
name: "
|
|
122998
|
+
name: "revoke",
|
|
122979
122999
|
aliases: [],
|
|
122980
|
-
description: "registry.
|
|
122981
|
-
priority:
|
|
123000
|
+
description: "registry.revoke_desc",
|
|
123001
|
+
priority: 193,
|
|
123002
|
+
availability: "idle-only"
|
|
123003
|
+
},
|
|
123004
|
+
{
|
|
123005
|
+
name: "cc",
|
|
123006
|
+
aliases: [],
|
|
123007
|
+
description: "registry.cc_desc",
|
|
123008
|
+
priority: 192,
|
|
123009
|
+
availability: "always"
|
|
123010
|
+
},
|
|
123011
|
+
{
|
|
123012
|
+
name: "cc-connect",
|
|
123013
|
+
aliases: [],
|
|
123014
|
+
description: "registry.cc_connect_desc",
|
|
123015
|
+
priority: 191,
|
|
122982
123016
|
availability: "always"
|
|
122983
123017
|
},
|
|
122984
123018
|
{
|
|
122985
123019
|
name: "theme",
|
|
122986
123020
|
aliases: [],
|
|
122987
123021
|
description: "registry.theme_desc",
|
|
122988
|
-
priority:
|
|
123022
|
+
priority: 190,
|
|
122989
123023
|
availability: "always"
|
|
122990
123024
|
},
|
|
122991
123025
|
{
|
|
122992
123026
|
name: "language",
|
|
122993
123027
|
aliases: ["lang"],
|
|
122994
123028
|
description: "registry.language_desc",
|
|
122995
|
-
priority:
|
|
123029
|
+
priority: 189,
|
|
123030
|
+
availability: "always"
|
|
123031
|
+
},
|
|
123032
|
+
{
|
|
123033
|
+
name: "permission",
|
|
123034
|
+
aliases: [],
|
|
123035
|
+
description: "registry.permission_desc",
|
|
123036
|
+
priority: 188,
|
|
122996
123037
|
availability: "always"
|
|
122997
123038
|
},
|
|
122998
123039
|
{
|
|
122999
123040
|
name: "editor",
|
|
123000
123041
|
aliases: [],
|
|
123001
123042
|
description: "registry.editor_desc",
|
|
123002
|
-
priority:
|
|
123043
|
+
priority: 187,
|
|
123003
123044
|
availability: "always"
|
|
123004
123045
|
},
|
|
123005
123046
|
{
|
|
123006
123047
|
name: "settings",
|
|
123007
123048
|
aliases: [],
|
|
123008
123049
|
description: "registry.settings_desc",
|
|
123009
|
-
priority:
|
|
123050
|
+
priority: 186,
|
|
123010
123051
|
availability: "always"
|
|
123011
123052
|
},
|
|
123012
123053
|
{
|
|
123013
123054
|
name: "init",
|
|
123014
123055
|
aliases: [],
|
|
123015
123056
|
description: "registry.init_desc",
|
|
123016
|
-
priority:
|
|
123057
|
+
priority: 185
|
|
123017
123058
|
},
|
|
123018
123059
|
{
|
|
123019
123060
|
name: "export-md",
|
|
123020
123061
|
aliases: ["export"],
|
|
123021
123062
|
description: "registry.export_md_desc",
|
|
123022
|
-
priority:
|
|
123063
|
+
priority: 184
|
|
123023
123064
|
},
|
|
123024
123065
|
{
|
|
123025
123066
|
name: "export-debug-zip",
|
|
123026
123067
|
aliases: [],
|
|
123027
123068
|
description: "registry.export_debug_desc",
|
|
123028
|
-
priority:
|
|
123069
|
+
priority: 183
|
|
123070
|
+
},
|
|
123071
|
+
{
|
|
123072
|
+
name: "eval",
|
|
123073
|
+
aliases: [],
|
|
123074
|
+
description: "registry.eval_desc",
|
|
123075
|
+
priority: 182
|
|
123029
123076
|
},
|
|
123030
123077
|
{
|
|
123031
123078
|
name: "update",
|
|
123032
123079
|
aliases: [],
|
|
123033
123080
|
description: "registry.update_desc",
|
|
123034
|
-
priority:
|
|
123081
|
+
priority: 181,
|
|
123035
123082
|
availability: "idle-only"
|
|
123036
123083
|
},
|
|
123037
123084
|
{
|
|
123038
123085
|
name: "version",
|
|
123039
123086
|
aliases: [],
|
|
123040
123087
|
description: "registry.version_desc",
|
|
123041
|
-
priority:
|
|
123088
|
+
priority: 180,
|
|
123042
123089
|
availability: "always"
|
|
123043
123090
|
},
|
|
123044
123091
|
{
|
|
123045
123092
|
name: "logout",
|
|
123046
123093
|
aliases: ["disconnect"],
|
|
123047
123094
|
description: "registry.logout_desc",
|
|
123048
|
-
priority:
|
|
123049
|
-
},
|
|
123050
|
-
{
|
|
123051
|
-
name: "eval",
|
|
123052
|
-
aliases: [],
|
|
123053
|
-
description: "registry.eval_desc",
|
|
123054
|
-
priority: 92
|
|
123095
|
+
priority: 179
|
|
123055
123096
|
},
|
|
123056
123097
|
{
|
|
123057
123098
|
name: "exit",
|
|
@@ -123171,6 +123212,8 @@ function getCtrlCHint() {
|
|
|
123171
123212
|
}
|
|
123172
123213
|
const MAIN_AGENT_ID$1 = "main";
|
|
123173
123214
|
const EXIT_CONFIRM_WINDOW_MS = 1500;
|
|
123215
|
+
/** Partner model-provider page opened by Ctrl+F while the chat is empty. */
|
|
123216
|
+
const EMPTY_SESSION_HINT_URL = "https://opencode.ai/go?ref=75NKVRZQCY";
|
|
123174
123217
|
function isManagedUsageProvider(providerKey) {
|
|
123175
123218
|
return providerKey === DEFAULT_OAUTH_PROVIDER_NAME;
|
|
123176
123219
|
}
|
|
@@ -125693,7 +125736,15 @@ const HEADING_HASH_PREFIX = /^((?:\u001B\[[0-9;]*m)*)#{1,6}[ \t]+/;
|
|
|
125693
125736
|
* takes one formatter function per token; tokens not listed here fall back to
|
|
125694
125737
|
* its DEFAULT_THEME.
|
|
125695
125738
|
*/
|
|
125696
|
-
|
|
125739
|
+
/**
|
|
125740
|
+
* Markdown code-block highlight theme: green-dominant mapping (keyword,
|
|
125741
|
+
* function, built_in → primary; strings → success; numbers → warning;
|
|
125742
|
+
* comments → textDim). Kept distinct from the shared preview theme in
|
|
125743
|
+
* code-highlight-theme.ts on purpose — markdown code blocks use the green
|
|
125744
|
+
* primary hue, while file-preview panels use the classic blue/red/yellow
|
|
125745
|
+
* scheme mapped to the same palette. Both follow the active theme.
|
|
125746
|
+
*/
|
|
125747
|
+
function createMarkdownCodeHighlightTheme(colors) {
|
|
125697
125748
|
const keyword = chalk.hex(colors.primary);
|
|
125698
125749
|
const str = chalk.hex(colors.success);
|
|
125699
125750
|
const comment = chalk.hex(colors.textDim);
|
|
@@ -125743,7 +125794,7 @@ function createMarkdownTheme(colors) {
|
|
|
125743
125794
|
const stripHash = (text) => text.replace(HEADING_HASH_PREFIX, "$1");
|
|
125744
125795
|
const muted = chalk.hex(colors.textMuted);
|
|
125745
125796
|
const border = chalk.hex(colors.border);
|
|
125746
|
-
const codeTheme =
|
|
125797
|
+
const codeTheme = createMarkdownCodeHighlightTheme(colors);
|
|
125747
125798
|
return {
|
|
125748
125799
|
heading: (text) => chalk.bold.hex(colors.text)(stripHash(text)),
|
|
125749
125800
|
link: (text) => chalk.hex(colors.mdLink)(text),
|
|
@@ -127595,7 +127646,7 @@ async function guidedGoalSetup(host) {
|
|
|
127595
127646
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
127596
127647
|
return;
|
|
127597
127648
|
}
|
|
127598
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
127649
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-DXLBq_rF.mjs");
|
|
127599
127650
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
127600
127651
|
title: t("goal.setup_title_initial"),
|
|
127601
127652
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -127616,7 +127667,7 @@ async function guidedGoalSetup(host) {
|
|
|
127616
127667
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
127617
127668
|
}
|
|
127618
127669
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
127619
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
127670
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-DXLBq_rF.mjs");
|
|
127620
127671
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
127621
127672
|
title: t("goal.wizard_title", { objective }),
|
|
127622
127673
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -128981,6 +129032,60 @@ var CachedContainer = class extends Container {
|
|
|
128981
129032
|
}
|
|
128982
129033
|
};
|
|
128983
129034
|
//#endregion
|
|
129035
|
+
//#region src/tui/theme/code-highlight-theme.ts
|
|
129036
|
+
function createCodeHighlightTheme(colors) {
|
|
129037
|
+
const keyword = chalk.hex(colors.mdCodeBlock);
|
|
129038
|
+
const str = chalk.hex(colors.error);
|
|
129039
|
+
const num = chalk.hex(colors.primary);
|
|
129040
|
+
const fn = chalk.hex(colors.warning);
|
|
129041
|
+
const builtin = chalk.hex(colors.planMode);
|
|
129042
|
+
const cls = chalk.hex(colors.accent);
|
|
129043
|
+
const text = chalk.hex(colors.text);
|
|
129044
|
+
return {
|
|
129045
|
+
keyword,
|
|
129046
|
+
built_in: builtin,
|
|
129047
|
+
type: keyword,
|
|
129048
|
+
literal: builtin,
|
|
129049
|
+
number: num,
|
|
129050
|
+
regexp: str,
|
|
129051
|
+
string: str,
|
|
129052
|
+
subst: str,
|
|
129053
|
+
symbol: num,
|
|
129054
|
+
class: cls,
|
|
129055
|
+
function: fn,
|
|
129056
|
+
title: fn,
|
|
129057
|
+
params: text,
|
|
129058
|
+
comment: num,
|
|
129059
|
+
doctag: num,
|
|
129060
|
+
meta: chalk.hex(colors.textMuted),
|
|
129061
|
+
"meta-keyword": keyword,
|
|
129062
|
+
"meta-string": str,
|
|
129063
|
+
section: keyword,
|
|
129064
|
+
tag: keyword,
|
|
129065
|
+
name: fn,
|
|
129066
|
+
"builtin-name": builtin,
|
|
129067
|
+
attr: fn,
|
|
129068
|
+
attribute: fn,
|
|
129069
|
+
variable: text,
|
|
129070
|
+
bullet: num,
|
|
129071
|
+
code: str,
|
|
129072
|
+
emphasis: (s) => chalk.italic(s),
|
|
129073
|
+
strong: (s) => chalk.bold(s),
|
|
129074
|
+
formula: text,
|
|
129075
|
+
link: chalk.hex(colors.mdLink),
|
|
129076
|
+
quote: chalk.hex(colors.mdQuote),
|
|
129077
|
+
addition: chalk.hex(colors.diffAdded),
|
|
129078
|
+
deletion: chalk.hex(colors.diffRemoved),
|
|
129079
|
+
"selector-tag": keyword,
|
|
129080
|
+
"selector-id": cls,
|
|
129081
|
+
"selector-class": cls,
|
|
129082
|
+
"selector-attr": fn,
|
|
129083
|
+
"selector-pseudo": fn,
|
|
129084
|
+
"template-tag": str,
|
|
129085
|
+
"template-variable": fn
|
|
129086
|
+
};
|
|
129087
|
+
}
|
|
129088
|
+
//#endregion
|
|
128984
129089
|
//#region src/tui/components/media/code-highlight.ts
|
|
128985
129090
|
/**
|
|
128986
129091
|
* Shared syntax-highlighting helpers for code previews
|
|
@@ -129018,13 +129123,14 @@ function langFromPath(filePath) {
|
|
|
129018
129123
|
const lang = EXT_LANG_MAP[ext] ?? ext;
|
|
129019
129124
|
return supportsLanguage(lang) ? lang : void 0;
|
|
129020
129125
|
}
|
|
129021
|
-
function highlightLines(code, lang) {
|
|
129126
|
+
function highlightLines(code, lang, colors) {
|
|
129022
129127
|
const normalizedLang = lang?.trim().toLowerCase();
|
|
129023
129128
|
if (!normalizedLang || !supportsLanguage(normalizedLang)) return code.split("\n");
|
|
129024
129129
|
try {
|
|
129025
129130
|
return highlight(code, {
|
|
129026
129131
|
language: normalizedLang,
|
|
129027
|
-
ignoreIllegals: true
|
|
129132
|
+
ignoreIllegals: true,
|
|
129133
|
+
theme: createCodeHighlightTheme(colors)
|
|
129028
129134
|
}).split("\n");
|
|
129029
129135
|
} catch {
|
|
129030
129136
|
return code.split("\n");
|
|
@@ -129086,10 +129192,12 @@ function visualizeIndent(line) {
|
|
|
129086
129192
|
}
|
|
129087
129193
|
/**
|
|
129088
129194
|
* Render a diff line's code with leading-whitespace visualization (tabs as
|
|
129089
|
-
* `->`, spaces as `·`, dimmed) and optional syntax highlighting.
|
|
129090
|
-
* runs on the raw code
|
|
129091
|
-
*
|
|
129092
|
-
*
|
|
129195
|
+
* `->`, spaces as `·`, dimmed) and optional syntax highlighting. Indent
|
|
129196
|
+
* visualization runs on the raw code FIRST, then highlighting runs on the
|
|
129197
|
+
* stripped remainder: once the text is ANSI-tinted (the theme's `default`
|
|
129198
|
+
* color wraps whitespace too), a leading tab is no longer the first raw
|
|
129199
|
+
* character and would be missed. The dimmed indent itself carries no token
|
|
129200
|
+
* color.
|
|
129093
129201
|
*
|
|
129094
129202
|
* When highlighting is on and produced token colors, the diff line color is
|
|
129095
129203
|
* layered as the base foreground: syntax tokens override it, but every ANSI
|
|
@@ -129098,17 +129206,18 @@ function visualizeIndent(line) {
|
|
|
129098
129206
|
* When highlighting is off (streaming) or produced no token colors, the code
|
|
129099
129207
|
* part is colored with the diff line color instead.
|
|
129100
129208
|
*/
|
|
129101
|
-
function renderDiffCode(code, colorFn, highlight, lang) {
|
|
129102
|
-
const { text, indentEnd } = visualizeIndent(
|
|
129209
|
+
function renderDiffCode(code, colorFn, highlight, lang, colors) {
|
|
129210
|
+
const { text, indentEnd } = visualizeIndent(code);
|
|
129103
129211
|
const indent = text.slice(0, indentEnd);
|
|
129104
129212
|
const rest = text.slice(indentEnd);
|
|
129105
129213
|
const dimIndent = indent.length > 0 ? chalk.dim(indent) : indent;
|
|
129106
|
-
|
|
129214
|
+
const source = highlight ? highlightLines(rest, lang, colors)[0] ?? rest : rest;
|
|
129215
|
+
if (highlight && ANSI_RE.test(source)) {
|
|
129107
129216
|
const prefix = extractAnsiPrefix(colorFn);
|
|
129108
|
-
if (prefix.length > 0) return dimIndent + prefix +
|
|
129109
|
-
return dimIndent +
|
|
129217
|
+
if (prefix.length > 0) return dimIndent + prefix + source.replace(ANSI_RESET_RE, (m) => m + prefix) + "\x1B[39m";
|
|
129218
|
+
return dimIndent + source;
|
|
129110
129219
|
}
|
|
129111
|
-
return dimIndent + colorFn(
|
|
129220
|
+
return dimIndent + colorFn(source);
|
|
129112
129221
|
}
|
|
129113
129222
|
/**
|
|
129114
129223
|
* Compute word-level diff between two single lines and highlight the changed
|
|
@@ -129242,7 +129351,7 @@ function renderDiffLines(oldText, newText, path, colors, isIncomplete = false, o
|
|
|
129242
129351
|
const line = shown[i];
|
|
129243
129352
|
const marker = line.kind === "add" ? "+" : "-";
|
|
129244
129353
|
const color = line.kind === "add" ? s.add : s.del;
|
|
129245
|
-
output.push(s.gutter(String(line.lineNum).padStart(4) + " ") + color(`${marker} `) + renderDiffCode(line.code, color, doHighlight, lang));
|
|
129354
|
+
output.push(s.gutter(String(line.lineNum).padStart(4) + " ") + color(`${marker} `) + renderDiffCode(line.code, color, doHighlight, lang, colors));
|
|
129246
129355
|
i += 1;
|
|
129247
129356
|
}
|
|
129248
129357
|
const hidden = changedLines.length - shown.length;
|
|
@@ -129293,11 +129402,11 @@ function buildClusters(diffLines, contextLines) {
|
|
|
129293
129402
|
removedCount: removed
|
|
129294
129403
|
};
|
|
129295
129404
|
}
|
|
129296
|
-
function formatDiffRow(line, s, doHighlight, lang) {
|
|
129405
|
+
function formatDiffRow(line, s, doHighlight, lang, colors) {
|
|
129297
129406
|
const gutter = s.gutter(String(line.lineNum).padStart(4) + " ");
|
|
129298
|
-
if (line.kind === "add") return gutter + s.add("+ ") + renderDiffCode(line.code, s.add, doHighlight, lang);
|
|
129299
|
-
if (line.kind === "delete") return gutter + s.del("- ") + renderDiffCode(line.code, s.del, doHighlight, lang);
|
|
129300
|
-
return gutter + " " + renderDiffCode(line.code, (x) => x, doHighlight, lang);
|
|
129407
|
+
if (line.kind === "add") return gutter + s.add("+ ") + renderDiffCode(line.code, s.add, doHighlight, lang, colors);
|
|
129408
|
+
if (line.kind === "delete") return gutter + s.del("- ") + renderDiffCode(line.code, s.del, doHighlight, lang, colors);
|
|
129409
|
+
return gutter + " " + renderDiffCode(line.code, (x) => x, doHighlight, lang, colors);
|
|
129301
129410
|
}
|
|
129302
129411
|
/**
|
|
129303
129412
|
* Render a diff with surrounding context, eliding unchanged middle
|
|
@@ -129368,7 +129477,7 @@ function renderDiffLinesClustered(oldText, newText, path, colors, opts = {}) {
|
|
|
129368
129477
|
i += 2;
|
|
129369
129478
|
continue;
|
|
129370
129479
|
}
|
|
129371
|
-
output.push(formatDiffRow(line, s, doHighlight, lang));
|
|
129480
|
+
output.push(formatDiffRow(line, s, doHighlight, lang, colors));
|
|
129372
129481
|
body++;
|
|
129373
129482
|
if (line.kind !== "context") shownChanges++;
|
|
129374
129483
|
prevEnd = i;
|
|
@@ -131251,7 +131360,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
131251
131360
|
if (name === "Write") {
|
|
131252
131361
|
const content = str(this.toolCall.args["content"]);
|
|
131253
131362
|
if (content.length === 0) return;
|
|
131254
|
-
const allLines = highlightLines(content, langFromPath(str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"])));
|
|
131363
|
+
const allLines = highlightLines(content, langFromPath(str(this.toolCall.args["file_path"] ?? this.toolCall.args["path"])), this.colors);
|
|
131255
131364
|
const writeShouldCap = !this.expanded;
|
|
131256
131365
|
const shown = writeShouldCap ? allLines.slice(0, 10) : allLines;
|
|
131257
131366
|
const remaining = allLines.length - shown.length;
|
|
@@ -131356,7 +131465,7 @@ var ToolCallComponent = class ToolCallComponent extends CachedContainer {
|
|
|
131356
131465
|
const fragmentEnd = Math.min(streamText.length, fragmentStart + ToolCallComponent.WRITE_STREAM_TAIL_RAW_CHARS);
|
|
131357
131466
|
const fragment = unescapeJsonStringValue(streamText, fragmentStart, fragmentEnd);
|
|
131358
131467
|
if (fragment.length === 0) return;
|
|
131359
|
-
const lines = highlightLines(fragment, this.writeStreamLang);
|
|
131468
|
+
const lines = highlightLines(fragment, this.writeStreamLang, this.colors);
|
|
131360
131469
|
const displayLines = lines.slice(-10);
|
|
131361
131470
|
const firstFragmentLineNo = this.writeStreamNlCount + (tailStart > contentStart ? 2 : 1);
|
|
131362
131471
|
const skipped = lines.length - displayLines.length;
|
|
@@ -137552,6 +137661,51 @@ function shellQuote(path) {
|
|
|
137552
137661
|
return `'${path.replaceAll("'", "'\\''")}'`;
|
|
137553
137662
|
}
|
|
137554
137663
|
//#endregion
|
|
137664
|
+
//#region src/tui/utils/ui-preferences.ts
|
|
137665
|
+
/**
|
|
137666
|
+
* TUI-local UI preferences persisted to `<dataDir>/ui-preferences.json`.
|
|
137667
|
+
* Distinct from agent/SDK config files (which are owned by core) — this file
|
|
137668
|
+
* stores lightweight view-state knobs owned by the TUI itself, e.g. whether
|
|
137669
|
+
* the empty-session provider hint has been dismissed by the user.
|
|
137670
|
+
*/
|
|
137671
|
+
const EMPTY = {};
|
|
137672
|
+
function getUiPreferencesPath() {
|
|
137673
|
+
return join(getDataDir(), SCREAM_CODE_UI_PREFERENCES_FILE_NAME);
|
|
137674
|
+
}
|
|
137675
|
+
function readUiPreferences() {
|
|
137676
|
+
try {
|
|
137677
|
+
const file = getUiPreferencesPath();
|
|
137678
|
+
if (!existsSync(file)) return { ...EMPTY };
|
|
137679
|
+
const raw = readFileSync(file, "utf8");
|
|
137680
|
+
const parsed = JSON.parse(raw);
|
|
137681
|
+
return {
|
|
137682
|
+
...EMPTY,
|
|
137683
|
+
...parsed
|
|
137684
|
+
};
|
|
137685
|
+
} catch {
|
|
137686
|
+
return { ...EMPTY };
|
|
137687
|
+
}
|
|
137688
|
+
}
|
|
137689
|
+
function writeUiPreferences(prefs) {
|
|
137690
|
+
try {
|
|
137691
|
+
const file = getUiPreferencesPath();
|
|
137692
|
+
mkdirSync(dirname$1(file), { recursive: true });
|
|
137693
|
+
writeFileSync(file, JSON.stringify(prefs, null, 2), "utf8");
|
|
137694
|
+
} catch {}
|
|
137695
|
+
}
|
|
137696
|
+
function isEmptySessionHintDismissed() {
|
|
137697
|
+
return readUiPreferences().emptySessionHintDismissed === true;
|
|
137698
|
+
}
|
|
137699
|
+
/** Toggle the empty-session hint on/off (Ctrl+B). Returns the new state:
|
|
137700
|
+
* true = hidden, false = shown. Persisted immediately. */
|
|
137701
|
+
function toggleEmptySessionHint() {
|
|
137702
|
+
const prefs = readUiPreferences();
|
|
137703
|
+
const dismissed = prefs.emptySessionHintDismissed !== true;
|
|
137704
|
+
prefs.emptySessionHintDismissed = dismissed;
|
|
137705
|
+
writeUiPreferences(prefs);
|
|
137706
|
+
return dismissed;
|
|
137707
|
+
}
|
|
137708
|
+
//#endregion
|
|
137555
137709
|
//#region src/tui/controllers/editor-keyboard.ts
|
|
137556
137710
|
var EditorKeyboardController = class {
|
|
137557
137711
|
host;
|
|
@@ -137605,6 +137759,18 @@ var EditorKeyboardController = class {
|
|
|
137605
137759
|
}
|
|
137606
137760
|
this.armPendingExit("ctrl-d", getCtrlDHint());
|
|
137607
137761
|
};
|
|
137762
|
+
editor.onCtrlF = () => {
|
|
137763
|
+
if (host.state.transcriptEntries.length > 0) return false;
|
|
137764
|
+
if (isEmptySessionHintDismissed()) return false;
|
|
137765
|
+
openUrl(EMPTY_SESSION_HINT_URL);
|
|
137766
|
+
return true;
|
|
137767
|
+
};
|
|
137768
|
+
editor.onCtrlB = () => {
|
|
137769
|
+
if (host.state.transcriptEntries.length > 0) return false;
|
|
137770
|
+
toggleEmptySessionHint();
|
|
137771
|
+
host.forceUpdateStatusBar();
|
|
137772
|
+
return true;
|
|
137773
|
+
};
|
|
137608
137774
|
editor.onEscape = () => {
|
|
137609
137775
|
if (this.pendingExit) this.clearPendingExit();
|
|
137610
137776
|
if (host.state.activeDialog === "session-picker") {
|
|
@@ -138164,6 +138330,8 @@ var SessionEventHandler = class {
|
|
|
138164
138330
|
this.renderedSkillActivationIds.clear();
|
|
138165
138331
|
this.renderedMcpServerStatusKeys.clear();
|
|
138166
138332
|
this.stopAllMcpServerStatusSpinners();
|
|
138333
|
+
this.pendingSkillCandidates = [];
|
|
138334
|
+
this.promptedSkillCandidates.clear();
|
|
138167
138335
|
this.host.setAppState({ subagentUsage: {} });
|
|
138168
138336
|
}
|
|
138169
138337
|
startSubscription() {
|
|
@@ -138419,6 +138587,12 @@ var SessionEventHandler = class {
|
|
|
138419
138587
|
const todos = this.host.state.todoPanel.getTodos();
|
|
138420
138588
|
if (todos.length > 0 && todos.every((todo) => todo.status === "done")) this.host.streamingUI.setTodoList([]);
|
|
138421
138589
|
this.host.streamingUI.resetToolUi();
|
|
138590
|
+
if (this.pendingSkillCandidates.length > 0 && this.host.state.queuedMessages.length === 0) {
|
|
138591
|
+
const pending = this.pendingSkillCandidates;
|
|
138592
|
+
this.pendingSkillCandidates = [];
|
|
138593
|
+
const session = this.host.session;
|
|
138594
|
+
if (session !== void 0) for (const candidate of pending) this.promptSkillCandidate(session, candidate.name, candidate.purpose);
|
|
138595
|
+
}
|
|
138422
138596
|
this.host.streamingUI.finalizeTurn(sendQueued);
|
|
138423
138597
|
}
|
|
138424
138598
|
handleStepBegin(event) {
|
|
@@ -138793,6 +138967,10 @@ var SessionEventHandler = class {
|
|
|
138793
138967
|
}
|
|
138794
138968
|
/** Candidates already prompted this session, to avoid repeating the request. */
|
|
138795
138969
|
promptedSkillCandidates = /* @__PURE__ */ new Set();
|
|
138970
|
+
/** Skill candidates detected while a turn was still active. Flushed at the
|
|
138971
|
+
* next turn end so the AskUserQuestion dialog never interrupts an ongoing
|
|
138972
|
+
* response. */
|
|
138973
|
+
pendingSkillCandidates = [];
|
|
138796
138974
|
/**
|
|
138797
138975
|
* Handles a `skill_candidate` event emitted after compaction detected a
|
|
138798
138976
|
* reusable process in the summary. Best-effort and non-fatal: if the session
|
|
@@ -138800,6 +138978,10 @@ var SessionEventHandler = class {
|
|
|
138800
138978
|
* no confirmation. When it works, the model runs an AskUserQuestion dialog
|
|
138801
138979
|
* (确定/忽略) so the user can confirm or dismiss with a single keystroke,
|
|
138802
138980
|
* then generates the skill via MakeSkillPlanTool on confirmation.
|
|
138981
|
+
*
|
|
138982
|
+
* If a turn is still streaming (the compaction finished while the agent was
|
|
138983
|
+
* mid-response), the candidate is queued and prompted at the next turn end —
|
|
138984
|
+
* never interrupting the running turn.
|
|
138803
138985
|
*/
|
|
138804
138986
|
handleSkillCandidate(event) {
|
|
138805
138987
|
try {
|
|
@@ -138809,10 +138991,25 @@ var SessionEventHandler = class {
|
|
|
138809
138991
|
this.promptedSkillCandidates.add(name);
|
|
138810
138992
|
const session = this.host.session;
|
|
138811
138993
|
if (session === void 0) return;
|
|
138812
|
-
|
|
138813
|
-
|
|
138994
|
+
if (this.host.streamingUI.hasActiveTurn()) {
|
|
138995
|
+
this.pendingSkillCandidates.push({
|
|
138996
|
+
name,
|
|
138997
|
+
purpose
|
|
138998
|
+
});
|
|
138999
|
+
return;
|
|
139000
|
+
}
|
|
139001
|
+
this.promptSkillCandidate(session, name, purpose);
|
|
138814
139002
|
} catch {}
|
|
138815
139003
|
}
|
|
139004
|
+
/** Fire the AskUserQuestion prompt for a skill candidate. On failure, surface
|
|
139005
|
+
* a status hint instead of failing silently so the user still learns the
|
|
139006
|
+
* candidate was detected. */
|
|
139007
|
+
promptSkillCandidate(session, name, purpose) {
|
|
139008
|
+
const request = `检测到可复用过程「${name}」${purpose ? `(${purpose})` : ""}。请用 AskUserQuestion 工具询问用户是否将其保存为可复用技能(选项:生成 / 忽略)。用户选择「生成」后,用 MakeSkillPlanTool 生成该技能;选择「忽略」则直接说明已跳过。`;
|
|
139009
|
+
session.prompt(request).catch(() => {
|
|
139010
|
+
this.host.showStatus(`检测到可复用过程「${name}」,但询问失败。可稍后手动处理。`, this.host.state.theme.colors.warning);
|
|
139011
|
+
});
|
|
139012
|
+
}
|
|
138816
139013
|
finishCompaction(sendQueued) {
|
|
138817
139014
|
if (!this.host.streamingUI.hasActiveTurn()) {
|
|
138818
139015
|
this.host.setAppState({
|
|
@@ -139293,6 +139490,7 @@ var SessionReplayRenderer = class {
|
|
|
139293
139490
|
return false;
|
|
139294
139491
|
} finally {
|
|
139295
139492
|
this.host.setAppState({ isReplaying: false });
|
|
139493
|
+
this.host.forceUpdateStatusBar();
|
|
139296
139494
|
}
|
|
139297
139495
|
}
|
|
139298
139496
|
hydrateSnapshot(agent) {
|
|
@@ -141831,6 +142029,7 @@ var TranscriptController = class TranscriptController {
|
|
|
141831
142029
|
state.errorBanner.clear();
|
|
141832
142030
|
imageStore.clear();
|
|
141833
142031
|
this.renderWelcome();
|
|
142032
|
+
this.host.forceUpdateStatusBar();
|
|
141834
142033
|
}
|
|
141835
142034
|
showStatus(message, color) {
|
|
141836
142035
|
this.host.state.transcriptContainer.addChild(new StatusMessageComponent(message, this.host.state.theme.colors, color));
|
|
@@ -141937,7 +142136,13 @@ var StatusBarPaneComponent = class extends Container {
|
|
|
141937
142136
|
* swaps which children are visible. */
|
|
141938
142137
|
update(options) {
|
|
141939
142138
|
this.clear();
|
|
141940
|
-
if (options.mode === "idle")
|
|
142139
|
+
if (options.mode === "idle") {
|
|
142140
|
+
if (options.label.length > 0) {
|
|
142141
|
+
const text = options.labelColor !== void 0 ? chalk.hex(options.labelColor)(options.label) : options.label;
|
|
142142
|
+
this.addChild(new Text(text, 1, 0));
|
|
142143
|
+
}
|
|
142144
|
+
return;
|
|
142145
|
+
}
|
|
141941
142146
|
if (options.mode === "waiting" || options.mode === "tool" || options.mode === "composing" || options.mode === "thinking") {
|
|
141942
142147
|
if (options.pulseWave !== void 0) {
|
|
141943
142148
|
if (options.label.length > 0) {
|
|
@@ -142552,10 +142757,23 @@ var LifecycleController = class LifecycleController {
|
|
|
142552
142757
|
break;
|
|
142553
142758
|
case "thinking": break;
|
|
142554
142759
|
case "idle":
|
|
142555
|
-
case "hidden":
|
|
142760
|
+
case "hidden":
|
|
142761
|
+
if (mode === "idle" && state.transcriptEntries.length === 0 && !isEmptySessionHintDismissed()) state.statusBarContainer.addChild(new StatusBarPaneComponent({
|
|
142762
|
+
mode: "idle",
|
|
142763
|
+
label: t("editor.empty_session_hint"),
|
|
142764
|
+
labelColor: state.theme.colors.textDim
|
|
142765
|
+
}));
|
|
142766
|
+
break;
|
|
142556
142767
|
}
|
|
142557
142768
|
state.ui.requestRender();
|
|
142558
142769
|
}
|
|
142770
|
+
/** Force a status-bar refresh even when the mode is unchanged. Used after
|
|
142771
|
+
* session switches/clears: the transcript is emptied, so the empty-session
|
|
142772
|
+
* hint must (re)appear even though idle -> idle normally short-circuits. */
|
|
142773
|
+
forceUpdateStatusBar() {
|
|
142774
|
+
this.lastActivityMode = void 0;
|
|
142775
|
+
this.updateActivityPane();
|
|
142776
|
+
}
|
|
142559
142777
|
resolveActivityPaneMode() {
|
|
142560
142778
|
const { state } = this.host;
|
|
142561
142779
|
if (state.activeDialog !== null) return "hidden";
|
|
@@ -143720,7 +143938,7 @@ function imagePathBoundaryEnd(payload, segmentStart, extensionEnd) {
|
|
|
143720
143938
|
function normalizePastedImagePath(path) {
|
|
143721
143939
|
const trimmed = path.trim();
|
|
143722
143940
|
const first = trimmed[0];
|
|
143723
|
-
const last = trimmed
|
|
143941
|
+
const last = trimmed.at(-1);
|
|
143724
143942
|
return (trimmed.length > 1 && (first === "\"" || first === "'") && last === first ? trimmed.slice(1, -1) : trimmed).replace(SHELL_ESCAPED_PATH_CHAR_REGEX, "$1");
|
|
143725
143943
|
}
|
|
143726
143944
|
/**
|
|
@@ -143818,6 +144036,11 @@ var CustomEditor = class extends Editor {
|
|
|
143818
144036
|
onEscape;
|
|
143819
144037
|
onCtrlD;
|
|
143820
144038
|
onCtrlC;
|
|
144039
|
+
/** Return true when the key was handled (consumed); false falls through to
|
|
144040
|
+
* the base editor's own binding (pi-tui maps Ctrl+F to cursorRight). */
|
|
144041
|
+
onCtrlF;
|
|
144042
|
+
/** Same contract as onCtrlF: true consumes the key, false falls through. */
|
|
144043
|
+
onCtrlB;
|
|
143821
144044
|
onToggleToolExpand;
|
|
143822
144045
|
onTogglePlanExpand;
|
|
143823
144046
|
onOpenExternalEditor;
|
|
@@ -143978,6 +144201,12 @@ var CustomEditor = class extends Editor {
|
|
|
143978
144201
|
this.onCtrlC?.();
|
|
143979
144202
|
return;
|
|
143980
144203
|
}
|
|
144204
|
+
if (matchesKey(normalized, Key.ctrl("b"))) {
|
|
144205
|
+
if (this.onCtrlB?.() ?? false) return;
|
|
144206
|
+
}
|
|
144207
|
+
if (matchesKey(normalized, Key.ctrl("f"))) {
|
|
144208
|
+
if (this.onCtrlF?.() ?? false) return;
|
|
144209
|
+
}
|
|
143981
144210
|
if (matchesKey(normalized, Key.ctrl("g"))) {
|
|
143982
144211
|
this.onOpenExternalEditor?.();
|
|
143983
144212
|
return;
|
|
@@ -145163,7 +145392,7 @@ function renderDisplayBlock(block, s, colors) {
|
|
|
145163
145392
|
});
|
|
145164
145393
|
case "file_content": {
|
|
145165
145394
|
const lang = block.language ?? langFromPath(block.path);
|
|
145166
|
-
const allLines = highlightLines(replaceTabs(block.content), lang);
|
|
145395
|
+
const allLines = highlightLines(replaceTabs(block.content), lang, colors);
|
|
145167
145396
|
const shown = allLines.slice(0, CONTENT_SUMMARY_MAX_LINES);
|
|
145168
145397
|
const lines = [s.strong(block.path)];
|
|
145169
145398
|
for (const [i, line] of shown.entries()) lines.push(s.gutter(String(i + 1).padStart(4) + " ") + line);
|
|
@@ -145574,7 +145803,7 @@ function buildDiffBody(block, colors) {
|
|
|
145574
145803
|
}
|
|
145575
145804
|
function buildFileContentBody(block, colors) {
|
|
145576
145805
|
const lang = block.language ?? langFromPath(block.path);
|
|
145577
|
-
const highlighted = highlightLines(block.content, lang);
|
|
145806
|
+
const highlighted = highlightLines(block.content, lang, colors);
|
|
145578
145807
|
const gutter = chalk.hex(colors.diffGutter);
|
|
145579
145808
|
return {
|
|
145580
145809
|
lines: highlighted.map((line, i) => gutter(String(i + 1).padStart(4) + " ") + line),
|
|
@@ -147030,6 +147259,7 @@ var DialogManager = class {
|
|
|
147030
147259
|
this.host.state.editorContainer.addChild(panel);
|
|
147031
147260
|
this.host.state.ui.setFocus(panel);
|
|
147032
147261
|
this.host.state.ui.requestRender();
|
|
147262
|
+
this.host.forceUpdateStatusBar();
|
|
147033
147263
|
}
|
|
147034
147264
|
restoreEditor() {
|
|
147035
147265
|
disposeChildren(this.host.state.editorContainer);
|
|
@@ -147037,6 +147267,7 @@ var DialogManager = class {
|
|
|
147037
147267
|
this.host.state.ui.setFocus(this.host.state.editor);
|
|
147038
147268
|
this.host.state.activeDialog = null;
|
|
147039
147269
|
this.host.state.ui.requestRender();
|
|
147270
|
+
this.host.forceUpdateStatusBar();
|
|
147040
147271
|
}
|
|
147041
147272
|
showHelpPanel(commands) {
|
|
147042
147273
|
this.host.state.activeDialog = "help";
|
|
@@ -147775,6 +148006,9 @@ var ScreamTUI = class {
|
|
|
147775
148006
|
showStatus(message, color) {
|
|
147776
148007
|
this.transcriptController.showStatus(message, color);
|
|
147777
148008
|
}
|
|
148009
|
+
forceUpdateStatusBar() {
|
|
148010
|
+
this.lifecycleController.forceUpdateStatusBar();
|
|
148011
|
+
}
|
|
147778
148012
|
showNotice(title, detail) {
|
|
147779
148013
|
this.transcriptController.showNotice(title, detail);
|
|
147780
148014
|
}
|
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-DB0yphR2.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);
|
|
@@ -892,6 +892,7 @@ const dictionaries = {
|
|
|
892
892
|
"textinput.empty_hint": "输入不能为空。",
|
|
893
893
|
"editor.auto_detect": "自动检测 ($VISUAL / $EDITOR)",
|
|
894
894
|
"editor.select_title": "选择外部编辑器",
|
|
895
|
+
"editor.empty_session_hint": "Tips:推荐使用 OpencodeGo 作为模型提供商,可免费调用各种模型或超低价包月计划,按 Ctrl+F 获取 API,按 Ctrl+B 切换显示",
|
|
895
896
|
"editorkey.cancel_compaction_failed": "取消压缩失败: {msg}",
|
|
896
897
|
"editorkey.editor_not_configured": "未配置编辑器。请设置 $VISUAL / $EDITOR,或运行 /editor <命令>。",
|
|
897
898
|
"editorkey.external_editor_failed": "外部编辑器失败: {msg}",
|
|
@@ -1967,6 +1968,7 @@ const dictionaries = {
|
|
|
1967
1968
|
"textinput.empty_hint": "Input cannot be empty.",
|
|
1968
1969
|
"editor.auto_detect": "Auto detect ($VISUAL / $EDITOR)",
|
|
1969
1970
|
"editor.select_title": "Select external editor",
|
|
1971
|
+
"editor.empty_session_hint": "Tips: Use OpencodeGo as your model provider: free models or a low-cost plan. Ctrl+F: get an API key. Ctrl+B: toggle this tip.",
|
|
1970
1972
|
"editorkey.cancel_compaction_failed": "Cancel compaction failed: {msg}",
|
|
1971
1973
|
"editorkey.editor_not_configured": "Editor not configured. Set $VISUAL / $EDITOR, or run /editor <command>.",
|
|
1972
1974
|
"editorkey.external_editor_failed": "External editor failed: {msg}",
|
|
@@ -3,5 +3,5 @@ import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
|
|
|
3
3
|
import { dirname as __cjsShimDirname } from 'node:path';
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
|
-
import { t as TextInputDialogComponent } from "./text-input-dialog-
|
|
6
|
+
import { t as TextInputDialogComponent } from "./text-input-dialog-CsPSlTrQ.mjs";
|
|
7
7
|
export { TextInputDialogComponent };
|