stratagate-dsh 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +590 -583
- package/LICENSE +21 -21
- package/dist/client.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/plugin.js +17 -8
- package/dist/plugin.js.map +1 -1
- package/docs/DSH.md +256 -256
- package/docs/DSH.zh-CN.md +233 -233
- package/docs/validation/issue119-after.json +30 -30
- package/docs/validation/issue119-before.json +94 -94
- package/docs/validation/issue119.md +41 -41
- package/package.json +1 -1
- package/screenshots.json +8 -8
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dsh-host-resolution.ts","../src/bootstrap.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\r\nimport { createRequire, registerHooks } from 'node:module'\r\nimport { dirname, isAbsolute, join, relative } from 'node:path'\r\nimport { fileURLToPath, pathToFileURL } from 'node:url'\r\n\r\nconst HOST_PACKAGES = [\r\n '@deepseek-ai/cordis',\r\n '@deepseek-ai/dsh-agent-default-model',\r\n '@deepseek-ai/dsh-client-ui-conversation',\r\n '@deepseek-ai/dsh-llm',\r\n '@deepseek-ai/dsh-native-command',\r\n '@deepseek-ai/dsh-session',\r\n '@deepseek-ai/dsh-session-format',\r\n '@deepseek-ai/dsh-session-format-catalog',\r\n '@deepseek-ai/dsh-session-format-v0-to-v1',\r\n '@deepseek-ai/dsh-settings',\r\n '@deepseek-ai/dsh-system-prompt',\r\n '@deepseek-ai/dsh-tools',\r\n '@deepseek-ai/schemastery',\r\n] as const\r\n\r\nconst installedHooks = Symbol.for('stratagate.dsh-host-resolution.v1')\r\n\r\nfunction isWithin(path: string, root: string): boolean {\r\n const child = relative(root, path)\r\n return child === '' || (!child.startsWith('..') && !isAbsolute(child))\r\n}\r\n\r\nfunction profileDirectory(start: string): string | undefined {\r\n let directory = start\r\n for (;;) {\r\n const manifestPath = join(directory, 'package.json')\r\n if (existsSync(manifestPath)) {\r\n try {\r\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { dsh?: { profile?: unknown } }\r\n if (manifest.dsh?.profile && typeof manifest.dsh.profile === 'object') return directory\r\n } catch {}\r\n }\r\n const parent = dirname(directory)\r\n if (parent === directory) return undefined\r\n directory = parent\r\n }\r\n}\r\n\r\nfunction hostPackage(specifier: string): boolean {\r\n return HOST_PACKAGES.some((name) => specifier === name || specifier.startsWith(`${name}/`))\r\n}\r\n\r\n/**\r\n * Force only StrataGate's DSH imports through the installation-owned fallback\r\n * created by DSH. The hook neither deletes nor rewrites the profile's package\r\n * tree, and imports belonging to the host or other plugins are untouched.\r\n */\r\nexport function installDshHostResolution(moduleUrl: string): boolean {\r\n const packageDirectory = dirname(dirname(fileURLToPath(moduleUrl)))\r\n const profile = profileDirectory(packageDirectory)\r\n if (!profile) return false\r\n\r\n const globalState = globalThis as typeof globalThis & { [installedHooks]?: Set<string> }\r\n const installed = globalState[installedHooks] ??= new Set<string>()\r\n const key = `${profile}\\u0000${packageDirectory}`\r\n if (installed.has(key)) return true\r\n\r\n const fallbackModules = join(dirname(profile), 'node_modules')\r\n const hostRequire = createRequire(join(fallbackModules, '.stratagate-host-resolution.cjs'))\r\n registerHooks({\r\n resolve(specifier, context, nextResolve) {\r\n const parent = context.parentURL?.startsWith('file:') ? fileURLToPath(context.parentURL) : undefined\r\n if (!hostPackage(specifier) || !parent || !isWithin(parent, packageDirectory)) {\r\n return nextResolve(specifier, context)\r\n }\r\n try {\r\n return { url: pathToFileURL(hostRequire.resolve(specifier)).href, shortCircuit: true }\r\n } catch (cause) {\r\n if (specifier === '@deepseek-ai/dsh-session-format-v0-to-v1'\r\n || specifier === '@deepseek-ai/dsh-session-format-catalog') {\r\n return nextResolve(specifier, context)\r\n }\r\n throw new Error(\r\n `StrataGate cannot resolve host-provided DSH package ${JSON.stringify(specifier)} from ${fallbackModules}. `\r\n + 'Start StrataGate through the DSH profile launcher so the host can prepare its module fallback.',\r\n { cause },\r\n )\r\n }\r\n },\r\n })\r\n installed.add(key)\r\n return true\r\n}\r\n","import { installDshHostResolution } from './dsh-host-resolution.js'\
|
|
1
|
+
{"version":3,"sources":["../src/dsh-host-resolution.ts","../src/bootstrap.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs'\r\nimport { createRequire, registerHooks } from 'node:module'\r\nimport { dirname, isAbsolute, join, relative } from 'node:path'\r\nimport { fileURLToPath, pathToFileURL } from 'node:url'\r\n\r\nconst HOST_PACKAGES = [\r\n '@deepseek-ai/cordis',\r\n '@deepseek-ai/dsh-agent-default-model',\r\n '@deepseek-ai/dsh-client-ui-conversation',\r\n '@deepseek-ai/dsh-llm',\r\n '@deepseek-ai/dsh-native-command',\r\n '@deepseek-ai/dsh-session',\r\n '@deepseek-ai/dsh-session-format',\r\n '@deepseek-ai/dsh-session-format-catalog',\r\n '@deepseek-ai/dsh-session-format-v0-to-v1',\r\n '@deepseek-ai/dsh-settings',\r\n '@deepseek-ai/dsh-system-prompt',\r\n '@deepseek-ai/dsh-tools',\r\n '@deepseek-ai/schemastery',\r\n] as const\r\n\r\nconst installedHooks = Symbol.for('stratagate.dsh-host-resolution.v1')\r\n\r\nfunction isWithin(path: string, root: string): boolean {\r\n const child = relative(root, path)\r\n return child === '' || (!child.startsWith('..') && !isAbsolute(child))\r\n}\r\n\r\nfunction profileDirectory(start: string): string | undefined {\r\n let directory = start\r\n for (;;) {\r\n const manifestPath = join(directory, 'package.json')\r\n if (existsSync(manifestPath)) {\r\n try {\r\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { dsh?: { profile?: unknown } }\r\n if (manifest.dsh?.profile && typeof manifest.dsh.profile === 'object') return directory\r\n } catch {}\r\n }\r\n const parent = dirname(directory)\r\n if (parent === directory) return undefined\r\n directory = parent\r\n }\r\n}\r\n\r\nfunction hostPackage(specifier: string): boolean {\r\n return HOST_PACKAGES.some((name) => specifier === name || specifier.startsWith(`${name}/`))\r\n}\r\n\r\n/**\r\n * Force only StrataGate's DSH imports through the installation-owned fallback\r\n * created by DSH. The hook neither deletes nor rewrites the profile's package\r\n * tree, and imports belonging to the host or other plugins are untouched.\r\n */\r\nexport function installDshHostResolution(moduleUrl: string): boolean {\r\n const packageDirectory = dirname(dirname(fileURLToPath(moduleUrl)))\r\n const profile = profileDirectory(packageDirectory)\r\n if (!profile) return false\r\n\r\n const globalState = globalThis as typeof globalThis & { [installedHooks]?: Set<string> }\r\n const installed = globalState[installedHooks] ??= new Set<string>()\r\n const key = `${profile}\\u0000${packageDirectory}`\r\n if (installed.has(key)) return true\r\n\r\n const fallbackModules = join(dirname(profile), 'node_modules')\r\n const hostRequire = createRequire(join(fallbackModules, '.stratagate-host-resolution.cjs'))\r\n registerHooks({\r\n resolve(specifier, context, nextResolve) {\r\n const parent = context.parentURL?.startsWith('file:') ? fileURLToPath(context.parentURL) : undefined\r\n if (!hostPackage(specifier) || !parent || !isWithin(parent, packageDirectory)) {\r\n return nextResolve(specifier, context)\r\n }\r\n try {\r\n return { url: pathToFileURL(hostRequire.resolve(specifier)).href, shortCircuit: true }\r\n } catch (cause) {\r\n if (specifier === '@deepseek-ai/dsh-session-format-v0-to-v1'\r\n || specifier === '@deepseek-ai/dsh-session-format-catalog') {\r\n return nextResolve(specifier, context)\r\n }\r\n throw new Error(\r\n `StrataGate cannot resolve host-provided DSH package ${JSON.stringify(specifier)} from ${fallbackModules}. `\r\n + 'Start StrataGate through the DSH profile launcher so the host can prepare its module fallback.',\r\n { cause },\r\n )\r\n }\r\n },\r\n })\r\n installed.add(key)\r\n return true\r\n}\r\n","import { installDshHostResolution } from './dsh-host-resolution.js'\n\n// DSH prepares its installation-owned module fallback before it imports a\n// profile bundle. Install the narrowly-scoped resolver first, then load the\n// implementation so stale profile-local peers cannot win ESM resolution.\ninstallDshHostResolution(import.meta.url)\n\n// This file and src/index.ts are separate tsup entries. The runtime path is\n// intentionally external so the implementation is instantiated only after\n// the resolver above is active.\n// @ts-expect-error dist/plugin.js is emitted from src/index.ts during build.\nconst plugin = await import('./plugin.js') as typeof import('./index.js')\n\nexport const name = plugin.name\nexport const inject = plugin.inject\nexport const STRATAGATE_SETTINGS_NAMESPACE = plugin.STRATAGATE_SETTINGS_NAMESPACE\nexport const Config = plugin.Config\nexport const apply = plugin.apply\nexport type PluginConfig = import('./index.js').PluginConfig\n"],"mappings":";AAAA,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe,qBAAqB;AAC7C,SAAS,SAAS,YAAY,MAAM,gBAAgB;AACpD,SAAS,eAAe,qBAAqB;AAE7C,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,iBAAiB,uBAAO,IAAI,mCAAmC;AAErE,SAAS,SAAS,MAAc,MAAuB;AACrD,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,SAAO,UAAU,MAAO,CAAC,MAAM,WAAW,IAAI,KAAK,CAAC,WAAW,KAAK;AACtE;AAEA,SAAS,iBAAiB,OAAmC;AAC3D,MAAI,YAAY;AAChB,aAAS;AACP,UAAM,eAAe,KAAK,WAAW,cAAc;AACnD,QAAI,WAAW,YAAY,GAAG;AAC5B,UAAI;AACF,cAAM,WAAW,KAAK,MAAM,aAAa,cAAc,MAAM,CAAC;AAC9D,YAAI,SAAS,KAAK,WAAW,OAAO,SAAS,IAAI,YAAY,SAAU,QAAO;AAAA,MAChF,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,UAAM,SAAS,QAAQ,SAAS;AAChC,QAAI,WAAW,UAAW,QAAO;AACjC,gBAAY;AAAA,EACd;AACF;AAEA,SAAS,YAAY,WAA4B;AAC/C,SAAO,cAAc,KAAK,CAACA,UAAS,cAAcA,SAAQ,UAAU,WAAW,GAAGA,KAAI,GAAG,CAAC;AAC5F;AAOO,SAAS,yBAAyB,WAA4B;AACnE,QAAM,mBAAmB,QAAQ,QAAQ,cAAc,SAAS,CAAC,CAAC;AAClE,QAAM,UAAU,iBAAiB,gBAAgB;AACjD,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,cAAc;AACpB,QAAM,YAAY,YAAY,cAAc,MAAM,oBAAI,IAAY;AAClE,QAAM,MAAM,GAAG,OAAO,KAAS,gBAAgB;AAC/C,MAAI,UAAU,IAAI,GAAG,EAAG,QAAO;AAE/B,QAAM,kBAAkB,KAAK,QAAQ,OAAO,GAAG,cAAc;AAC7D,QAAM,cAAc,cAAc,KAAK,iBAAiB,iCAAiC,CAAC;AAC1F,gBAAc;AAAA,IACZ,QAAQ,WAAW,SAAS,aAAa;AACvC,YAAM,SAAS,QAAQ,WAAW,WAAW,OAAO,IAAI,cAAc,QAAQ,SAAS,IAAI;AAC3F,UAAI,CAAC,YAAY,SAAS,KAAK,CAAC,UAAU,CAAC,SAAS,QAAQ,gBAAgB,GAAG;AAC7E,eAAO,YAAY,WAAW,OAAO;AAAA,MACvC;AACA,UAAI;AACF,eAAO,EAAE,KAAK,cAAc,YAAY,QAAQ,SAAS,CAAC,EAAE,MAAM,cAAc,KAAK;AAAA,MACvF,SAAS,OAAO;AACd,YAAI,cAAc,8CACb,cAAc,2CAA2C;AAC5D,iBAAO,YAAY,WAAW,OAAO;AAAA,QACvC;AACA,cAAM,IAAI;AAAA,UACR,uDAAuD,KAAK,UAAU,SAAS,CAAC,SAAS,eAAe;AAAA,UAExG,EAAE,MAAM;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACD,YAAU,IAAI,GAAG;AACjB,SAAO;AACT;;;ACnFA,yBAAyB,YAAY,GAAG;AAMxC,IAAM,SAAS,MAAM,OAAO,aAAa;AAElC,IAAM,OAAO,OAAO;AACpB,IAAM,SAAS,OAAO;AACtB,IAAM,gCAAgC,OAAO;AAC7C,IAAM,SAAS,OAAO;AACtB,IAAM,QAAQ,OAAO;","names":["name"]}
|
package/dist/plugin.js
CHANGED
|
@@ -9467,6 +9467,11 @@ var DshMetadataStore = class {
|
|
|
9467
9467
|
|
|
9468
9468
|
// src/runtime.ts
|
|
9469
9469
|
import { createHash as createHash3 } from "node:crypto";
|
|
9470
|
+
|
|
9471
|
+
// src/feedback-policy.ts
|
|
9472
|
+
var AGENT_FEEDBACK_ENABLED = false;
|
|
9473
|
+
|
|
9474
|
+
// src/runtime.ts
|
|
9470
9475
|
import { existsSync } from "node:fs";
|
|
9471
9476
|
import { dirname, resolve as resolve2 } from "node:path";
|
|
9472
9477
|
import { DatabaseSync as DatabaseSync3 } from "node:sqlite";
|
|
@@ -9818,7 +9823,9 @@ function workspaceDisplayName(cwd) {
|
|
|
9818
9823
|
return canonical2.split(/[\\/]/).at(-1) || "\u5F53\u524D\u5DE5\u4F5C\u533A";
|
|
9819
9824
|
}
|
|
9820
9825
|
function feedbackText(value, maximum) {
|
|
9821
|
-
|
|
9826
|
+
if (typeof value !== "string") return "";
|
|
9827
|
+
const text3 = value.trim().replace(/[\uD800-\uDFFF]/gu, "\uFFFD").slice(0, maximum);
|
|
9828
|
+
return text3.replace(/[\uD800-\uDBFF]$/, "");
|
|
9822
9829
|
}
|
|
9823
9830
|
var FEEDBACK_MARKDOWN_HEADINGS = {
|
|
9824
9831
|
description: "\u95EE\u9898\u63CF\u8FF0",
|
|
@@ -10810,6 +10817,7 @@ var StrataGateRuntime = class {
|
|
|
10810
10817
|
await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
|
|
10811
10818
|
if (this.profileStorage) await this.profileStorage.close();
|
|
10812
10819
|
this.sessionsById.clear();
|
|
10820
|
+
this.pendingFeedbackSuggestionSessions.clear();
|
|
10813
10821
|
if (flushError !== void 0) throw flushError;
|
|
10814
10822
|
}
|
|
10815
10823
|
namespaceFor(session) {
|
|
@@ -10936,9 +10944,10 @@ var StrataGateRuntime = class {
|
|
|
10936
10944
|
return { items, total: items.length };
|
|
10937
10945
|
}
|
|
10938
10946
|
notePluginError(session, _error) {
|
|
10939
|
-
if (!this.closed) this.pendingFeedbackSuggestionSessions.add(String(session.id));
|
|
10947
|
+
if (AGENT_FEEDBACK_ENABLED && !this.closed) this.pendingFeedbackSuggestionSessions.add(String(session.id));
|
|
10940
10948
|
}
|
|
10941
10949
|
takeFeedbackSuggestion(session, now = Date.now()) {
|
|
10950
|
+
if (!AGENT_FEEDBACK_ENABLED) return "";
|
|
10942
10951
|
const sessionId = String(session.id);
|
|
10943
10952
|
if (!this.pendingFeedbackSuggestionSessions.delete(sessionId)) return "";
|
|
10944
10953
|
const last = this.lastFeedbackPromptAt();
|
|
@@ -12322,7 +12331,7 @@ function registerMemoryTools(ctx, runtime) {
|
|
|
12322
12331
|
return runtime.updatePersistentProfileFromTool(args.field, args.value);
|
|
12323
12332
|
}
|
|
12324
12333
|
}));
|
|
12325
|
-
ctx.tools.register(defineTool({
|
|
12334
|
+
if (AGENT_FEEDBACK_ENABLED) ctx.tools.register(defineTool({
|
|
12326
12335
|
name: "feedback_prepare",
|
|
12327
12336
|
description: 'This tool is provided by the StrataGate plugin. Create or revise a local StrataGate feedback draft when the user directly requests it, or after a proactive suggestion permitted by the StrataGate feedback policy and the user explicitly agrees. A direct user request is already authorization. Use only facts known from the current conversation; leave unknown fields empty and never invent versions, logs, Block counts, or diagnostics. Never submit anything to GitHub. After success, briefly say the draft is local and not submitted, then render feedbackUrl as a Markdown link labeled "\u6253\u5F00\u53CD\u9988\u8349\u7A3F". Then ask exactly once: "\u8981\u4E0D\u8981\u987A\u4FBF\u8BA9\u6211\u5C1D\u8BD5\u4FEE\u590D\u8FD9\u4E2A\u95EE\u9898\uFF0C\u5E76\u63D0\u4EA4\u4E00\u4E2A PR\uFF1F" If the user does not respond or declines, do not ask again. Do not use a popup or other additional UI. Do not print draft fields or an Issue-content table, and do not direct the user through Settings manually.',
|
|
12328
12337
|
parameters: {
|
|
@@ -14615,7 +14624,7 @@ async function apply(ctx, config) {
|
|
|
14615
14624
|
}
|
|
14616
14625
|
});
|
|
14617
14626
|
ctx.systemPrompt.section({ name: "tool:stratagate-memory", order: 113, text: MEMORY_PROTOCOL });
|
|
14618
|
-
ctx.systemPrompt.section({ name: "tool:stratagate-feedback", order: 114, text: FEEDBACK_PROTOCOL });
|
|
14627
|
+
if (AGENT_FEEDBACK_ENABLED) ctx.systemPrompt.section({ name: "tool:stratagate-feedback", order: 114, text: FEEDBACK_PROTOCOL });
|
|
14619
14628
|
ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
|
|
14620
14629
|
const assembled = await next();
|
|
14621
14630
|
const contexts = [...assembled.contexts];
|
|
@@ -14634,24 +14643,24 @@ async function apply(ctx, config) {
|
|
|
14634
14643
|
autoMemory = { name: "stratagate:auto-memory", text: text3 };
|
|
14635
14644
|
} catch (error) {
|
|
14636
14645
|
ctx.logger.warn(`stratagate-memory auto-context failed: ${renderError(error)}`);
|
|
14637
|
-
runtime.notePluginError(session, error);
|
|
14646
|
+
if (AGENT_FEEDBACK_ENABLED) runtime.notePluginError(session, error);
|
|
14638
14647
|
}
|
|
14639
14648
|
try {
|
|
14640
14649
|
const directory = await runtime.buildMemoryDirectory(session);
|
|
14641
14650
|
if (directory) contexts.push({ name: "stratagate:memory-directory", text: directory });
|
|
14642
14651
|
} catch (error) {
|
|
14643
14652
|
ctx.logger.warn(`stratagate-memory directory failed: ${renderError(error)}`);
|
|
14644
|
-
runtime.notePluginError(session, error);
|
|
14653
|
+
if (AGENT_FEEDBACK_ENABLED) runtime.notePluginError(session, error);
|
|
14645
14654
|
}
|
|
14646
14655
|
if (autoMemory) contexts.push(autoMemory);
|
|
14647
|
-
const feedbackSuggestion = runtime.takeFeedbackSuggestion(session);
|
|
14656
|
+
const feedbackSuggestion = AGENT_FEEDBACK_ENABLED ? runtime.takeFeedbackSuggestion(session) : "";
|
|
14648
14657
|
if (feedbackSuggestion) contexts.push({ name: "stratagate:feedback-suggestion", text: feedbackSuggestion });
|
|
14649
14658
|
return { ...assembled, contexts };
|
|
14650
14659
|
});
|
|
14651
14660
|
installRetrievalFinalization(ctx, runtime);
|
|
14652
14661
|
registerMemoryTools(ctx, runtime);
|
|
14653
14662
|
ctx.on("tools/result", (exec, result) => {
|
|
14654
|
-
if (!result.isError || !exec.agent) return;
|
|
14663
|
+
if (!AGENT_FEEDBACK_ENABLED || !result.isError || !exec.agent) return;
|
|
14655
14664
|
if (exec.name === "feedback_prepare" || exec.name.startsWith("memory_")) {
|
|
14656
14665
|
runtime.notePluginError(exec.agent.session, result.error.message);
|
|
14657
14666
|
}
|