scream-code 0.14.8 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-CJCkJpo_.mjs → app-CPu6R7nd.mjs} +1301 -61
- package/dist/{dispatch-DVJhr5Rj.mjs → dispatch-BPcn43Vw.mjs} +1 -1
- package/dist/{dispatch-usl5yQnT.mjs → dispatch-D8ovLbmr.mjs} +533 -208
- package/dist/main.mjs +1 -1
- package/dist/public/assets/index-BfVIY6UW.css +1 -0
- package/dist/public/assets/index-CYmwuyd8.js +93 -0
- package/dist/public/assets/index-CYmwuyd8.js.map +1 -0
- package/dist/public/assets/logo-v2-ChmPAIbI.svg +1 -0
- package/dist/public/gateway.html +156 -0
- package/dist/public/index.html +2 -2
- package/dist/{src-DCp4eCi5.mjs → src-h6ZExh4A.mjs} +1 -1
- package/dist/{src-BH9W5k24.mjs → src-tDEINaMV.mjs} +196 -17
- package/dist/{text-input-dialog-B0yHChBj.mjs → text-input-dialog-BUXAKycx.mjs} +40 -14
- package/dist/{text-input-dialog-B9uwX1Uy.mjs → text-input-dialog-Dr42qLMU.mjs} +1 -1
- package/package.json +8 -2
- package/dist/public/assets/index-CopRDlgZ.js +0 -90
- package/dist/public/assets/index-CopRDlgZ.js.map +0 -1
- package/dist/public/assets/index-CyGAkekh.css +0 -1
|
@@ -4,9 +4,9 @@ import { dirname as __cjsShimDirname } from 'node:path';
|
|
|
4
4
|
const __filename = __cjsShimFileURLToPath(import.meta.url);
|
|
5
5
|
const __dirname = __cjsShimDirname(__filename);
|
|
6
6
|
import { i as __require, o as __toESM, r as __exportAll, t as __commonJSMin } from "./chunk-D90kvbyJ.mjs";
|
|
7
|
-
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-
|
|
7
|
+
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-tDEINaMV.mjs";
|
|
8
8
|
import { t as require_base64_js } from "./base64-js-DzVmk6Nb.mjs";
|
|
9
|
-
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-
|
|
9
|
+
import { a as setLocale, i as getLocale, n as assertScreamHostIdentity, o as t, r as createScreamDefaultHeaders, t as TextInputDialogComponent } from "./text-input-dialog-BUXAKycx.mjs";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
|
|
12
12
|
import * as fs$1 from "node:fs/promises";
|
|
@@ -575,7 +575,7 @@ function createToolMessage(toolCallId, output) {
|
|
|
575
575
|
}
|
|
576
576
|
//#endregion
|
|
577
577
|
//#region ../../packages/ltod/src/rate-limit-utils.ts
|
|
578
|
-
const QUOTA_EXHAUSTED_BACKOFF_MS =
|
|
578
|
+
const QUOTA_EXHAUSTED_BACKOFF_MS = 30 * 1e3;
|
|
579
579
|
const RATE_LIMIT_EXCEEDED_BACKOFF_MS = 30 * 1e3;
|
|
580
580
|
const MODEL_CAPACITY_BASE_MS = 45 * 1e3;
|
|
581
581
|
const MODEL_CAPACITY_JITTER_MS = 30 * 1e3;
|
|
@@ -605,9 +605,9 @@ function parseRateLimitReason(errorMessage) {
|
|
|
605
605
|
/**
|
|
606
606
|
* Backoff in ms for a given reason. MODEL_CAPACITY gets jitter to avoid
|
|
607
607
|
* thundering herd. UNKNOWN (a classified 429 with no recognized reason)
|
|
608
|
-
* uses the short 20s SERVER_ERROR backoff — a
|
|
609
|
-
*
|
|
610
|
-
* quota exhaustion.
|
|
608
|
+
* uses the short 20s SERVER_ERROR backoff — a long quota backoff on an
|
|
609
|
+
* unrecoverable error would hang a turn for far longer than the value of
|
|
610
|
+
* retrying, and UNKNOWN is not evidence of quota exhaustion.
|
|
611
611
|
*/
|
|
612
612
|
function calculateRateLimitBackoffMs(reason) {
|
|
613
613
|
switch (reason) {
|
|
@@ -765,14 +765,9 @@ var APIEmptyResponseError = class extends ChatProviderError {
|
|
|
765
765
|
function isRetryableGenerateError(error) {
|
|
766
766
|
if (error instanceof APIConnectionError$3 || error instanceof APITimeoutError) return true;
|
|
767
767
|
if (error instanceof APIEmptyResponseError) return true;
|
|
768
|
-
if (error instanceof APIProviderRateLimitError) return
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
500,
|
|
772
|
-
502,
|
|
773
|
-
503,
|
|
774
|
-
504
|
|
775
|
-
].includes(error.statusCode);
|
|
768
|
+
if (error instanceof APIProviderRateLimitError) return true;
|
|
769
|
+
if (error instanceof APIStatusError) return error.statusCode === 429 || error.statusCode === 408 || error.statusCode >= 500 && error.statusCode < 600;
|
|
770
|
+
return false;
|
|
776
771
|
}
|
|
777
772
|
const CONTEXT_OVERFLOW_MESSAGE_PATTERNS = [
|
|
778
773
|
/context[ _-]?length/,
|
|
@@ -47489,12 +47484,9 @@ async function chatWithRetry(input) {
|
|
|
47489
47484
|
logRequestFailure(input, error, attempt, maxAttempts);
|
|
47490
47485
|
throw error;
|
|
47491
47486
|
}
|
|
47492
|
-
|
|
47493
|
-
|
|
47494
|
-
|
|
47495
|
-
}
|
|
47496
|
-
if (attempt >= maxAttempts || !input.llm.isRetryableError(error)) {
|
|
47497
|
-
logRequestFailure(input, error, attempt, maxAttempts);
|
|
47487
|
+
const attemptLimit = error instanceof APIProviderRateLimitError && error.reason === "QUOTA_EXHAUSTED" ? Math.min(3, maxAttempts) : maxAttempts;
|
|
47488
|
+
if (attempt >= attemptLimit || !input.llm.isRetryableError(error)) {
|
|
47489
|
+
logRequestFailure(input, error, attempt, attemptLimit);
|
|
47498
47490
|
throw error;
|
|
47499
47491
|
}
|
|
47500
47492
|
const delayMs = computeDelayMs(error, delays, attempt);
|
|
@@ -47506,7 +47498,7 @@ async function chatWithRetry(input) {
|
|
|
47506
47498
|
stepUuid: input.stepUuid,
|
|
47507
47499
|
failedAttempt: attempt,
|
|
47508
47500
|
nextAttempt: attempt + 1,
|
|
47509
|
-
maxAttempts,
|
|
47501
|
+
maxAttempts: attemptLimit,
|
|
47510
47502
|
delayMs,
|
|
47511
47503
|
...retryErrorFields(error)
|
|
47512
47504
|
});
|
|
@@ -53192,12 +53184,15 @@ function createFastEmbedEngine(cacheDir) {
|
|
|
53192
53184
|
engineCache.set(key, engine);
|
|
53193
53185
|
return engine;
|
|
53194
53186
|
}
|
|
53187
|
+
/** Stable identity of the model used by every engine created here. */
|
|
53188
|
+
const EMBEDDING_MODEL_NAME = "bge-small-zh-v1.5";
|
|
53195
53189
|
function createFastEmbedEngineImpl(cacheDir) {
|
|
53196
53190
|
let embedder = null;
|
|
53197
53191
|
let initPromise = null;
|
|
53198
53192
|
let loadFailed = false;
|
|
53199
53193
|
let lastError;
|
|
53200
53194
|
return {
|
|
53195
|
+
modelName: EMBEDDING_MODEL_NAME,
|
|
53201
53196
|
get available() {
|
|
53202
53197
|
return embedder !== null && !loadFailed;
|
|
53203
53198
|
},
|
|
@@ -53595,7 +53590,7 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
53595
53590
|
CREATE TABLE IF NOT EXISTS memory_embeddings (
|
|
53596
53591
|
memory_id TEXT PRIMARY KEY REFERENCES memos(id) ON DELETE CASCADE,
|
|
53597
53592
|
embedding_json TEXT NOT NULL,
|
|
53598
|
-
model TEXT NOT NULL DEFAULT '
|
|
53593
|
+
model TEXT NOT NULL DEFAULT '${EMBEDDING_MODEL_NAME}',
|
|
53599
53594
|
created_at INTEGER NOT NULL
|
|
53600
53595
|
);
|
|
53601
53596
|
|
|
@@ -53901,7 +53896,7 @@ var MemoryMemoStore = class MemoryMemoStore {
|
|
|
53901
53896
|
const now = Date.now();
|
|
53902
53897
|
this.db.exec("BEGIN TRANSACTION");
|
|
53903
53898
|
try {
|
|
53904
|
-
for (let i = 0; i < pending.length; i++) insert.run(pending[i].id, JSON.stringify([...vectors[i]]),
|
|
53899
|
+
for (let i = 0; i < pending.length; i++) insert.run(pending[i].id, JSON.stringify([...vectors[i]]), EMBEDDING_MODEL_NAME, now);
|
|
53905
53900
|
this.db.exec("COMMIT");
|
|
53906
53901
|
} catch (error) {
|
|
53907
53902
|
this.db.exec("ROLLBACK");
|
|
@@ -54910,7 +54905,7 @@ var KnowledgeLookupTool = class {
|
|
|
54910
54905
|
const llm = { generate: async (systemPrompt, userPrompt) => {
|
|
54911
54906
|
return this.agent.generateText(systemPrompt, userPrompt);
|
|
54912
54907
|
} };
|
|
54913
|
-
const { multiSearchWithTrace } = await import("./src-
|
|
54908
|
+
const { multiSearchWithTrace } = await import("./src-h6ZExh4A.mjs");
|
|
54914
54909
|
const { results, trace } = await multiSearchWithTrace(store, llm, query, { topK });
|
|
54915
54910
|
if (results.length === 0) return {
|
|
54916
54911
|
isError: false,
|
|
@@ -64668,7 +64663,7 @@ function renderPrompt(template, vars) {
|
|
|
64668
64663
|
}
|
|
64669
64664
|
//#endregion
|
|
64670
64665
|
//#region ../../packages/agent-core/src/tools/builtin/collaboration/skill-tool.md
|
|
64671
|
-
var skill_tool_default = "Invoke a registered skill from the current skill listing. BLOCKING REQUIREMENT: when a skill from the listing matches the user's request, you MUST call this tool (not free-form text). Do NOT call the same skill repeatedly inside one turn — recursive depth is capped at {{ MAX_SKILL_QUERY_DEPTH }}.\n\n## Currently model-invocable skills\n{{ AVAILABLE_SKILLS }}\nThis list is a construction-time snapshot; for the live catalog inspect the skill registry.\n\n## Matching guide\n\n- A skill matches when the user's request involves the scenarios/trigger conditions listed in the skill's \"When to use\" line.\n- Look for: (a) keywords in the user's request that match the skill's trigger phrases, (b) the task type or scenario described in the skill's \"When to use\", (c) similar requests the skill was designed for.\n- If a skill seems relevant, invoke it first — if it doesn't fit, you can always fall back to doing it yourself.";
|
|
64666
|
+
var skill_tool_default = "Invoke a registered skill from the current skill listing. BLOCKING REQUIREMENT: when a skill from the listing matches the user's request, you MUST call this tool (not free-form text). Do NOT call the same skill repeatedly inside one turn — recursive depth is capped at {{ MAX_SKILL_QUERY_DEPTH }}.\n\n## Currently model-invocable skills\n{{ AVAILABLE_SKILLS }}\nThis list is a construction-time snapshot; for the live catalog inspect the skill registry.\n\n**Not for inventory questions**: when the user asks what you have — in any wording (skills / plugins / capabilities / MCP / tools) — answer with **InspectOwnAssets** (the full read-only catalog), not this listing. To find or install new capabilities, use **ManagePlugin**. This listing exists for **invocation**: when one of the skills above matches the task at hand.\n\n## Matching guide\n\n- A skill matches when the user's request involves the scenarios/trigger conditions listed in the skill's \"When to use\" line.\n- Look for: (a) keywords in the user's request that match the skill's trigger phrases, (b) the task type or scenario described in the skill's \"When to use\", (c) similar requests the skill was designed for.\n- If a skill seems relevant, invoke it first — if it doesn't fit, you can always fall back to doing it yourself.";
|
|
64672
64667
|
/** Max skills to list inline in the tool description; rest are summarized. */
|
|
64673
64668
|
const MAX_INLINE_SKILLS = 15;
|
|
64674
64669
|
/** Max description length per skill line. */
|
|
@@ -65280,7 +65275,7 @@ var import_yauzl = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
65280
65275
|
var EventEmitter$3 = __require("events").EventEmitter;
|
|
65281
65276
|
var Transform$1 = __require("stream").Transform;
|
|
65282
65277
|
var PassThrough$2 = __require("stream").PassThrough;
|
|
65283
|
-
var Writable = __require("stream").Writable;
|
|
65278
|
+
var Writable$1 = __require("stream").Writable;
|
|
65284
65279
|
exports.fromBuffer = fromBuffer;
|
|
65285
65280
|
function fromBuffer(buffer, options, callback) {
|
|
65286
65281
|
if (typeof options === "function") {
|
|
@@ -65804,7 +65799,7 @@ var import_yauzl = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
65804
65799
|
start: position,
|
|
65805
65800
|
end: position + length
|
|
65806
65801
|
});
|
|
65807
|
-
var writeStream = new Writable();
|
|
65802
|
+
var writeStream = new Writable$1();
|
|
65808
65803
|
var written = 0;
|
|
65809
65804
|
writeStream._write = function(chunk, encoding, cb) {
|
|
65810
65805
|
chunk.copy(buffer, offset + written, 0, chunk.length);
|
|
@@ -65973,9 +65968,9 @@ async function hasManifest(dir) {
|
|
|
65973
65968
|
const dirManifest = path.join(dir, ".scream-plugin", "plugin.json");
|
|
65974
65969
|
const claudeDirManifest = path.join(dir, ".claude-plugin", "plugin.json");
|
|
65975
65970
|
const skillMd = path.join(dir, "SKILL.md");
|
|
65976
|
-
return await isFile$
|
|
65971
|
+
return await isFile$3(rootManifest) || await isFile$3(dirManifest) || await isFile$3(claudeDirManifest) || await isFile$3(skillMd);
|
|
65977
65972
|
}
|
|
65978
|
-
async function isFile$
|
|
65973
|
+
async function isFile$3(p) {
|
|
65979
65974
|
try {
|
|
65980
65975
|
return (await stat(p)).isFile();
|
|
65981
65976
|
} catch {
|
|
@@ -66438,12 +66433,12 @@ async function parseManifest(pluginRoot) {
|
|
|
66438
66433
|
const rootJsonPath = path.join(pluginRoot, SCREAM_PLUGIN_ROOT_PATH);
|
|
66439
66434
|
const dirJsonPath = path.join(pluginRoot, SCREAM_PLUGIN_DIR_PATH);
|
|
66440
66435
|
const claudeDirJsonPath = path.join(pluginRoot, CLAUDE_PLUGIN_DIR_PATH);
|
|
66441
|
-
const rootJsonExists = await isFile$
|
|
66442
|
-
const dirJsonExists = await isFile$
|
|
66443
|
-
const claudeDirJsonExists = await isFile$
|
|
66436
|
+
const rootJsonExists = await isFile$2(rootJsonPath);
|
|
66437
|
+
const dirJsonExists = await isFile$2(dirJsonPath);
|
|
66438
|
+
const claudeDirJsonExists = await isFile$2(claudeDirJsonPath);
|
|
66444
66439
|
if (!rootJsonExists && !dirJsonExists && !claudeDirJsonExists) {
|
|
66445
66440
|
const skillMdPath = path.join(pluginRoot, BARE_SKILL_PATH);
|
|
66446
|
-
if (await isFile$
|
|
66441
|
+
if (await isFile$2(skillMdPath)) return {
|
|
66447
66442
|
manifest: {
|
|
66448
66443
|
name: path.basename(pluginRoot),
|
|
66449
66444
|
skills: [pluginRoot]
|
|
@@ -66521,7 +66516,7 @@ async function parseManifest(pluginRoot) {
|
|
|
66521
66516
|
}
|
|
66522
66517
|
let skills = await resolveSkillsField(pluginRoot, raw["skills"], diagnostics);
|
|
66523
66518
|
if (raw["skills"] === void 0) {
|
|
66524
|
-
if (await isFile$
|
|
66519
|
+
if (await isFile$2(path.join(pluginRoot, "SKILL.md"))) skills = [pluginRoot];
|
|
66525
66520
|
}
|
|
66526
66521
|
const skillInstructions = typeof raw["skillInstructions"] === "string" ? raw["skillInstructions"] : void 0;
|
|
66527
66522
|
const config = typeof raw["config"] === "object" && raw["config"] !== null && !Array.isArray(raw["config"]) ? raw["config"] : void 0;
|
|
@@ -66853,7 +66848,7 @@ async function discoverSkillDirs(root, maxDepth) {
|
|
|
66853
66848
|
for (const entry of entries) {
|
|
66854
66849
|
if (!entry.isDirectory()) continue;
|
|
66855
66850
|
const child = path.join(dir, entry.name);
|
|
66856
|
-
if (await isFile$
|
|
66851
|
+
if (await isFile$2(path.join(child, BARE_SKILL_PATH))) found.push(child);
|
|
66857
66852
|
await walk(child, depth + 1);
|
|
66858
66853
|
}
|
|
66859
66854
|
}
|
|
@@ -66863,7 +66858,7 @@ async function discoverSkillDirs(root, maxDepth) {
|
|
|
66863
66858
|
for (const dir of sorted) if (!result.some((parent) => dir.startsWith(parent + path.sep))) result.push(dir);
|
|
66864
66859
|
return result;
|
|
66865
66860
|
}
|
|
66866
|
-
async function isFile$
|
|
66861
|
+
async function isFile$2(p) {
|
|
66867
66862
|
try {
|
|
66868
66863
|
return (await stat(p)).isFile();
|
|
66869
66864
|
} catch {
|
|
@@ -67860,7 +67855,10 @@ async function keepNewestBackupOnly(backupsForId) {
|
|
|
67860
67855
|
async function recordFrom(input) {
|
|
67861
67856
|
const { parsed } = input;
|
|
67862
67857
|
const hasError = parsed.diagnostics.some((d) => d.severity === "error");
|
|
67863
|
-
const
|
|
67858
|
+
const discovery = hasError || parsed.manifest === void 0 ? {
|
|
67859
|
+
skills: [],
|
|
67860
|
+
diagnostics: []
|
|
67861
|
+
} : await discoverPluginSkills(input.id, parsed.manifest);
|
|
67864
67862
|
return {
|
|
67865
67863
|
id: input.id,
|
|
67866
67864
|
root: input.root,
|
|
@@ -67872,13 +67870,13 @@ async function recordFrom(input) {
|
|
|
67872
67870
|
originalSource: input.originalSource,
|
|
67873
67871
|
capabilities: input.capabilities,
|
|
67874
67872
|
github: input.github,
|
|
67875
|
-
skills,
|
|
67876
|
-
skillCount: skills.length,
|
|
67873
|
+
skills: discovery.skills,
|
|
67874
|
+
skillCount: discovery.skills.length,
|
|
67877
67875
|
manifest: parsed.manifest,
|
|
67878
67876
|
manifestKind: parsed.manifestKind,
|
|
67879
67877
|
manifestPath: parsed.manifestPath,
|
|
67880
67878
|
shadowedManifestPath: parsed.shadowedManifestPath,
|
|
67881
|
-
diagnostics: parsed.diagnostics,
|
|
67879
|
+
diagnostics: [...parsed.diagnostics, ...discovery.diagnostics],
|
|
67882
67880
|
skillInstructions: parsed.manifest?.skillInstructions
|
|
67883
67881
|
};
|
|
67884
67882
|
}
|
|
@@ -67908,11 +67906,26 @@ async function discoverPluginSkills(pluginId, manifest) {
|
|
|
67908
67906
|
instructions: manifest?.skillInstructions
|
|
67909
67907
|
}
|
|
67910
67908
|
}));
|
|
67911
|
-
if (roots.length === 0) return
|
|
67912
|
-
|
|
67913
|
-
|
|
67914
|
-
|
|
67915
|
-
|
|
67909
|
+
if (roots.length === 0) return {
|
|
67910
|
+
skills: [],
|
|
67911
|
+
diagnostics: []
|
|
67912
|
+
};
|
|
67913
|
+
const diagnostics = [];
|
|
67914
|
+
return {
|
|
67915
|
+
skills: (await discoverSkills({
|
|
67916
|
+
roots,
|
|
67917
|
+
onWarning: (message) => {
|
|
67918
|
+
diagnostics.push({
|
|
67919
|
+
severity: "warn",
|
|
67920
|
+
message
|
|
67921
|
+
});
|
|
67922
|
+
}
|
|
67923
|
+
})).map((skill) => ({
|
|
67924
|
+
name: skill.name,
|
|
67925
|
+
description: skill.description
|
|
67926
|
+
})),
|
|
67927
|
+
diagnostics
|
|
67928
|
+
};
|
|
67916
67929
|
}
|
|
67917
67930
|
function recordToInfo(record) {
|
|
67918
67931
|
return {
|
|
@@ -72402,10 +72415,10 @@ async function findExistingRg(shareDir) {
|
|
|
72402
72415
|
source: "share-bin-cached"
|
|
72403
72416
|
};
|
|
72404
72417
|
}
|
|
72405
|
-
let downloadPromise;
|
|
72418
|
+
let downloadPromise$1;
|
|
72406
72419
|
async function downloadRgWithLock(shareDir) {
|
|
72407
|
-
if (downloadPromise !== void 0) return downloadPromise;
|
|
72408
|
-
downloadPromise = (async () => {
|
|
72420
|
+
if (downloadPromise$1 !== void 0) return downloadPromise$1;
|
|
72421
|
+
downloadPromise$1 = (async () => {
|
|
72409
72422
|
try {
|
|
72410
72423
|
const existing = await findExistingRg(shareDir);
|
|
72411
72424
|
if (existing) return existing;
|
|
@@ -72414,10 +72427,10 @@ async function downloadRgWithLock(shareDir) {
|
|
|
72414
72427
|
source: "share-bin-downloaded"
|
|
72415
72428
|
};
|
|
72416
72429
|
} finally {
|
|
72417
|
-
downloadPromise = void 0;
|
|
72430
|
+
downloadPromise$1 = void 0;
|
|
72418
72431
|
}
|
|
72419
72432
|
})();
|
|
72420
|
-
return downloadPromise;
|
|
72433
|
+
return downloadPromise$1;
|
|
72421
72434
|
}
|
|
72422
72435
|
function rgBinaryName() {
|
|
72423
72436
|
return process.platform === "win32" ? "rg.exe" : "rg";
|
|
@@ -75402,7 +75415,7 @@ function messageOf(error) {
|
|
|
75402
75415
|
}
|
|
75403
75416
|
//#endregion
|
|
75404
75417
|
//#region ../../packages/agent-core/src/tools/builtin/plugin/manage-plugin.md
|
|
75405
|
-
var manage_plugin_default = "Manage this product's own plugins. Plugins are the vehicle for add-on capabilities: each one can contribute skills, MCP servers, shell hooks, and — only after an explicit activate — code that runs in this process.\n\nUse this tool in two situations: (a) a task needs a capability you do not currently have — exhaust your existing tools first and add a plugin only when the gap is real; (b) the user asks you to find, install, configure, inspect, or clean up a capability (e.g. \"install something that can render mermaid\", \"
|
|
75418
|
+
var manage_plugin_default = "Manage this product's own plugins. Plugins are the vehicle for add-on capabilities: each one can contribute skills, MCP servers, shell hooks, and — only after an explicit activate — code that runs in this process.\n\nUse this tool in two situations: (a) a task needs a capability you do not currently have — exhaust your existing tools first and add a plugin only when the gap is real; (b) the user asks you to find, install, configure, inspect, or clean up a capability (e.g. \"install something that can render mermaid\", \"disable the plugin I no longer use\"). In both cases, remove or disable additions again when the work is done unless the capability is broadly reusable.\n\n**Not for inventory questions**: when the user asks what you have — in any wording (skills / plugins / capabilities / MCP / tools / 技能 / 插件 / 能力) — answer with **InspectOwnAssets** (the read-only inventory: every skill package on disk with live `invocable` status, plus MCP declarations). `{\"action\":\"list\"}` here is the **plugin management view**: a pre-change check of the plugin table before installing or modifying, not the answer to \"what can you do\".\n\nChoose the lightest vehicle that solves the problem, in this order: a skill (prompt instructions — safest, no code, no process), an MCP server (an external process), a code plugin (runs in this process — the most powerful, needs its own approved `activate`).\n\n## Workflow\n\n1. `{\"action\":\"list\"}` — see what is already installed before adding anything. To confirm the gap is real, start from an `InspectOwnAssets` inventory (what exists on disk and what is invocable), then check this table for an existing plugin that already covers it. Do not grow a second plugin that duplicates an existing capability; enable, disable, or extend the one you already have.\n2. `{\"action\":\"marketplace\",\"query\":\"pdf\"}` — browse the catalog for a matching capability. Pass `source` to read a different catalog. `tier` is a display label only; judge an entry by its description and source. Entries whose origin previously produced a tripped plugin carry `quarantined: true`.\n3. `{\"action\":\"precheck\",\"source\":\"https://github.com/owner/repo\"}` — free read-only origin check before installing anything external that you have not vetted in this session: it reports past incidents recorded by the plugin center's immune memory (`quarantined:true` + the reason). Surface such a warning to the user BEFORE asking for the install approval.\n4. If nothing in the catalog fits and precheck is clean, you may search the web (`WebSearch` / `FetchURL`) and evaluate a repository yourself: read its README and manifest, prefer a pinned branch/tag/commit URL over a moving default, and check that it does something you cannot already do.\n5. To **make a capability yourself**: if the user invoked `/make-skill`, use the `MakeSkillPlan` → `MakeSkillApply` pair (it produces a valid skill package and registers it into this table automatically). Otherwise build a plugin directory by hand (a `scream.plugin.json` manifest plus your SKILL.md under `skills/`) and register it with `register_generated` — that route lands in this table, hot-applies the same turn, and stays manageable here (disable/remove/circuit). Writing a plain SKILL.md into the user skills directory is also a legitimate skill, but it does NOT appear in this table and cannot be managed with this tool.\n6. `{\"action\":\"install\",\"source\":\"https://github.com/owner/repo/tree/v1.2.0\"}` — install from a local path, a GitHub URL, or a zip URL. **Installing never executes code.** The plugin's files are copied and its record is registered; a manifest `entryPoint` stays dormant until step 8.\n7. `{\"action\":\"install\",\"source\":\"...\",\"upgrade\":true}` — same-name refresh: backs up the current files (single slot under `plugins/backups/<id>/`), swaps in the new ones, and reloads; `from`/`to` versions and any downgrade warning are reported. If activation then fails, `rollback` restores the previous files in one approved step.\n8. `{\"action\":\"enable\",\"id\":\"...\"}` / `{\"action\":\"disable\",\"id\":\"...\"}` / `{\"action\":\"set_mcp_enabled\",\"id\":\"...\",\"server\":\"...\",\"enabled\":false}` — turn contributed capabilities on and off.\n9. `{\"action\":\"activate\",\"id\":\"...\"}` — the only action that runs a plugin's code entry point in this process. Skip it for skill-only and MCP-only plugins; they need no activation.\n10. `{\"action\":\"info\",\"id\":\"...\"}` / `{\"action\":\"check\"}` — inspect one record in full, or the health of the table. Each entry also carries `usage: {calls, okCalls, okRate, lastUsedAt}` from this machine: zero calls since install marks it as a cleanup candidate; high usage with a high okRate is worth keeping when you must choose.\n11. `{\"action\":\"reset\",\"id\":\"...\"}` — recover a circuit-tripped or manually disabled plugin: clears its failure ledger, re-derives the record from disk, enables it, and hot-applies its capabilities. It never runs code — a code plugin still needs its own approved `activate`.\n12. `{\"action\":\"rollback\",\"id\":\"...\"}` — restore the pre-upgrade backup taken by step 7 in one approved step.\n13. `{\"action\":\"reload\"}` — re-read the plugin table from disk and get an added/removed/error summary.\n14. `{\"action\":\"remove\",\"id\":\"...\"}` (or `disable` to keep the record) — retreat once the capability is no longer wanted.\n\n## Circuit breaker (limbs that fail get pulled, the loop never dies)\n\nPlugin-owned capabilities are counted per plugin: every failed tool call (in-process plugin tools and plugin MCP servers) and every faulty event handler. A success clears the streak; three consecutive failures trip the breaker, and the failing result arrives with a `[circuit]` advisory: the plugin was marked with the reason, disabled persistently, its code deactivated, and its tools, MCP servers, skills, and hooks pulled from every live session. Nothing retries automatically — read the state with `check` (each plugin carries `circuit: {failures, tripped}`), then `reset` to give it another chance or `remove` to let it go. User-configured MCP servers and built-in tools are never charged to anyone's breaker.\n\nEvery trip ALSO writes the origin into the plugin center's immune memory (quarantine ledger): future `precheck`/`install`/`marketplace` calls for the same repository will surface that history before anything is executed again, even across sessions — and even if the broken plugin was removed afterwards.\n\nChanges are hot-applied to the running session wherever the host supports it: mutating results carry a `sync` report (`{ok, applied[], failed[]}`) naming exactly what landed — `skills.inject`, `mcp.add`/`mcp.remove`, tool teardown. An `install` deliberately reports no `mcp.add`: installing never starts a plugin's MCP process; that waits for an approved `enable`/`activate`/`reload`. Treat `sync.failed` as advisory (the mutation itself already succeeded) and re-check with `info`/`check` rather than re-running the mutation. Reload before judging whether a change landed.\n\n## Registration of your own output\n\n`{\"action\":\"register_generated\",\"source\":\"/abs/path/to/generated/plugin\"}` registers a plugin directory you produced locally, without a copy step. It refuses any manifest that declares an `entryPoint`: this action is normally auto-approved and must never become a route to running code. For a code plugin, use `install` and then `activate`.\n\n## Approval contract\n\n- Read-only actions (`list`, `info`, `check`, `marketplace`) run without prompting.\n- Every mutating action (`install`, `register_generated`, `enable`, `disable`, `set_mcp_enabled`, `activate`, `deactivate`, `remove`, `reset`, `reload`) requires the user's approval. State what you are about to do and why in one line, then call the tool — never describe a plugin change you have not made.\n- The approval prompt for `install` and `register_generated` shows the full source string, and a session grant applies to that exact source only.\n- `activate` is the step that executes third-party code; treat it as its own decision, made after you have read what the plugin does.\n- A rejected approval is an answer, not a retry cue. Report the refusal and continue without the capability.\n\n## Output\n\nEvery action returns a JSON string. Success is a compact result object; failure is\n`{\"error\":{\"code\",\"message\",\"next\"}}` with `isError: true`. Follow `next` rather than\nguessing at a repair — it names the call that resolves the problem.\n";
|
|
75406
75419
|
//#endregion
|
|
75407
75420
|
//#region ../../packages/agent-core/src/tools/builtin/plugin/manage-plugin.ts
|
|
75408
75421
|
/** Every action this tool understands. */
|
|
@@ -77139,7 +77152,7 @@ function describeError(error) {
|
|
|
77139
77152
|
}
|
|
77140
77153
|
//#endregion
|
|
77141
77154
|
//#region ../../packages/agent-core/src/tools/builtin/state/inspect-own-assets.md
|
|
77142
|
-
var inspect_own_assets_default = "Use this tool to inspect the agent's own persistent assets: skills, MCP server declarations, configuration files, the memory store, and the knowledge base. It reports what exists, where it lives, and whether it looks valid.\n\n**When to use:**\n- The user asks \"what skills do you have?\", \"show me your mcp config\", \"how is your memory set up?\", \"where is your knowledge base?\"\n- Auditing your own configuration and data (e.g. checking whether mcp.json parses,
|
|
77155
|
+
var inspect_own_assets_default = "Use this tool to inspect the agent's own persistent assets: skills, MCP server declarations, configuration files, the memory store, and the knowledge base. It reports what exists, where it lives, and whether it looks valid.\n\n**When to use:**\n- The user asks \"what skills do you have?\", \"show me your mcp config\", \"how is your memory set up?\", \"where is your knowledge base?\", \"检查技能/插件状态\"\n- Auditing your own configuration and data (e.g. checking whether mcp.json parses, why a skill is not usable, which plugin directories are unregistered)\n\n**Inventory questions — this tool is the single answering view.** When the user asks what you have, in ANY wording (skills / plugins / capabilities / MCP / tools / 技能 / 插件 / 能力), treat it as one intent and answer from this inventory: it reports the skill packages in the three standard skill scopes (user, plugin-managed, project) with live `invocable` status from the skill registry, plus MCP declarations. Every \"not invocable\" entry carries its concrete reason (registered but not invocable / broken frontmatter with the parse message / unregistered), and plugin-managed entries show whether their directory is registered in the plugin table, with unregistered directories listed separately — so a status question is answered by this single call, no need to consult ManagePlugin for the same facts. Skills discovered from non-standard locations (nested directories, `.agents/skills`) may not appear here. Pair it with the system-prompt skills section for the \"callable right now\" view. Division of labor: **InspectOwnAssets = inventory (read-only)**, **ManagePlugin = changes (install/enable/remove)**, **Skill tool = invocation**.\n\nYou must NOT modify any of these assets unless the user explicitly asks you to — this tool is strictly read-only.\n\n**When NOT to use:**\n- Reading the user's workspace files — use `read` / `glob` / `grep` instead\n- Writing or editing anything — this tool is strictly read-only\n\n**How to use:**\n- Call with no arguments (or `scope: \"all\"`) to inspect everything\n- Narrow with `scope: \"skills\"` / `\"mcp\"` / `\"config\"` / `\"memory\"` / `\"knowledge\"` to inspect a single category\n\nThis tool never writes, creates, or modifies any file.\n";
|
|
77143
77156
|
const InspectOwnAssetsInputSchema = z.object({ scope: z.enum([
|
|
77144
77157
|
"all",
|
|
77145
77158
|
"skills",
|
|
@@ -77148,8 +77161,14 @@ const InspectOwnAssetsInputSchema = z.object({ scope: z.enum([
|
|
|
77148
77161
|
"memory",
|
|
77149
77162
|
"knowledge"
|
|
77150
77163
|
]).optional().describe("Which self-assets to inspect: 'all' (default) reports everything; narrow to 'skills', 'mcp', 'config', 'memory', or 'knowledge'.") });
|
|
77151
|
-
/**
|
|
77152
|
-
|
|
77164
|
+
/**
|
|
77165
|
+
* Skills larger than this are reported as broken rather than parsed. Real
|
|
77166
|
+
* skill bundles are a few KB; the cap exists only to guard against reading a
|
|
77167
|
+
* pathological file (e.g. a multi-GB binary misnamed SKILL.md) in one shot.
|
|
77168
|
+
*/
|
|
77169
|
+
const SKILL_FILE_READ_LIMIT = 4 * 1024 * 1024;
|
|
77170
|
+
/** Recursion bound for counting nested SKILL.md files inside unregistered dirs. */
|
|
77171
|
+
const ORPHAN_SCAN_DEPTH = 4;
|
|
77153
77172
|
/**
|
|
77154
77173
|
* Common documentation files shipped inside skill/plugin bundles are not
|
|
77155
77174
|
* skills; matched case-insensitively against top-level flat `.md` entries
|
|
@@ -77189,34 +77208,73 @@ function describeFile(info) {
|
|
|
77189
77208
|
if (!info.exists) return "missing";
|
|
77190
77209
|
return `${info.size} bytes`;
|
|
77191
77210
|
}
|
|
77192
|
-
|
|
77193
|
-
async function checkFrontmatter(path) {
|
|
77194
|
-
let handle;
|
|
77211
|
+
async function isFile$1(path) {
|
|
77195
77212
|
try {
|
|
77196
|
-
|
|
77197
|
-
const buffer = Buffer.alloc(FRONTMATTER_READ_LIMIT);
|
|
77198
|
-
const { bytesRead } = await handle.read(buffer, 0, FRONTMATTER_READ_LIMIT, 0);
|
|
77199
|
-
const head = buffer.subarray(0, bytesRead).toString("utf-8").split("\n").slice(0, 25);
|
|
77200
|
-
if (head[0]?.trim() !== "---") return "missing";
|
|
77201
|
-
return head.some((line) => /^name\s*:/.test(line)) ? "ok" : "broken";
|
|
77213
|
+
return (await stat(path)).isFile();
|
|
77202
77214
|
} catch {
|
|
77203
|
-
return
|
|
77204
|
-
} finally {
|
|
77205
|
-
await handle?.close().catch(() => {});
|
|
77215
|
+
return false;
|
|
77206
77216
|
}
|
|
77207
77217
|
}
|
|
77208
|
-
/**
|
|
77209
|
-
|
|
77218
|
+
/**
|
|
77219
|
+
* Parse a skill file the same way the registry does (`skill/parser`), so the
|
|
77220
|
+
* inventory's status and names agree with what is actually invocable: a file
|
|
77221
|
+
* whose frontmatter fails YAML parsing is `broken` with the real message, not
|
|
77222
|
+
* a heuristic "ok" based on the presence of a `name:` line.
|
|
77223
|
+
*/
|
|
77224
|
+
async function parseSkillEntry(path, fallbackName, requireFrontmatter) {
|
|
77225
|
+
let info;
|
|
77210
77226
|
try {
|
|
77211
|
-
|
|
77227
|
+
info = await stat(path);
|
|
77212
77228
|
} catch {
|
|
77213
|
-
return
|
|
77229
|
+
return {
|
|
77230
|
+
name: fallbackName,
|
|
77231
|
+
frontmatter: "missing"
|
|
77232
|
+
};
|
|
77233
|
+
}
|
|
77234
|
+
if (info.size > SKILL_FILE_READ_LIMIT) return {
|
|
77235
|
+
name: fallbackName,
|
|
77236
|
+
frontmatter: "ok"
|
|
77237
|
+
};
|
|
77238
|
+
let text;
|
|
77239
|
+
try {
|
|
77240
|
+
text = await readFile(path, "utf8");
|
|
77241
|
+
} catch {
|
|
77242
|
+
return {
|
|
77243
|
+
name: fallbackName,
|
|
77244
|
+
frontmatter: "missing"
|
|
77245
|
+
};
|
|
77246
|
+
}
|
|
77247
|
+
if (text.split(/\r?\n/, 1)[0]?.trim() !== "---") {
|
|
77248
|
+
if (requireFrontmatter) return {
|
|
77249
|
+
name: fallbackName,
|
|
77250
|
+
frontmatter: "missing"
|
|
77251
|
+
};
|
|
77252
|
+
return {
|
|
77253
|
+
name: fallbackName,
|
|
77254
|
+
frontmatter: "ok"
|
|
77255
|
+
};
|
|
77256
|
+
}
|
|
77257
|
+
try {
|
|
77258
|
+
return {
|
|
77259
|
+
name: (await parseSkillFromFile({
|
|
77260
|
+
skillMdPath: path,
|
|
77261
|
+
skillDirName: fallbackName,
|
|
77262
|
+
source: "user"
|
|
77263
|
+
})).name,
|
|
77264
|
+
frontmatter: "ok"
|
|
77265
|
+
};
|
|
77266
|
+
} catch (error) {
|
|
77267
|
+
return {
|
|
77268
|
+
name: fallbackName,
|
|
77269
|
+
frontmatter: "broken",
|
|
77270
|
+
reason: error instanceof SkillParseError ? error.message : error instanceof Error ? error.message : String(error)
|
|
77271
|
+
};
|
|
77214
77272
|
}
|
|
77215
77273
|
}
|
|
77216
77274
|
/**
|
|
77217
|
-
* List skill entries under a
|
|
77218
|
-
*
|
|
77219
|
-
*
|
|
77275
|
+
* List skill entries under a skills directory, mirroring the loader's rules:
|
|
77276
|
+
* skip dot-entries, node_modules and README.md; directory skills must contain
|
|
77277
|
+
* SKILL.md; flat skills are non-README `.md` files (frontmatter optional).
|
|
77220
77278
|
*/
|
|
77221
77279
|
async function listSkills(dir) {
|
|
77222
77280
|
let entries;
|
|
@@ -77229,29 +77287,141 @@ async function listSkills(dir) {
|
|
|
77229
77287
|
for (const entry of entries) {
|
|
77230
77288
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
77231
77289
|
if (entry.isDirectory()) {
|
|
77232
|
-
if (!await isSkillDir(join$1(dir, entry.name))) continue;
|
|
77233
77290
|
const skillMd = join$1(dir, entry.name, "SKILL.md");
|
|
77234
|
-
|
|
77291
|
+
if (!await isFile$1(skillMd)) continue;
|
|
77292
|
+
const parsed = await parseSkillEntry(skillMd, entry.name, true);
|
|
77235
77293
|
out.push({
|
|
77236
|
-
|
|
77294
|
+
...parsed,
|
|
77237
77295
|
path: skillMd,
|
|
77238
|
-
kind: "dir"
|
|
77239
|
-
frontmatter: fm
|
|
77296
|
+
kind: "dir"
|
|
77240
77297
|
});
|
|
77241
77298
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
77242
77299
|
if (DOCUMENTATION_MARKDOWN_LOWER.has(entry.name.toLowerCase())) continue;
|
|
77300
|
+
const path = join$1(dir, entry.name);
|
|
77301
|
+
const parsed = await parseSkillEntry(path, entry.name.slice(0, -3), false);
|
|
77243
77302
|
out.push({
|
|
77244
|
-
|
|
77245
|
-
path
|
|
77246
|
-
kind: "flat"
|
|
77247
|
-
frontmatter: "ok"
|
|
77303
|
+
...parsed,
|
|
77304
|
+
path,
|
|
77305
|
+
kind: "flat"
|
|
77248
77306
|
});
|
|
77249
77307
|
}
|
|
77250
77308
|
}
|
|
77251
77309
|
return out.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
77252
77310
|
}
|
|
77253
|
-
|
|
77254
|
-
|
|
77311
|
+
/** Count every SKILL.md reachable under `dirPath` (inclusive), depth-limited. */
|
|
77312
|
+
async function countNestedSkillFiles(dirPath, depth) {
|
|
77313
|
+
if (depth > ORPHAN_SCAN_DEPTH) return 0;
|
|
77314
|
+
let entries;
|
|
77315
|
+
try {
|
|
77316
|
+
entries = await readdir(dirPath, { withFileTypes: true });
|
|
77317
|
+
} catch {
|
|
77318
|
+
return 0;
|
|
77319
|
+
}
|
|
77320
|
+
let count = 0;
|
|
77321
|
+
for (const entry of entries) {
|
|
77322
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
77323
|
+
if (entry.isDirectory()) count += await countNestedSkillFiles(join$1(dirPath, entry.name), depth + 1);
|
|
77324
|
+
else if (entry.name === "SKILL.md") count++;
|
|
77325
|
+
}
|
|
77326
|
+
return count;
|
|
77327
|
+
}
|
|
77328
|
+
/**
|
|
77329
|
+
* Describe an unregistered directory under the managed plugins root so the
|
|
77330
|
+
* inventory can surface things the plugin table cannot see: orphan skill
|
|
77331
|
+
* bundles, nested skill trees, and dormant code plugins (entryPoint) that
|
|
77332
|
+
* would run arbitrary code on install+activate.
|
|
77333
|
+
*/
|
|
77334
|
+
async function describeUnregisteredDir(dirPath) {
|
|
77335
|
+
const parts = [];
|
|
77336
|
+
const topLevelSkillMd = await isFile$1(join$1(dirPath, "SKILL.md"));
|
|
77337
|
+
const totalSkills = await countNestedSkillFiles(dirPath, 0);
|
|
77338
|
+
if (topLevelSkillMd) parts.push(totalSkills > 1 ? `skill bundle (${totalSkills} SKILL.md)` : "skill bundle");
|
|
77339
|
+
else if (totalSkills > 0) parts.push(`${totalSkills} nested skills (not registered)`);
|
|
77340
|
+
try {
|
|
77341
|
+
const manifestText = await readFile(join$1(dirPath, "scream.plugin.json"), "utf8");
|
|
77342
|
+
let hasEntryPoint = false;
|
|
77343
|
+
try {
|
|
77344
|
+
const manifest = JSON.parse(manifestText);
|
|
77345
|
+
hasEntryPoint = typeof manifest.entryPoint === "string" && manifest.entryPoint.length > 0;
|
|
77346
|
+
} catch {
|
|
77347
|
+
parts.push("scream.plugin.json (unparseable)");
|
|
77348
|
+
return parts.join("; ");
|
|
77349
|
+
}
|
|
77350
|
+
parts.push(hasEntryPoint ? "code plugin (entryPoint)" : "manifest but not registered");
|
|
77351
|
+
} catch {}
|
|
77352
|
+
if (parts.length === 0) parts.push("no manifest or skill files");
|
|
77353
|
+
return parts.join("; ");
|
|
77354
|
+
}
|
|
77355
|
+
/**
|
|
77356
|
+
* Plugin-managed skills live under `<managedDir>/<plugin>/SKILL.md`. When the
|
|
77357
|
+
* plugin table (via `agent.toolServices.plugins`) is reachable, entries carry
|
|
77358
|
+
* their registration status and unregistered directories are reported in a
|
|
77359
|
+
* dedicated section; without it, the listing falls back to a pure file scan.
|
|
77360
|
+
*/
|
|
77361
|
+
async function listManagedSkills(managedDir, records) {
|
|
77362
|
+
let plugins;
|
|
77363
|
+
try {
|
|
77364
|
+
plugins = await readdir(managedDir, { withFileTypes: true });
|
|
77365
|
+
} catch {
|
|
77366
|
+
return {
|
|
77367
|
+
entries: [],
|
|
77368
|
+
orphanLines: []
|
|
77369
|
+
};
|
|
77370
|
+
}
|
|
77371
|
+
const entries = [];
|
|
77372
|
+
const orphanLines = [];
|
|
77373
|
+
for (const plugin of plugins) {
|
|
77374
|
+
if (!plugin.isDirectory() || plugin.name.startsWith(".")) continue;
|
|
77375
|
+
const dirPath = join$1(managedDir, plugin.name);
|
|
77376
|
+
const skillMd = join$1(dirPath, "SKILL.md");
|
|
77377
|
+
const record = records?.get(plugin.name);
|
|
77378
|
+
if (records === void 0 || record === void 0) {
|
|
77379
|
+
if (records === void 0) {
|
|
77380
|
+
if (!await isFile$1(skillMd)) continue;
|
|
77381
|
+
const parsed = await parseSkillEntry(skillMd, plugin.name, true);
|
|
77382
|
+
entries.push({
|
|
77383
|
+
...parsed,
|
|
77384
|
+
path: skillMd,
|
|
77385
|
+
kind: "dir",
|
|
77386
|
+
plugin: plugin.name
|
|
77387
|
+
});
|
|
77388
|
+
} else if (await isFile$1(skillMd)) orphanLines.push(`- ${plugin.name} — ${await describeUnregisteredDir(dirPath)} — \`${dirPath}\``);
|
|
77389
|
+
else if (await isFile$1(join$1(dirPath, "scream.plugin.json"))) orphanLines.push(`- ${plugin.name} — ${await describeUnregisteredDir(dirPath)} — \`${dirPath}\``);
|
|
77390
|
+
else if (await countNestedSkillFiles(dirPath, 0) > 0) orphanLines.push(`- ${plugin.name} — ${await describeUnregisteredDir(dirPath)} — \`${dirPath}\``);
|
|
77391
|
+
continue;
|
|
77392
|
+
}
|
|
77393
|
+
if (await isFile$1(skillMd)) {
|
|
77394
|
+
const parsed = await parseSkillEntry(skillMd, plugin.name, true);
|
|
77395
|
+
entries.push({
|
|
77396
|
+
...parsed,
|
|
77397
|
+
path: skillMd,
|
|
77398
|
+
kind: "dir",
|
|
77399
|
+
plugin: plugin.name,
|
|
77400
|
+
registered: true
|
|
77401
|
+
});
|
|
77402
|
+
}
|
|
77403
|
+
}
|
|
77404
|
+
return {
|
|
77405
|
+
entries,
|
|
77406
|
+
orphanLines
|
|
77407
|
+
};
|
|
77408
|
+
}
|
|
77409
|
+
/**
|
|
77410
|
+
* Traffic-light status for a skill entry. The registry is the single source of
|
|
77411
|
+
* truth for what is callable; everything else is a disk-side explanation.
|
|
77412
|
+
*/
|
|
77413
|
+
function classify(entry, invocableNames, registeredNames) {
|
|
77414
|
+
const variants = [entry.name];
|
|
77415
|
+
if (entry.plugin !== void 0) variants.push(`${entry.plugin}:${entry.name}`);
|
|
77416
|
+
if (variants.some((v) => invocableNames.has(v))) return "invocable";
|
|
77417
|
+
if (variants.some((v) => registeredNames.has(v))) return "not invocable — registered but not invocable";
|
|
77418
|
+
if (entry.frontmatter === "broken") return `not invocable — broken: ${entry.reason ?? "frontmatter failed to parse"}`;
|
|
77419
|
+
return "not invocable — unregistered";
|
|
77420
|
+
}
|
|
77421
|
+
function formatSkillEntry(entry, status) {
|
|
77422
|
+
const origin = entry.plugin === void 0 ? "" : ` — plugin: ${entry.plugin}`;
|
|
77423
|
+
const registration = entry.registered === true ? " — registered" : "";
|
|
77424
|
+
return `- ${entry.name} — ${entry.kind} — ${entry.frontmatter} — ${status}${origin}${registration} — \`${entry.path}\``;
|
|
77255
77425
|
}
|
|
77256
77426
|
async function inspectConfig(home, userHome) {
|
|
77257
77427
|
const items = [
|
|
@@ -77267,52 +77437,50 @@ async function inspectConfig(home, userHome) {
|
|
|
77267
77437
|
}
|
|
77268
77438
|
return lines.join("\n");
|
|
77269
77439
|
}
|
|
77270
|
-
async function inspectSkills(home, userHome, cwd) {
|
|
77440
|
+
async function inspectSkills(home, userHome, cwd, agent) {
|
|
77271
77441
|
const { userDir, projectDir } = await resolveSkillInstallPaths({
|
|
77272
77442
|
userHomeDir: userHome,
|
|
77273
77443
|
workDir: cwd
|
|
77274
77444
|
});
|
|
77445
|
+
const registry = agent.skills?.registry;
|
|
77446
|
+
const invocableNames = new Set((registry?.listInvocableSkills() ?? []).map((s) => s.name));
|
|
77447
|
+
const registeredNames = new Set((registry?.listSkills?.() ?? []).map((s) => s.name));
|
|
77448
|
+
const manager = agent.toolServices?.plugins;
|
|
77449
|
+
const pluginRecords = manager === void 0 ? void 0 : new Map(manager.list().map((r) => [r.id, r]));
|
|
77275
77450
|
const sections = ["## Skills", ""];
|
|
77451
|
+
let invocableCount = 0;
|
|
77452
|
+
let totalCount = 0;
|
|
77453
|
+
const track = (entry) => {
|
|
77454
|
+
totalCount++;
|
|
77455
|
+
if (classify(entry, invocableNames, registeredNames) === "invocable") invocableCount++;
|
|
77456
|
+
return formatSkillEntry(entry, classify(entry, invocableNames, registeredNames));
|
|
77457
|
+
};
|
|
77276
77458
|
const userEntries = await listSkills(userDir);
|
|
77277
77459
|
sections.push(`User skills (${userDir}): ${userEntries.length === 0 ? "none" : ""}`);
|
|
77278
|
-
sections.push(...userEntries.
|
|
77460
|
+
sections.push(...userEntries.map(track));
|
|
77279
77461
|
const extraDir = join$1(home, "plugins", "managed");
|
|
77280
|
-
const
|
|
77462
|
+
const managed = await listManagedSkills(extraDir, pluginRecords);
|
|
77281
77463
|
sections.push("");
|
|
77282
|
-
sections.push(`Plugin-managed skills (${extraDir}): ${
|
|
77283
|
-
sections.push(...
|
|
77464
|
+
sections.push(`Plugin-managed skills (${extraDir}): ${managed.entries.length === 0 ? "none" : ""}`);
|
|
77465
|
+
sections.push(...managed.entries.map(track));
|
|
77466
|
+
if (pluginRecords !== void 0) for (const record of pluginRecords.values()) {
|
|
77467
|
+
const warns = record.diagnostics.filter((d) => d.severity === "warn");
|
|
77468
|
+
if (warns.length === 0) continue;
|
|
77469
|
+
sections.push(`plugin ${record.id}: warnings: [${warns.map((d) => d.message).join(" | ")}]`);
|
|
77470
|
+
}
|
|
77471
|
+
if (managed.orphanLines.length > 0) {
|
|
77472
|
+
sections.push("");
|
|
77473
|
+
sections.push(`Unregistered plugin dirs (${extraDir}):`);
|
|
77474
|
+
sections.push(...managed.orphanLines);
|
|
77475
|
+
}
|
|
77284
77476
|
const projectEntries = await listSkills(projectDir);
|
|
77285
77477
|
sections.push("");
|
|
77286
77478
|
sections.push(`Project skills (${projectDir}): ${projectEntries.length === 0 ? "none" : ""}`);
|
|
77287
|
-
sections.push(...projectEntries.
|
|
77479
|
+
sections.push(...projectEntries.map((e) => track(e)));
|
|
77480
|
+
sections.push("");
|
|
77481
|
+
sections.push(`Invocable now: ${invocableCount}/${totalCount} (against the live skill registry; entries marked "not invocable" carry their reason — registered-but-not, broken frontmatter, or unregistered)`);
|
|
77288
77482
|
return sections.join("\n");
|
|
77289
77483
|
}
|
|
77290
|
-
/**
|
|
77291
|
-
* List plugin-managed skills: each `<dir>/SKILL.md` under a managed plugin
|
|
77292
|
-
* directory is a skill entry (Extra source, mirroring plugin/manager.ts).
|
|
77293
|
-
*/
|
|
77294
|
-
async function listManagedSkills(managedDir) {
|
|
77295
|
-
let plugins;
|
|
77296
|
-
try {
|
|
77297
|
-
plugins = await readdir(managedDir, { withFileTypes: true });
|
|
77298
|
-
} catch {
|
|
77299
|
-
return [];
|
|
77300
|
-
}
|
|
77301
|
-
const out = [];
|
|
77302
|
-
for (const plugin of plugins) {
|
|
77303
|
-
if (!plugin.isDirectory() || plugin.name.startsWith(".")) continue;
|
|
77304
|
-
const skillMd = join$1(managedDir, plugin.name, "SKILL.md");
|
|
77305
|
-
if (!await isSkillDir(join$1(managedDir, plugin.name))) continue;
|
|
77306
|
-
const fm = await checkFrontmatter(skillMd);
|
|
77307
|
-
out.push({
|
|
77308
|
-
name: plugin.name,
|
|
77309
|
-
path: skillMd,
|
|
77310
|
-
kind: "dir",
|
|
77311
|
-
frontmatter: fm
|
|
77312
|
-
});
|
|
77313
|
-
}
|
|
77314
|
-
return out.toSorted((a, b) => a.name.localeCompare(b.name));
|
|
77315
|
-
}
|
|
77316
77484
|
/** mcp.json files larger than this are reported as oversize and not parsed. */
|
|
77317
77485
|
const MCP_CONFIG_SIZE_LIMIT = 1024 * 1024;
|
|
77318
77486
|
async function inspectMcp(home, cwd) {
|
|
@@ -77404,7 +77572,7 @@ var InspectOwnAssetsTool = class {
|
|
|
77404
77572
|
const scope = args.scope ?? "all";
|
|
77405
77573
|
const sections = [];
|
|
77406
77574
|
if (scope === "all" || scope === "config") sections.push(await inspectConfig(home, userHome));
|
|
77407
|
-
if (scope === "all" || scope === "skills") sections.push(await inspectSkills(home, userHome, cwd));
|
|
77575
|
+
if (scope === "all" || scope === "skills") sections.push(await inspectSkills(home, userHome, cwd, this.agent));
|
|
77408
77576
|
if (scope === "all" || scope === "mcp") sections.push(await inspectMcp(home, cwd));
|
|
77409
77577
|
if (scope === "all" || scope === "memory") sections.push(await inspectMemory(home));
|
|
77410
77578
|
if (scope === "all" || scope === "knowledge") sections.push(await inspectKnowledge(home));
|
|
@@ -125389,6 +125557,92 @@ const PULSE_WAVE_FRAMES = [
|
|
|
125389
125557
|
}
|
|
125390
125558
|
];
|
|
125391
125559
|
//#endregion
|
|
125560
|
+
//#region src/tui/commands/knowledge-store.ts
|
|
125561
|
+
let knowledgeStoreInstance;
|
|
125562
|
+
let embeddingEngineInstance;
|
|
125563
|
+
let embeddingStatus = "idle";
|
|
125564
|
+
function getEmbeddingCacheDir() {
|
|
125565
|
+
const dir = join(getDataDir(), "cache", "fastembed");
|
|
125566
|
+
mkdirSync(dir, { recursive: true });
|
|
125567
|
+
return dir;
|
|
125568
|
+
}
|
|
125569
|
+
/** Subdirectory fastembed uses for the BGESmallZH model inside the cache dir. */
|
|
125570
|
+
const EMBEDDING_MODEL_DIR = "fast-bge-small-zh-v1.5";
|
|
125571
|
+
/**
|
|
125572
|
+
* Whether the embedding model has been downloaded to the local cache.
|
|
125573
|
+
* Checks the ONNX weights plus the config and tokenizer sidecars — all
|
|
125574
|
+
* required for FlagEmbedding.init to load without network access (a missing
|
|
125575
|
+
* tokenizer would trigger a network fetch and violate the warm-up's
|
|
125576
|
+
* offline-load guarantee).
|
|
125577
|
+
*/
|
|
125578
|
+
function isEmbeddingModelCached() {
|
|
125579
|
+
const modelDir = join(getEmbeddingCacheDir(), EMBEDDING_MODEL_DIR);
|
|
125580
|
+
return existsSync(join(modelDir, "model_optimized.onnx")) && existsSync(join(modelDir, "config.json")) && existsSync(join(modelDir, "tokenizer.json"));
|
|
125581
|
+
}
|
|
125582
|
+
async function getKnowledgeStore() {
|
|
125583
|
+
if (knowledgeStoreInstance === void 0) {
|
|
125584
|
+
knowledgeStoreInstance = new KnowledgeStore(getDataDir());
|
|
125585
|
+
await knowledgeStoreInstance.init();
|
|
125586
|
+
embeddingEngineInstance = createFastEmbedEngine(getEmbeddingCacheDir());
|
|
125587
|
+
knowledgeStoreInstance.setEmbeddingEngine(embeddingEngineInstance);
|
|
125588
|
+
}
|
|
125589
|
+
return knowledgeStoreInstance;
|
|
125590
|
+
}
|
|
125591
|
+
function getEmbeddingStatus() {
|
|
125592
|
+
return embeddingStatus;
|
|
125593
|
+
}
|
|
125594
|
+
/**
|
|
125595
|
+
* Manually trigger the embedding model download/load.
|
|
125596
|
+
* Only mutates embeddingStatus; the actual download is delegated to
|
|
125597
|
+
* EmbeddingEngine.ensureReady() and saves the model to the shared cache dir.
|
|
125598
|
+
* Returns { ok: true } on success, or { ok: false, error } on failure.
|
|
125599
|
+
* Concurrent calls join the in-flight download and share its result instead
|
|
125600
|
+
* of failing — the startup warm-up and a user-initiated download must never
|
|
125601
|
+
* race into a spurious "download already in progress" error.
|
|
125602
|
+
*/
|
|
125603
|
+
async function startManualEmbeddingDownload() {
|
|
125604
|
+
if (embeddingEngineInstance === void 0) return {
|
|
125605
|
+
ok: false,
|
|
125606
|
+
error: "embedding engine not initialized"
|
|
125607
|
+
};
|
|
125608
|
+
if (embeddingEngineInstance.available) {
|
|
125609
|
+
embeddingStatus = "ready";
|
|
125610
|
+
return {
|
|
125611
|
+
ok: true,
|
|
125612
|
+
alreadyReady: true
|
|
125613
|
+
};
|
|
125614
|
+
}
|
|
125615
|
+
if (downloadPromise !== void 0) return downloadPromise;
|
|
125616
|
+
embeddingStatus = "downloading";
|
|
125617
|
+
downloadPromise = performDownload().finally(() => {
|
|
125618
|
+
downloadPromise = void 0;
|
|
125619
|
+
});
|
|
125620
|
+
return downloadPromise;
|
|
125621
|
+
}
|
|
125622
|
+
let downloadPromise;
|
|
125623
|
+
async function performDownload() {
|
|
125624
|
+
try {
|
|
125625
|
+
let ok = await embeddingEngineInstance.ensureReady();
|
|
125626
|
+
let error = ok ? void 0 : embeddingEngineInstance.lastError;
|
|
125627
|
+
if (!ok) {
|
|
125628
|
+
clearEmbeddingModelCache(getEmbeddingCacheDir());
|
|
125629
|
+
ok = await embeddingEngineInstance.ensureReady();
|
|
125630
|
+
error = ok ? void 0 : embeddingEngineInstance.lastError;
|
|
125631
|
+
}
|
|
125632
|
+
embeddingStatus = ok ? "ready" : "failed";
|
|
125633
|
+
return {
|
|
125634
|
+
ok,
|
|
125635
|
+
error
|
|
125636
|
+
};
|
|
125637
|
+
} catch (error) {
|
|
125638
|
+
embeddingStatus = "failed";
|
|
125639
|
+
return {
|
|
125640
|
+
ok: false,
|
|
125641
|
+
error: error instanceof Error ? error.message : String(error)
|
|
125642
|
+
};
|
|
125643
|
+
}
|
|
125644
|
+
}
|
|
125645
|
+
//#endregion
|
|
125392
125646
|
//#region src/tui/commands/experimental-flags.ts
|
|
125393
125647
|
let snapshot = {};
|
|
125394
125648
|
/** Replace the cached flag snapshot. Call once after fetching via `harness.getExperimentalFlags()`. */
|
|
@@ -126698,31 +126952,16 @@ function getWireTypeOptions() {
|
|
|
126698
126952
|
}
|
|
126699
126953
|
function getThinkingOptions() {
|
|
126700
126954
|
return [
|
|
126701
|
-
|
|
126702
|
-
|
|
126703
|
-
|
|
126704
|
-
|
|
126705
|
-
|
|
126706
|
-
|
|
126707
|
-
|
|
126708
|
-
|
|
126709
|
-
|
|
126710
|
-
|
|
126711
|
-
label: t("prompts.thinking_medium")
|
|
126712
|
-
},
|
|
126713
|
-
{
|
|
126714
|
-
value: "high",
|
|
126715
|
-
label: t("prompts.thinking_high")
|
|
126716
|
-
},
|
|
126717
|
-
{
|
|
126718
|
-
value: "xhigh",
|
|
126719
|
-
label: t("prompts.thinking_xhigh")
|
|
126720
|
-
},
|
|
126721
|
-
{
|
|
126722
|
-
value: "max",
|
|
126723
|
-
label: t("prompts.thinking_max")
|
|
126724
|
-
}
|
|
126725
|
-
];
|
|
126955
|
+
"off",
|
|
126956
|
+
"low",
|
|
126957
|
+
"medium",
|
|
126958
|
+
"high",
|
|
126959
|
+
"xhigh",
|
|
126960
|
+
"max"
|
|
126961
|
+
].map((level) => ({
|
|
126962
|
+
value: level,
|
|
126963
|
+
label: level
|
|
126964
|
+
}));
|
|
126726
126965
|
}
|
|
126727
126966
|
function getImageOptions() {
|
|
126728
126967
|
return [{
|
|
@@ -131469,7 +131708,7 @@ async function guidedGoalSetup(host) {
|
|
|
131469
131708
|
host.showNotice(t("goal.storm_breaker"), t("goal.conflict_loop"));
|
|
131470
131709
|
return;
|
|
131471
131710
|
}
|
|
131472
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
131711
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-Dr42qLMU.mjs");
|
|
131473
131712
|
const initialDesc = await promptText(host, TextInputDialogComponent, {
|
|
131474
131713
|
title: t("goal.setup_title_initial"),
|
|
131475
131714
|
subtitle: t("goal.setup_desc_hint"),
|
|
@@ -131490,7 +131729,7 @@ async function guidedGoalSetup(host) {
|
|
|
131490
131729
|
await showGoalConfigWizard(host, session, confirmed.trim() || objective, false);
|
|
131491
131730
|
}
|
|
131492
131731
|
async function showGoalConfigWizard(host, session, objective, replace) {
|
|
131493
|
-
const { TextInputDialogComponent } = await import("./text-input-dialog-
|
|
131732
|
+
const { TextInputDialogComponent } = await import("./text-input-dialog-Dr42qLMU.mjs");
|
|
131494
131733
|
const turnInput = await promptNumber(host, TextInputDialogComponent, {
|
|
131495
131734
|
title: t("goal.wizard_title", { objective }),
|
|
131496
131735
|
subtitle: t("goal.budget_turns_hint"),
|
|
@@ -138296,7 +138535,7 @@ function buildRoleAdditionalText(prefs) {
|
|
|
138296
138535
|
if (items.length === 0 && (doNot === void 0 || doNot.length === 0) && !hasToolPriority) return "";
|
|
138297
138536
|
lines.push("", ...items);
|
|
138298
138537
|
if (doNot !== void 0 && doNot.length > 0) lines.push("", "## Do NOT (explicit prohibitions — NEVER do these)", doNot);
|
|
138299
|
-
if (hasToolPriority) lines.push("", "## Tool priority (set via /like — HIGHEST PRIORITY)", prefs.toolPriority === "skill" ? "For every user request: (1) first analyze the intent, (2) then identify whether any installed skill matches THIS request, (3) if one matches, invoke the Skill tool with it as your FIRST action — before MCP tools or doing it yourself, (4) if none matches, proceed with MCP tools or solve it yourself.
|
|
138538
|
+
if (hasToolPriority) lines.push("", "## Tool priority (set via /like — HIGHEST PRIORITY)", prefs.toolPriority === "skill" ? "For every user request: (1) first analyze the intent, (2) then identify whether any installed skill matches THIS request, (3) if one matches, invoke the Skill tool with it as your FIRST action — before MCP tools or doing it yourself, (4) if none matches, proceed with MCP tools or solve it yourself. When the user asks what you have (skills / plugins / capabilities, any wording), answer from an InspectOwnAssets inventory; the skills section of the system prompt is the authoritative view of what is invocable right now." : "For every user request: (1) first analyze the intent, (2) then identify whether any available MCP tool matches THIS request, (3) if one matches, use it as your FIRST action — before the Skill tool or doing it yourself, (4) if none matches, proceed with the Skill tool or solve it yourself.");
|
|
138300
138539
|
lines.push("", t("like.priority"));
|
|
138301
138540
|
return lines.join("\n");
|
|
138302
138541
|
}
|
|
@@ -138426,65 +138665,6 @@ function openUrl(url) {
|
|
|
138426
138665
|
execFile(command[0], command[1], () => {});
|
|
138427
138666
|
}
|
|
138428
138667
|
//#endregion
|
|
138429
|
-
//#region src/tui/commands/knowledge-store.ts
|
|
138430
|
-
let knowledgeStoreInstance;
|
|
138431
|
-
let embeddingEngineInstance;
|
|
138432
|
-
let embeddingStatus = "idle";
|
|
138433
|
-
function getEmbeddingCacheDir() {
|
|
138434
|
-
const dir = join(getDataDir(), "cache", "fastembed");
|
|
138435
|
-
mkdirSync(dir, { recursive: true });
|
|
138436
|
-
return dir;
|
|
138437
|
-
}
|
|
138438
|
-
async function getKnowledgeStore() {
|
|
138439
|
-
if (knowledgeStoreInstance === void 0) {
|
|
138440
|
-
knowledgeStoreInstance = new KnowledgeStore(getDataDir());
|
|
138441
|
-
await knowledgeStoreInstance.init();
|
|
138442
|
-
embeddingEngineInstance = createFastEmbedEngine(getEmbeddingCacheDir());
|
|
138443
|
-
knowledgeStoreInstance.setEmbeddingEngine(embeddingEngineInstance);
|
|
138444
|
-
}
|
|
138445
|
-
return knowledgeStoreInstance;
|
|
138446
|
-
}
|
|
138447
|
-
function getEmbeddingStatus() {
|
|
138448
|
-
return embeddingStatus;
|
|
138449
|
-
}
|
|
138450
|
-
/**
|
|
138451
|
-
* Manually trigger the embedding model download/load.
|
|
138452
|
-
* Only mutates embeddingStatus; the actual download is delegated to
|
|
138453
|
-
* EmbeddingEngine.ensureReady() and saves the model to the shared cache dir.
|
|
138454
|
-
* Returns { ok: true } on success, or { ok: false, error } on failure.
|
|
138455
|
-
* Concurrent calls while a download is already in flight are ignored.
|
|
138456
|
-
*/
|
|
138457
|
-
async function startManualEmbeddingDownload() {
|
|
138458
|
-
if (embeddingEngineInstance === void 0) return {
|
|
138459
|
-
ok: false,
|
|
138460
|
-
error: "embedding engine not initialized"
|
|
138461
|
-
};
|
|
138462
|
-
if (embeddingStatus === "downloading") return {
|
|
138463
|
-
ok: false,
|
|
138464
|
-
error: "download already in progress"
|
|
138465
|
-
};
|
|
138466
|
-
if (embeddingEngineInstance.available) {
|
|
138467
|
-
embeddingStatus = "ready";
|
|
138468
|
-
return {
|
|
138469
|
-
ok: true,
|
|
138470
|
-
alreadyReady: true
|
|
138471
|
-
};
|
|
138472
|
-
}
|
|
138473
|
-
embeddingStatus = "downloading";
|
|
138474
|
-
let ok = await embeddingEngineInstance.ensureReady();
|
|
138475
|
-
let error = ok ? void 0 : embeddingEngineInstance.lastError;
|
|
138476
|
-
if (!ok) {
|
|
138477
|
-
clearEmbeddingModelCache(getEmbeddingCacheDir());
|
|
138478
|
-
ok = await embeddingEngineInstance.ensureReady();
|
|
138479
|
-
error = ok ? void 0 : embeddingEngineInstance.lastError;
|
|
138480
|
-
}
|
|
138481
|
-
embeddingStatus = ok ? "ready" : "failed";
|
|
138482
|
-
return {
|
|
138483
|
-
ok,
|
|
138484
|
-
error
|
|
138485
|
-
};
|
|
138486
|
-
}
|
|
138487
|
-
//#endregion
|
|
138488
138668
|
//#region src/tui/commands/knowledge-web.ts
|
|
138489
138669
|
/**
|
|
138490
138670
|
* /knowledge web — 知识图谱可视化
|
|
@@ -140174,6 +140354,7 @@ async function handleIngest(host) {
|
|
|
140174
140354
|
}
|
|
140175
140355
|
const store = await getKnowledgeStore();
|
|
140176
140356
|
const llm = makeLlmCaller(host);
|
|
140357
|
+
if (!await ensureEmbeddingReadyInteractive(host)) return;
|
|
140177
140358
|
const spinner = host.showProgressSpinner(t("knowledge.ingesting"));
|
|
140178
140359
|
try {
|
|
140179
140360
|
if (stats.isDirectory()) {
|
|
@@ -140184,21 +140365,35 @@ async function handleIngest(host) {
|
|
|
140184
140365
|
ok: result.failed === 0,
|
|
140185
140366
|
label: t("knowledge.batch_done")
|
|
140186
140367
|
});
|
|
140368
|
+
const coverage = await store.embeddingCoverage();
|
|
140369
|
+
const syncLine = t("knowledge.sync_counts", {
|
|
140370
|
+
created: String(result.created),
|
|
140371
|
+
updated: String(result.updated),
|
|
140372
|
+
unchanged: String(result.unchanged)
|
|
140373
|
+
});
|
|
140187
140374
|
if (result.failed > 0) {
|
|
140188
140375
|
const summary = [
|
|
140189
140376
|
t("knowledge.succeeded", { count: String(result.succeeded) }),
|
|
140377
|
+
syncLine,
|
|
140190
140378
|
t("knowledge.failed", { count: String(result.failed) }),
|
|
140191
140379
|
t("knowledge.ingest_summary", {
|
|
140192
140380
|
chunks: String(result.totalChunks),
|
|
140193
140381
|
events: String(result.totalEvents),
|
|
140194
140382
|
entities: String(result.totalEntities)
|
|
140195
140383
|
}),
|
|
140384
|
+
t("knowledge.vector_coverage", {
|
|
140385
|
+
embedded: String(coverage.embedded),
|
|
140386
|
+
total: String(coverage.total)
|
|
140387
|
+
}),
|
|
140196
140388
|
"",
|
|
140197
140389
|
t("knowledge.failed_files"),
|
|
140198
140390
|
...result.errors.map((e) => ` • ${basename(e.filePath)}: ${e.message}`)
|
|
140199
140391
|
].join("\n");
|
|
140200
140392
|
host.showNotice(t("knowledge.batch_partial"), summary);
|
|
140201
|
-
} else host.showNotice(t("knowledge.batch_done"), `${
|
|
140393
|
+
} else host.showNotice(t("knowledge.batch_done"), `${syncLine}\n${result.totalChunks} chunks, ${result.totalEvents} events, ${result.totalEntities} entities\n${t("knowledge.vector_coverage", {
|
|
140394
|
+
embedded: String(coverage.embedded),
|
|
140395
|
+
total: String(coverage.total)
|
|
140396
|
+
})}`);
|
|
140202
140397
|
} else {
|
|
140203
140398
|
if (!isSupportedFile(filePath)) {
|
|
140204
140399
|
spinner.stop({
|
|
@@ -140211,11 +140406,19 @@ async function handleIngest(host) {
|
|
|
140211
140406
|
const result = await ingestFile(store, llm, filePath, (progress) => {
|
|
140212
140407
|
spinner.setLabel(formatProgress(progress));
|
|
140213
140408
|
});
|
|
140409
|
+
const outcomeLabel = result.outcome === "unchanged" ? t("knowledge.ingest_unchanged") : result.outcome === "updated" ? t("knowledge.ingest_updated") : t("knowledge.ingest_done");
|
|
140214
140410
|
spinner.stop({
|
|
140215
140411
|
ok: true,
|
|
140216
|
-
label:
|
|
140412
|
+
label: outcomeLabel
|
|
140217
140413
|
});
|
|
140218
|
-
host.showNotice(
|
|
140414
|
+
if (result.outcome === "unchanged") host.showNotice(outcomeLabel, `${t("knowledge.file_label")}: ${basename(filePath)}`);
|
|
140415
|
+
else {
|
|
140416
|
+
const coverage = await store.embeddingCoverage();
|
|
140417
|
+
host.showNotice(outcomeLabel, `${t("knowledge.file_label")}: ${basename(filePath)}\nchunks: ${result.chunkCount}\nevents: ${result.eventCount}\nentities: ${result.entityCount}\n${t("knowledge.vector_coverage", {
|
|
140418
|
+
embedded: String(coverage.embedded),
|
|
140419
|
+
total: String(coverage.total)
|
|
140420
|
+
})}`);
|
|
140421
|
+
}
|
|
140219
140422
|
}
|
|
140220
140423
|
} catch (error) {
|
|
140221
140424
|
spinner.stop({
|
|
@@ -140225,6 +140428,31 @@ async function handleIngest(host) {
|
|
|
140225
140428
|
throw error;
|
|
140226
140429
|
}
|
|
140227
140430
|
}
|
|
140431
|
+
/**
|
|
140432
|
+
* Ensure the shared embedding engine is ready before a vector-dependent
|
|
140433
|
+
* operation. Returns true when ready; otherwise shows an error (missing
|
|
140434
|
+
* download) or attempts a cached-model load and reports the outcome.
|
|
140435
|
+
*/
|
|
140436
|
+
async function ensureEmbeddingReadyInteractive(host) {
|
|
140437
|
+
const engine = (await getKnowledgeStore()).getEmbeddingEngine();
|
|
140438
|
+
if (engine !== void 0 && engine.available) return true;
|
|
140439
|
+
if (!isEmbeddingModelCached()) {
|
|
140440
|
+
host.showError(t("knowledge.model_missing"));
|
|
140441
|
+
return false;
|
|
140442
|
+
}
|
|
140443
|
+
const spinner = host.showProgressSpinner(t("knowledge.loading_model"));
|
|
140444
|
+
const { ok, error } = await startManualEmbeddingDownload();
|
|
140445
|
+
spinner.stop({
|
|
140446
|
+
ok,
|
|
140447
|
+
label: ok ? t("kw.embedding_ready") : t("kw.embedding_failed")
|
|
140448
|
+
});
|
|
140449
|
+
if (!ok) {
|
|
140450
|
+
const detail = [t("knowledge.download_model_retry_hint"), error].filter(Boolean).join("\n");
|
|
140451
|
+
host.showNotice(t("knowledge.model_load_failed"), detail);
|
|
140452
|
+
return false;
|
|
140453
|
+
}
|
|
140454
|
+
return true;
|
|
140455
|
+
}
|
|
140228
140456
|
async function handleList(host) {
|
|
140229
140457
|
const store = await getKnowledgeStore();
|
|
140230
140458
|
const docs = await store.listDocuments();
|
|
@@ -140374,6 +140602,97 @@ async function handleDelete(host) {
|
|
|
140374
140602
|
if (await store.deleteSource(sourceId)) host.showNotice(t("knowledge.deleted"), t("knowledge.doc_removed"));
|
|
140375
140603
|
else host.showError(t("knowledge.delete_fail_not_found"));
|
|
140376
140604
|
}
|
|
140605
|
+
function pickSourceToReembed(host, sources) {
|
|
140606
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
140607
|
+
const options = sources.map((s) => ({
|
|
140608
|
+
value: s.id,
|
|
140609
|
+
label: s.name,
|
|
140610
|
+
description: s.filePath ?? void 0
|
|
140611
|
+
}));
|
|
140612
|
+
const picker = new ChoicePickerComponent({
|
|
140613
|
+
title: t("knowledge.reembed_pick"),
|
|
140614
|
+
options,
|
|
140615
|
+
colors: host.state.theme.colors,
|
|
140616
|
+
onSelect: (value) => {
|
|
140617
|
+
host.restoreEditor();
|
|
140618
|
+
resolve(value);
|
|
140619
|
+
},
|
|
140620
|
+
onCancel: () => {
|
|
140621
|
+
host.restoreEditor();
|
|
140622
|
+
resolve(void 0);
|
|
140623
|
+
}
|
|
140624
|
+
});
|
|
140625
|
+
host.mountEditorReplacement(picker);
|
|
140626
|
+
return promise;
|
|
140627
|
+
}
|
|
140628
|
+
function confirmReembedSource(host, source) {
|
|
140629
|
+
const { promise, resolve } = Promise.withResolvers();
|
|
140630
|
+
const options = [{
|
|
140631
|
+
value: "cancel",
|
|
140632
|
+
label: t("common.cancel")
|
|
140633
|
+
}, {
|
|
140634
|
+
value: "confirm",
|
|
140635
|
+
label: t("knowledge.reembed"),
|
|
140636
|
+
description: t("knowledge.reembed_confirm_desc")
|
|
140637
|
+
}];
|
|
140638
|
+
const picker = new ChoicePickerComponent({
|
|
140639
|
+
title: t("knowledge.reembed_confirm_name", { name: source.name }),
|
|
140640
|
+
options,
|
|
140641
|
+
colors: host.state.theme.colors,
|
|
140642
|
+
onSelect: (value) => {
|
|
140643
|
+
host.restoreEditor();
|
|
140644
|
+
resolve(value === "confirm");
|
|
140645
|
+
},
|
|
140646
|
+
onCancel: () => {
|
|
140647
|
+
host.restoreEditor();
|
|
140648
|
+
resolve(false);
|
|
140649
|
+
}
|
|
140650
|
+
});
|
|
140651
|
+
host.mountEditorReplacement(picker);
|
|
140652
|
+
return promise;
|
|
140653
|
+
}
|
|
140654
|
+
async function handleReembed(host) {
|
|
140655
|
+
const store = await getKnowledgeStore();
|
|
140656
|
+
const sources = await store.listSources();
|
|
140657
|
+
if (sources.length === 0) {
|
|
140658
|
+
host.showNotice(t("knowledge.empty"), t("knowledge.reembed_none"));
|
|
140659
|
+
return;
|
|
140660
|
+
}
|
|
140661
|
+
const sourceId = await pickSourceToReembed(host, sources);
|
|
140662
|
+
if (sourceId === void 0) return;
|
|
140663
|
+
const source = sources.find((s) => s.id === sourceId);
|
|
140664
|
+
if (source === void 0) return;
|
|
140665
|
+
if (!await confirmReembedSource(host, source)) {
|
|
140666
|
+
host.showNotice(t("knowledge.cancelled"));
|
|
140667
|
+
return;
|
|
140668
|
+
}
|
|
140669
|
+
if (!await ensureEmbeddingReadyInteractive(host)) return;
|
|
140670
|
+
const engine = store.getEmbeddingEngine();
|
|
140671
|
+
if (engine === void 0) {
|
|
140672
|
+
host.showError(t("knowledge.model_missing"));
|
|
140673
|
+
return;
|
|
140674
|
+
}
|
|
140675
|
+
const spinner = host.showProgressSpinner(t("knowledge.reembed"));
|
|
140676
|
+
try {
|
|
140677
|
+
const counts = await store.reembedSource(sourceId, engine);
|
|
140678
|
+
spinner.stop({
|
|
140679
|
+
ok: true,
|
|
140680
|
+
label: t("knowledge.reembed_done")
|
|
140681
|
+
});
|
|
140682
|
+
host.showNotice(t("knowledge.reembed_done"), t("knowledge.reembed_summary", {
|
|
140683
|
+
chunks: String(counts.chunks),
|
|
140684
|
+
events: String(counts.events),
|
|
140685
|
+
entities: String(counts.entities),
|
|
140686
|
+
relations: String(counts.relations)
|
|
140687
|
+
}));
|
|
140688
|
+
} catch (error) {
|
|
140689
|
+
spinner.stop({
|
|
140690
|
+
ok: false,
|
|
140691
|
+
label: t("knowledge.reembed")
|
|
140692
|
+
});
|
|
140693
|
+
throw error;
|
|
140694
|
+
}
|
|
140695
|
+
}
|
|
140377
140696
|
async function handleDownloadModel(host) {
|
|
140378
140697
|
await getKnowledgeStore();
|
|
140379
140698
|
const spinner = host.showProgressSpinner(t("kw.embedding_downloading"));
|
|
@@ -140455,6 +140774,11 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
140455
140774
|
description: t("knowledge.delete_desc"),
|
|
140456
140775
|
tone: "danger"
|
|
140457
140776
|
},
|
|
140777
|
+
{
|
|
140778
|
+
value: "reembed",
|
|
140779
|
+
label: "🔁 " + t("knowledge.reembed"),
|
|
140780
|
+
description: t("knowledge.reembed_desc")
|
|
140781
|
+
},
|
|
140458
140782
|
{
|
|
140459
140783
|
value: "stats",
|
|
140460
140784
|
label: "📊 " + t("knowledge.stats"),
|
|
@@ -140480,6 +140804,7 @@ async function handleKnowledgeCommand(host, _args) {
|
|
|
140480
140804
|
else if (value === "list") await handleList(host);
|
|
140481
140805
|
else if (value === "search") await handleSearch(host);
|
|
140482
140806
|
else if (value === "delete") await handleDelete(host);
|
|
140807
|
+
else if (value === "reembed") await handleReembed(host);
|
|
140483
140808
|
else if (value === "stats") await handleStats(host);
|
|
140484
140809
|
else if (value === "web") await handleWeb(host);
|
|
140485
140810
|
} catch (error) {
|
|
@@ -141127,4 +141452,4 @@ async function handleBuiltInSlashCommand(host, name, args) {
|
|
|
141127
141452
|
}
|
|
141128
141453
|
}
|
|
141129
141454
|
//#endregion
|
|
141130
|
-
export { handleTitleCommand as $, isTodoItemShape as $t, renderDiffLinesClustered as A,
|
|
141455
|
+
export { handleTitleCommand as $, isTodoItemShape as $t, renderDiffLinesClustered as A, getInputHistoryFile as An, ENABLE_TERMINAL_FOCUS_REPORTING as At, BackgroundAgentStatusComponent as B, ScreamHarness as Bn, TERMINAL_THEME_LIGHT as Bt, handleRevokeCommand as C, PULSE_WAVE_FRAMES as Cn, createThemeStyles as Ct, toggleEmptySessionHint as D, saveTuiConfig as Dn, parseOsc11BackgroundTheme as Dt, isTurnElapsedEnabled as E, loadTuiConfig as En, detectTerminalTheme as Et, estimateTokens as F, CLI_USER_AGENT_PRODUCT as Fn, OSC11_RESPONSE_PREFIX_NO_ESC as Ft, getBreathingFrame as G, log as Gn, handleSearchCommand as Gt, AgentGroupComponent as H, resolveScreamHome as Hn, isStreaming as Ht, getSharedSpeedTracker as I, PRODUCT_NAME as In, QUERY_TERMINAL_THEME as It, refineGoal as J, isOrphanedToolCallError as Jn, printableChar as Jt, resetBreathingClock as K, resolveGlobalLogPath as Kn, handleConnectCommand as Kt, SkillActivationComponent as L, DEFAULT_CATALOG_URL as Ln, TERMINAL_FOCUS_IN as Lt, langFromPath as M, detectInstallSource as Mn, OSC11_QUERY as Mt, CachedContainer as N, CLI_COMMAND_NAME as Nn, OSC11_RESPONSE as Nt, ToolCallComponent as O, detectShellEnvironment as On, DISABLE_TERMINAL_FOCUS_REPORTING as Ot, ThinkingComponent as P, CLI_UI_MODE as Pn, OSC11_RESPONSE_PREFIX as Pt, handleInitCommand as Q, formatErrorMessage as Qt, ReadGroupComponent as R, fetchCatalog as Rn, TERMINAL_FOCUS_OUT as Rt, getDaemonInstructions as S, PIXEL_PULSE_FRAMES as Sn, createMarkdownTheme as St, isEmptySessionHintDismissed as T, TuiLikePreferencesSchema as Tn, getColorPalette as Tt, WelcomeComponent as U, MemoryMemoStore as Un, FooterComponent as Ut, AssistantMessageComponent as V, MCP_OAUTH_AUTHORIZATION_URL_TOOL_UPDATE as Vn, isBusy as Vt, BREATHE_CYCLE_MS as W, flushDiagnosticLogs as Wn, handleTraceCommand as Wt, handleExportMdCommand as X, SCREAM_ERROR_INFO as Xn, appendStreamingArgsPreview as Xt, handleExportDebugZipCommand as Y, ErrorCodes as Yn, STATUS_BULLET as Yt, handleForkCommand as Z, argsRecord as Zt, refreshUpdateCache as _, isExperimentalFlagEnabled as _n, clearInfoPanelState as _t, handleExtensionCommand as a, EMPTY_SESSION_HINT_URL as an, handleEditorCommand as at, readJsonlFile as b, isEmbeddingModelCached as bn, resolveThemeSync as bt, hasDispose as c, SESSION_TIPS as cn, handlePlanCommand as ct, formatMemoryMemoForInjection as d, getCtrlDHint as dn, handleYoloCommand as dt, parseStreamingArgs as en, toTerminalHyperlink as et, handleMemoryCommand as f, getLlmNotSetMessage as fn, showModelPicker as ft, selectUpdateTarget as g, sortSlashCommands as gn, supportsBalance as gt, handleUpdateCommand as h, BUILTIN_SLASH_COMMANDS as hn, refreshProviderBalance as ht, buildRoleAdditionalText as i, CHARS_PER_TOKEN as in, handleCompactCommand as it, highlightLines as j, getLogDir as jn, ENABLE_TERMINAL_THEME_REPORTING as jt, renderDiffLines as k, getDataDir as kn, DISABLE_TERMINAL_THEME_REPORTING as kt, isPlanExpandable as l, TIP_ROTATION_INTERVAL_MS as ln, handleThemeCommand as lt, handleMcpCommand as m, buildSkillSlashCommands as mn, showSettingsSelector as mt, clearEvalPanelState as n, stringValue as nn, getModelCycleLevel as nt, handleSkillCommand as o, EXIT_CONFIRM_WINDOW_MS as on, handleFusionPlanCommand as ot, handleChannelCommand as p, getNoActiveSessionMessage as pn, showPermissionPicker as pt, clearGoalState as q, isScreamError as qn, handleLogoutCommand as qt, openUrl as r, truncateErrorMessage as rn, handleAutoCommand as rt, disposeChildren as s, MAIN_AGENT_ID$1 as sn, handleModelCommand as st, dispatchInput as t, serializeToolResultOutput as tn, changeThinkingLevel as tt, MoonLoader as u, getCtrlCHint as un, handleWolfpackCommand as ut, readUpdateCache as v, setExperimentalFlags as vn, showStatusReport as vt, UserMessageComponent as w, TuiConfigParseError as wn, contrastTextHex as wt, handleCcCommand as x, startManualEmbeddingDownload as xn, createEditorTheme as xt, appendJsonlLine as y, getKnowledgeStore as yn, showUsage as yt, parseReadGroupOutput as z, saveCatalogCache as zn, TERMINAL_THEME_DARK as zt };
|