stratagate-dsh 0.3.8 → 0.3.10

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/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'\r\n\r\n// DSH prepares its installation-owned module fallback before it imports a\r\n// profile bundle. Install the narrowly-scoped resolver first, then load the\r\n// implementation so stale profile-local peers cannot win ESM resolution.\r\ninstallDshHostResolution(import.meta.url)\r\n\r\n// This file and src/index.ts are separate tsup entries. The runtime path is\r\n// intentionally external so the implementation is instantiated only after\r\n// the resolver above is active.\r\n// @ts-expect-error dist/plugin.js is emitted from src/index.ts during build.\r\nconst plugin = await import('./plugin.js') as typeof import('./index.js')\r\n\r\nexport const name = plugin.name\r\nexport const inject = plugin.inject\r\nexport const STRATAGATE_SETTINGS_NAMESPACE = plugin.STRATAGATE_SETTINGS_NAMESPACE\r\nexport const Config = plugin.Config\r\nexport const apply = plugin.apply\r\nexport type PluginConfig = import('./index.js').PluginConfig\r\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"]}
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
- return typeof value === "string" ? value.trim().slice(0, maximum) : "";
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: {
@@ -13117,6 +13126,30 @@ async function topicEvents(runtime, url) {
13117
13126
  items: page.map(({ id, title, status, createdAt }) => ({ id, title, status, createdAt }))
13118
13127
  };
13119
13128
  }
