Fix KB local tool schemas so Netx Ops preset can mount.

DSH defineTool expects a flat parameter map; full JSON Schema objects broke hosts with KB configured (0.1.35).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-16 00:39:40 +08:00
parent da08c38949
commit 62d3f565ce
8 changed files with 272 additions and 425 deletions

View file

@ -672,6 +672,11 @@ function watchKbContext(listener) {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -708,20 +713,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -730,16 +731,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -748,20 +744,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -770,38 +762,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -672,6 +672,11 @@ function watchKbContext(listener) {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -708,20 +713,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -730,16 +731,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -748,20 +744,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -770,38 +762,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -672,6 +672,11 @@ function watchKbContext(listener) {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -708,20 +713,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -730,16 +731,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -748,20 +744,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -770,38 +762,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -672,6 +672,11 @@ function watchKbContext(listener) {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -708,20 +713,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -730,16 +731,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -748,20 +744,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -770,38 +762,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -672,6 +672,11 @@ function watchKbContext(listener) {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -708,20 +713,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -730,16 +731,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -748,20 +744,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -770,38 +762,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -1526,6 +1526,11 @@ function resetKbContext() {
var str = (description) => ({ type: "string", ...description ? { description } : {} }); var str = (description) => ({ type: "string", ...description ? { description } : {} });
var bool = (description) => ({ type: "boolean", ...description ? { description } : {} }); var bool = (description) => ({ type: "boolean", ...description ? { description } : {} });
var num = (description) => ({ type: "number", ...description ? { description } : {} }); var num = (description) => ({ type: "number", ...description ? { description } : {} });
var reqStr = (description) => ({
type: "string",
required: true,
...description ? { description } : {}
});
function renderJson(_args, value) { function renderJson(_args, value) {
return [{ type: "text", text: JSON.stringify(value, null, 0) }]; return [{ type: "text", text: JSON.stringify(value, null, 0) }];
} }
@ -1562,20 +1567,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
return () => {}; return () => {};
const entries = [ const entries = [
tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", { tool("netx__kbWriteMemory", `Create (or overwrite) one diary-style memory markdown under ${localRoot}/memories/. ` + "bucket: note→日常笔记, rca_review→排障复盘, ai_trace→AI思维链. " + "Default create-only; set overwrite=true to replace same filename. " + "Do not use workspace Write — KB is outside the sandbox.", {
type: "object", bucket: {
additionalProperties: false, type: "string",
required: ["bucket", "slug", "body"], required: true,
properties: { enum: ["note", "rca_review", "ai_trace"],
bucket: { description: "Memory subdirectory"
type: "string", },
enum: ["note", "rca_review", "ai_trace"], slug: reqStr("Short filename stem (no path separators)"),
description: "Memory subdirectory" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem (no path separators)"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createMemory(liveSnapshot(), { }, (args) => createMemory(liveSnapshot(), {
bucket: args.bucket, bucket: args.bucket,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -1584,16 +1585,11 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", { tool("netx__kbWriteDraft", `Create (or overwrite) a DRAFT case under ${localRoot}/drafts/. ` + "Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. " + "Set overwrite=true to replace. Not for identity/ or formal RCA.", {
type: "object", slug: reqStr("Case stem (DRAFT- prefix added if missing)"),
additionalProperties: false, body: reqStr("Markdown body (RCA-ish draft)"),
required: ["slug", "body"], domain: str("Optional fault-domain subfolder under drafts/"),
properties: { date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
slug: str("Case stem (DRAFT- prefix added if missing)"), overwrite: bool("Replace if the target file already exists")
body: str("Markdown body (RCA-ish draft)"),
domain: str("Optional fault-domain subfolder under drafts/"),
date: str("Optional event date YYYY-MM-DD or YYYYMMDD"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createDraft(liveSnapshot(), { }, (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
body: String(args.body ?? ""), body: String(args.body ?? ""),
@ -1602,20 +1598,16 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", { tool("netx__kbWriteSuggestion", `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` + "Use for theory corrections or tool/process improvements — never rewrite identity/.", {
type: "object", kind: {
additionalProperties: false, type: "string",
required: ["kind", "slug", "body"], required: true,
properties: { enum: ["theory", "improvement"],
kind: { description: "theory = knowledge fix; improvement = tools/skills/process"
type: "string", },
enum: ["theory", "improvement"], slug: reqStr("Short filename stem"),
description: "theory = knowledge fix; improvement = tools/skills/process" body: reqStr("Full markdown body"),
}, date: str("Optional YYYY-MM-DD (default today)"),
slug: str("Short filename stem"), overwrite: bool("Replace if the target file already exists")
body: str("Full markdown body"),
date: str("Optional YYYY-MM-DD (default today)"),
overwrite: bool("Replace if the target file already exists")
}
}, (args) => createSuggestion(liveSnapshot(), { }, (args) => createSuggestion(liveSnapshot(), {
kind: args.kind, kind: args.kind,
slug: String(args.slug ?? ""), slug: String(args.slug ?? ""),
@ -1624,38 +1616,24 @@ function registerKbLocalTools(ctx, snapshot = getKbContext()) {
overwrite: args.overwrite === true overwrite: args.overwrite === true
})), })),
tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", { tool("netx__kbUpdateLocal", `Replace the body of an existing file under memories|drafts|suggestions ` + `(absolute path or path relative to ${localRoot}). ` + "Cannot touch identity/ or outside paths.local. Drafts keep status: draft.", {
type: "object", path: reqStr("Absolute path or path relative to KB local root"),
additionalProperties: false, body: reqStr("New full markdown body")
required: ["path", "body"],
properties: {
path: str("Absolute path or path relative to KB local root"),
body: str("New full markdown body")
}
}, (args) => updateLocalFile(liveSnapshot(), { }, (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ""), path: String(args.path ?? ""),
body: String(args.body ?? "") body: String(args.body ?? "")
})), })),
tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, { tool("netx__kbDeleteLocal", `Delete one existing markdown under memories|drafts|suggestions ` + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, {
type: "object", path: reqStr("Absolute path or path relative to KB local root")
additionalProperties: false,
required: ["path"],
properties: {
path: str("Absolute path or path relative to KB local root")
}
}, (args) => deleteLocalFile(liveSnapshot(), { }, (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? "") path: String(args.path ?? "")
})), })),
tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", { tool("netx__kbListLocal", `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` + "Does not include identity/. Read contents via absolute paths from the listing.", {
type: "object", root: {
additionalProperties: false, type: "string",
properties: { enum: ["memories", "drafts", "suggestions", "all"],
root: { description: "Subtree to list (default all writable)"
type: "string", },
enum: ["memories", "drafts", "suggestions", "all"], limit: num("Max entries 1–200 (default 50)")
description: "Subtree to list (default all writable)"
},
limit: num("Max entries 1–200 (default 50)")
}
}, (args) => listLocalFiles(liveSnapshot(), { }, (args) => listLocalFiles(liveSnapshot(), {
root: args.root ?? "all", root: args.root ?? "all",
limit: typeof args.limit === "number" ? args.limit : undefined limit: typeof args.limit === "number" ? args.limit : undefined

View file

@ -1,6 +1,6 @@
{ {
"name": "dsh-netxops", "name": "dsh-netxops",
"version": "0.1.34", "version": "0.1.35",
"description": "DeepSeek Harness Netx Ops: ops/topology + IM delivery + session export + knowledge-base MANIFEST/_skills + localSkills", "description": "DeepSeek Harness Netx Ops: ops/topology + IM delivery + session export + knowledge-base MANIFEST/_skills + localSkills",
"license": "MIT", "license": "MIT",
"type": "module", "type": "module",

View file

@ -20,6 +20,11 @@ import { getKbContext } from './kb-runtime.ts'
const str = (description?: string) => ({ type: 'string' as const, ...(description ? { description } : {}) }) const str = (description?: string) => ({ type: 'string' as const, ...(description ? { description } : {}) })
const bool = (description?: string) => ({ type: 'boolean' as const, ...(description ? { description } : {}) }) const bool = (description?: string) => ({ type: 'boolean' as const, ...(description ? { description } : {}) })
const num = (description?: string) => ({ type: 'number' as const, ...(description ? { description } : {}) }) const num = (description?: string) => ({ type: 'number' as const, ...(description ? { description } : {}) })
const reqStr = (description?: string) => ({
type: 'string' as const,
required: true as const,
...(description ? { description } : {}),
})
function renderJson(_args: unknown, value: unknown) { function renderJson(_args: unknown, value: unknown) {
return [{ type: 'text' as const, text: JSON.stringify(value, null, 0) }] return [{ type: 'text' as const, text: JSON.stringify(value, null, 0) }]
@ -37,6 +42,7 @@ function liveSnapshot(): KbSnapshot {
function tool( function tool(
name: string, name: string,
description: string, description: string,
/** DSH `defineTool` expects a flat property map — not a full JSON Schema object. */
parameters: Record<string, unknown>, parameters: Record<string, unknown>,
execute: (args: Record<string, unknown>) => Promise<unknown> | unknown, execute: (args: Record<string, unknown>) => Promise<unknown> | unknown,
) { ) {
@ -79,20 +85,16 @@ export function registerKbLocalTools(
+ 'Default create-only; set overwrite=true to replace same filename. ' + 'Default create-only; set overwrite=true to replace same filename. '
+ 'Do not use workspace Write — KB is outside the sandbox.', + 'Do not use workspace Write — KB is outside the sandbox.',
{ {
type: 'object', bucket: {
additionalProperties: false, type: 'string' as const,
required: ['bucket', 'slug', 'body'], required: true as const,
properties: { enum: ['note', 'rca_review', 'ai_trace'],
bucket: { description: 'Memory subdirectory',
type: 'string',
enum: ['note', 'rca_review', 'ai_trace'],
description: 'Memory subdirectory',
},
slug: str('Short filename stem (no path separators)'),
body: str('Full markdown body'),
date: str('Optional YYYY-MM-DD (default today)'),
overwrite: bool('Replace if the target file already exists'),
}, },
slug: reqStr('Short filename stem (no path separators)'),
body: reqStr('Full markdown body'),
date: str('Optional YYYY-MM-DD (default today)'),
overwrite: bool('Replace if the target file already exists'),
}, },
(args) => createMemory(liveSnapshot(), { (args) => createMemory(liveSnapshot(), {
bucket: args.bucket as 'note' | 'rca_review' | 'ai_trace', bucket: args.bucket as 'note' | 'rca_review' | 'ai_trace',
@ -108,16 +110,11 @@ export function registerKbLocalTools(
+ 'Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. ' + 'Filename is forced to DRAFT-YYYYMMDD-…; body gets status: draft frontmatter if missing. '
+ 'Set overwrite=true to replace. Not for identity/ or formal RCA.', + 'Set overwrite=true to replace. Not for identity/ or formal RCA.',
{ {
type: 'object', slug: reqStr('Case stem (DRAFT- prefix added if missing)'),
additionalProperties: false, body: reqStr('Markdown body (RCA-ish draft)'),
required: ['slug', 'body'], domain: str('Optional fault-domain subfolder under drafts/'),
properties: { date: str('Optional event date YYYY-MM-DD or YYYYMMDD'),
slug: str('Case stem (DRAFT- prefix added if missing)'), overwrite: bool('Replace if the target file already exists'),
body: str('Markdown body (RCA-ish draft)'),
domain: str('Optional fault-domain subfolder under drafts/'),
date: str('Optional event date YYYY-MM-DD or YYYYMMDD'),
overwrite: bool('Replace if the target file already exists'),
},
}, },
(args) => createDraft(liveSnapshot(), { (args) => createDraft(liveSnapshot(), {
slug: String(args.slug ?? ''), slug: String(args.slug ?? ''),
@ -132,20 +129,16 @@ export function registerKbLocalTools(
`Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. ` `Create (or overwrite) a suggestion under ${localRoot}/suggestions/{theory|improvement}/. `
+ 'Use for theory corrections or tool/process improvements — never rewrite identity/.', + 'Use for theory corrections or tool/process improvements — never rewrite identity/.',
{ {
type: 'object', kind: {
additionalProperties: false, type: 'string' as const,
required: ['kind', 'slug', 'body'], required: true as const,
properties: { enum: ['theory', 'improvement'],
kind: { description: 'theory = knowledge fix; improvement = tools/skills/process',
type: 'string',
enum: ['theory', 'improvement'],
description: 'theory = knowledge fix; improvement = tools/skills/process',
},
slug: str('Short filename stem'),
body: str('Full markdown body'),
date: str('Optional YYYY-MM-DD (default today)'),
overwrite: bool('Replace if the target file already exists'),
}, },
slug: reqStr('Short filename stem'),
body: reqStr('Full markdown body'),
date: str('Optional YYYY-MM-DD (default today)'),
overwrite: bool('Replace if the target file already exists'),
}, },
(args) => createSuggestion(liveSnapshot(), { (args) => createSuggestion(liveSnapshot(), {
kind: args.kind as 'theory' | 'improvement', kind: args.kind as 'theory' | 'improvement',
@ -161,13 +154,8 @@ export function registerKbLocalTools(
+ `(absolute path or path relative to ${localRoot}). ` + `(absolute path or path relative to ${localRoot}). `
+ 'Cannot touch identity/ or outside paths.local. Drafts keep status: draft.', + 'Cannot touch identity/ or outside paths.local. Drafts keep status: draft.',
{ {
type: 'object', path: reqStr('Absolute path or path relative to KB local root'),
additionalProperties: false, body: reqStr('New full markdown body'),
required: ['path', 'body'],
properties: {
path: str('Absolute path or path relative to KB local root'),
body: str('New full markdown body'),
},
}, },
(args) => updateLocalFile(liveSnapshot(), { (args) => updateLocalFile(liveSnapshot(), {
path: String(args.path ?? ''), path: String(args.path ?? ''),
@ -179,12 +167,7 @@ export function registerKbLocalTools(
`Delete one existing markdown under memories|drafts|suggestions ` `Delete one existing markdown under memories|drafts|suggestions `
+ `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`, + `(absolute or relative to ${localRoot}). Refuses identity/ and escapes.`,
{ {
type: 'object', path: reqStr('Absolute path or path relative to KB local root'),
additionalProperties: false,
required: ['path'],
properties: {
path: str('Absolute path or path relative to KB local root'),
},
}, },
(args) => deleteLocalFile(liveSnapshot(), { (args) => deleteLocalFile(liveSnapshot(), {
path: String(args.path ?? ''), path: String(args.path ?? ''),
@ -195,16 +178,12 @@ export function registerKbLocalTools(
`List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). ` `List recent .md files under ${localRoot} memories|drafts|suggestions (newest first). `
+ 'Does not include identity/. Read contents via absolute paths from the listing.', + 'Does not include identity/. Read contents via absolute paths from the listing.',
{ {
type: 'object', root: {
additionalProperties: false, type: 'string' as const,
properties: { enum: ['memories', 'drafts', 'suggestions', 'all'],
root: { description: 'Subtree to list (default all writable)',
type: 'string',
enum: ['memories', 'drafts', 'suggestions', 'all'],
description: 'Subtree to list (default all writable)',
},
limit: num('Max entries 1–200 (default 50)'),
}, },
limit: num('Max entries 1–200 (default 50)'),
}, },
(args) => listLocalFiles(liveSnapshot(), { (args) => listLocalFiles(liveSnapshot(), {
root: (args.root as 'memories' | 'drafts' | 'suggestions' | 'all' | undefined) ?? 'all', root: (args.root as 'memories' | 'drafts' | 'suggestions' | 'all' | undefined) ?? 'all',