13129
+ function liveGraphProjection(snapshot) {
13130
+ const eventIds = new Set([...snapshot.events, ...snapshot.agentEvents].filter(({ status: status2 }) => status2 !== "forgotten" && status2 !== "archived").map(({ id }) => id));
13131
+ const jobs = snapshot.graphProjectionJobs.filter(({ sourceEventIds }) => sourceEventIds.some((id) => eventIds.has(id)));
13132
+ const status = summarizeDerivationJobs(jobs, (job) => job.status === "completed");
13133
+ const projected = new Set(jobs.filter(({ status: status2, projectorVersion }) => status2 === "completed" && projectorVersion === KNOWLEDGE_GRAPH_PROJECTOR_VERSION).flatMap(({ sourceEventIds }) => sourceEventIds.filter((id) => eventIds.has(id)))).size;
13134
+ const total = eventIds.size;
13135
+ const complete = projected === total && status.processing === 0 && status.terminalFailed === 0 && status.blocked === 0;
13136
+ const state = status.processing > 0 ? "processing" : status.terminalFailed > 0 ? "failed" : complete ? "complete" : "incomplete";
13137
+ return {
13138
+ jobs,
13139
+ status,
13140
+ migration: {
13141
+ projected,
13142
+ total,
13143
+ pending: jobs.filter(({ status: status2 }) => status2 === "pending").length,
13144
+ running: jobs.filter(({ status: status2 }) => status2 === "running").length,
13145
+ failed: status.terminalFailed,
13146
+ retryable: status.retryable,
13147
+ processing: status.processing,
13148
+ state,
13149
+ complete
13150
+ }
13151
+ };
13152
+ }
13120
13153
  async function overview(runtime, cachedEntries) {
13121
13154
  const entries = cachedEntries ?? await Promise.all((await runtime.adminNamespaces()).map(async (namespace) => ({
13122
13155
  namespace,
@@ -13222,20 +13255,21 @@ async function overview(runtime, cachedEntries) {
13222
13255
  const topicStatus = summarizeDerivationJobs(topicJobs, (job) => job.status === "completed");
13223
13256
  const summaryStatus = summarizeDerivationJobs(snapshot.summaryJobs, (job) => job.status === "succeeded");
13224
13257
  const extractionStatus = summarizeDerivationJobs(snapshot.extractionJobs, (job) => job.status === "succeeded" || job.status === "skipped");
13225
- const graphStatus = summarizeDerivationJobs(snapshot.graphProjectionJobs, (job) => job.status === "completed");
13258
+ const graphWork = liveGraphProjection(snapshot);
13259
+ const graphStatus = graphWork.status;
13226
13260
  const failedJobs = summaryStatus.terminalFailed + extractionStatus.terminalFailed + graphStatus.terminalFailed + topicStatus.terminalFailed;
13227
13261
  const processingJobs = summaryStatus.processing + extractionStatus.processing + graphStatus.processing + topicStatus.processing;
13228
13262
  const failedJobDetails = [
13229
13263
  ...topicJobs.filter((job) => job.status === "failed").map(describeTopicJob),
13230
13264
  ...snapshot.summaryJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("block-summary", job)),
13231
13265
  ...snapshot.extractionJobs.filter(({ status }) => status === "failed").map((job) => describeBlockJob("event-extraction", job)),
13232
- ...snapshot.graphProjectionJobs.filter(({ status }) => status === "failed").map(describeGraphJob)
13266
+ ...graphWork.jobs.filter(({ status }) => status === "failed").map(describeGraphJob)
13233
13267
  ];
13234
13268
  const processingJobDetails = [
13235
13269
  ...topicJobs.filter((job) => ["processing", "retryable"].includes(derivationJobState(job, job.status === "completed"))).map(describeTopicJob),
13236
13270
  ...snapshot.summaryJobs.filter((job) => derivationJobState(job, job.status === "succeeded") === "processing" || derivationJobState(job, job.status === "succeeded") === "retryable").map((job) => describeBlockJob("block-summary", job)),
13237
13271
  ...snapshot.extractionJobs.filter((job) => derivationJobState(job, job.status === "succeeded" || job.status === "skipped") === "processing" || derivationJobState(job, job.status === "succeeded" || job.status === "skipped") === "retryable").map((job) => describeBlockJob("event-extraction", job)),
13238
- ...snapshot.graphProjectionJobs.filter(graphProjectionIsProcessing).map(describeGraphJob)
13272
+ ...graphWork.jobs.filter(graphProjectionIsProcessing).map(describeGraphJob)
13239
13273
  ];
13240
13274
  const timestamps = [
13241
13275
  ...snapshot.blocks.map(({ createdAt }) => createdAt),
@@ -13265,23 +13299,7 @@ async function overview(runtime, cachedEntries) {
13265
13299
  graphProjection: graphStatus,
13266
13300
  topicProjection: topicStatus
13267
13301
  },
13268
- graphMigration: (() => {
13269
- const projected = new Set(snapshot.graphProjectionJobs.filter(({ status, projectorVersion }) => status === "completed" && projectorVersion === KNOWLEDGE_GRAPH_PROJECTOR_VERSION).flatMap(({ sourceEventIds }) => sourceEventIds)).size;
13270
- const total = snapshot.events.filter(({ status }) => status !== "forgotten" && status !== "archived").length;
13271
- const complete = projected >= total;
13272
- const state = complete ? "complete" : graphStatus.processing > 0 ? "processing" : graphStatus.terminalFailed > 0 ? "failed" : "incomplete";
13273
- return {
13274
- projected,
13275
- total,
13276
- pending: snapshot.graphProjectionJobs.filter(({ status }) => status === "pending").length,
13277
- running: snapshot.graphProjectionJobs.filter(({ status }) => status === "running").length,
13278
- failed: graphStatus.terminalFailed,
13279
- retryable: graphStatus.retryable,
13280
- processing: graphStatus.processing,
13281
- state,
13282
- complete
13283
- };
13284
- })(),
13302
+ graphMigration: graphWork.migration,
13285
13303
  usageReceipts: snapshot.usageReceipts.length,
13286
13304
  memoryUseCount: snapshot.usageReceipts.filter((receipt) => receipt.eventIds.length > 0 || receipt.elementIds.length > 0).length,
13287
13305
  failedJobs,
@@ -13668,23 +13686,7 @@ async function memories(runtime, url) {
13668
13686
  })),
13669
13687
  edges: snapshot.graphEdges,
13670
13688
  clusters: clusterKnowledgeGraph(snapshot.graphNodes, snapshot.graphEdges),
13671
- migration: (() => {
13672
- const projected = new Set(snapshot.graphProjectionJobs.filter(({ status, projectorVersion }) => status === "completed" && projectorVersion === KNOWLEDGE_GRAPH_PROJECTOR_VERSION).flatMap(({ sourceEventIds }) => sourceEventIds)).size;
13673
- const graphStatus = summarizeDerivationJobs(snapshot.graphProjectionJobs, (job) => job.status === "completed");
13674
- const total = snapshot.events.filter(({ status }) => status !== "forgotten" && status !== "archived").length;
13675
- const complete = projected >= total;
13676
- return {
13677
- projected,
13678
- total,
13679
- pending: snapshot.graphProjectionJobs.filter(({ status }) => status === "pending").length,
13680
- running: snapshot.graphProjectionJobs.filter(({ status }) => status === "running").length,
13681
- failed: graphStatus.terminalFailed,
13682
- retryable: graphStatus.retryable,
13683
- processing: graphStatus.processing,
13684
- state: complete ? "complete" : graphStatus.processing > 0 ? "processing" : graphStatus.terminalFailed > 0 ? "failed" : "incomplete",
13685
- complete
13686
- };
13687
- })()
13689
+ migration: liveGraphProjection(snapshot).migration
13688
13690
  };
13689
13691
  } else if (kind === "elements") values = snapshot.elements.map(elementSummary);
13690
13692
  else if (kind === "blocks") {
@@ -14615,7 +14617,7 @@ async function apply(ctx, config) {
14615
14617
  }
14616
14618
  });
14617
14619
  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 });
14620
+ if (AGENT_FEEDBACK_ENABLED) ctx.systemPrompt.section({ name: "tool:stratagate-feedback", order: 114, text: FEEDBACK_PROTOCOL });
14619
14621
  ctx.on("system-prompt/assemble", async (_assembly, context, next) => {
14620
14622
  const assembled = await next();
14621
14623
  const contexts = [...assembled.contexts];
@@ -14634,24 +14636,24 @@ async function apply(ctx, config) {
14634
14636
  autoMemory = { name: "stratagate:auto-memory", text: text3 };
14635
14637
  } catch (error) {
14636
14638
  ctx.logger.warn(`stratagate-memory auto-context failed: ${renderError(error)}`);
14637
- runtime.notePluginError(session, error);
14639
+ if (AGENT_FEEDBACK_ENABLED) runtime.notePluginError(session, error);
14638
14640
  }
14639
14641
  try {
14640
14642
  const directory = await runtime.buildMemoryDirectory(session);
14641
14643
  if (directory) contexts.push({ name: "stratagate:memory-directory", text: directory });
14642
14644
  } catch (error) {
14643
14645
  ctx.logger.warn(`stratagate-memory directory failed: ${renderError(error)}`);
14644
- runtime.notePluginError(session, error);
14646
+ if (AGENT_FEEDBACK_ENABLED) runtime.notePluginError(session, error);
14645
14647
  }
14646
14648
  if (autoMemory) contexts.push(autoMemory);
14647
- const feedbackSuggestion = runtime.takeFeedbackSuggestion(session);
14649
+ const feedbackSuggestion = AGENT_FEEDBACK_ENABLED ? runtime.takeFeedbackSuggestion(session) : "";
14648
14650
  if (feedbackSuggestion) contexts.push({ name: "stratagate:feedback-suggestion", text: feedbackSuggestion });
14649
14651
  return { ...assembled, contexts };
14650
14652
  });
14651
14653
  installRetrievalFinalization(ctx, runtime);
14652
14654
  registerMemoryTools(ctx, runtime);
14653
14655
  ctx.on("tools/result", (exec, result) => {
14654
- if (!result.isError || !exec.agent) return;
14656
+ if (!AGENT_FEEDBACK_ENABLED || !result.isError || !exec.agent) return;
14655
14657
  if (exec.name === "feedback_prepare" || exec.name.startsWith("memory_")) {
14656
14658
  runtime.notePluginError(exec.agent.session, result.error.message);
14657
14659
  }