@lmzhen/dsh-evolution-core 0.26.26 → 0.27.1

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.
Files changed (107) hide show
  1. package/README.md +16 -1
  2. package/lib/composition/index.js +3 -0
  3. package/lib/composition-D8Y3-ROd.js +604 -0
  4. package/lib/ensure-MEYMj9jn.js +222 -0
  5. package/lib/errors-Br2-uUSK.js +57 -0
  6. package/lib/http-routes-BP-S3KV-.js +149 -0
  7. package/lib/http-routes.js +2 -0
  8. package/lib/index.js +66 -850
  9. package/lib/restrictor.js +37 -0
  10. package/lib/subagent/index.js +186 -0
  11. package/lib/types/citations.js +192 -0
  12. package/lib/types/{preset-composition.d.ts → composition/composer.d.ts} +1 -1
  13. package/lib/types/composition/composer.js +681 -0
  14. package/lib/types/composition/ensure.d.ts +10 -0
  15. package/lib/types/composition/ensure.js +95 -0
  16. package/lib/types/composition/index.d.ts +5 -0
  17. package/lib/types/composition/index.js +7 -0
  18. package/lib/types/composition/preset-ids.d.ts +17 -0
  19. package/lib/types/composition/preset-ids.js +21 -0
  20. package/lib/types/composition/restricted-preset.d.ts +36 -0
  21. package/lib/types/composition/restricted-preset.js +42 -0
  22. package/lib/types/constants.js +223 -0
  23. package/lib/types/cost.js +67 -0
  24. package/lib/types/curator.js +346 -0
  25. package/lib/types/diagnostics.d.ts +26 -0
  26. package/lib/types/diagnostics.js +23 -0
  27. package/lib/types/document-body.js +91 -0
  28. package/lib/types/drift-signals.js +319 -0
  29. package/lib/types/env.js +25 -0
  30. package/lib/types/errors.d.ts +4 -1
  31. package/lib/types/errors.js +59 -0
  32. package/lib/types/events.js +16 -0
  33. package/lib/types/evidence.js +65 -0
  34. package/lib/types/evolution-events.js +479 -0
  35. package/lib/types/fold-untrusted.js +44 -0
  36. package/lib/types/frontmatter.js +623 -0
  37. package/lib/types/fuzzy-match.js +135 -0
  38. package/lib/types/gates.js +51 -0
  39. package/lib/types/guards.js +27 -0
  40. package/lib/types/health-baseline.js +63 -0
  41. package/lib/types/http-routes.d.ts +67 -35
  42. package/lib/types/http-routes.js +135 -0
  43. package/lib/types/index.d.ts +7 -2
  44. package/lib/types/index.js +101 -0
  45. package/lib/types/instance-scope.js +69 -0
  46. package/lib/types/io.js +675 -0
  47. package/lib/types/learn-prompt.js +42 -0
  48. package/lib/types/limits.js +63 -0
  49. package/lib/types/live.js +107 -0
  50. package/lib/types/long-runs.js +96 -0
  51. package/lib/types/memory-store.d.ts +0 -16
  52. package/lib/types/memory-store.js +606 -0
  53. package/lib/types/mutations.js +95 -0
  54. package/lib/types/namespace-migration.js +156 -0
  55. package/lib/types/numeric.js +47 -0
  56. package/lib/types/opt-in.js +101 -0
  57. package/lib/types/package-identity.js +47 -0
  58. package/lib/types/params.js +328 -0
  59. package/lib/types/payload.js +36 -0
  60. package/lib/types/platform-line.generated.d.ts +10 -0
  61. package/lib/types/platform-line.generated.js +12 -0
  62. package/lib/types/platform-services.d.ts +2 -2
  63. package/lib/types/platform-services.js +52 -0
  64. package/lib/types/probe.js +122 -0
  65. package/lib/types/prompts.js +409 -0
  66. package/lib/types/quality.js +308 -0
  67. package/lib/types/reachability.js +122 -0
  68. package/lib/types/redact.js +257 -0
  69. package/lib/types/reference-rewrite.js +144 -0
  70. package/lib/types/reports.js +110 -0
  71. package/lib/types/restrictor.d.ts +11 -0
  72. package/lib/types/restrictor.js +48 -0
  73. package/lib/types/review-channel.js +59 -0
  74. package/lib/types/run-registry.js +345 -0
  75. package/lib/types/scope.js +7 -0
  76. package/lib/types/serial.js +17 -0
  77. package/lib/types/session-fold.d.ts +15 -0
  78. package/lib/types/session-fold.js +28 -0
  79. package/lib/types/session-projection.js +295 -0
  80. package/lib/types/signals.js +209 -0
  81. package/lib/types/skill-discovery.js +66 -0
  82. package/lib/types/skill-health.js +134 -0
  83. package/lib/types/skill-history.js +745 -0
  84. package/lib/types/skill-lint.js +112 -0
  85. package/lib/types/skill-reads.js +67 -0
  86. package/lib/types/skill-row-cells.js +123 -0
  87. package/lib/types/skill-store.js +3528 -0
  88. package/lib/types/state-store.js +60 -0
  89. package/lib/types/store-ledger.js +117 -0
  90. package/lib/types/subagent/channel.d.ts +4 -0
  91. package/lib/types/subagent/channel.js +131 -0
  92. package/lib/types/subagent/index.d.ts +4 -0
  93. package/lib/types/subagent/index.js +5 -0
  94. package/lib/types/subagent/readiness.d.ts +6 -0
  95. package/lib/types/subagent/readiness.js +35 -0
  96. package/lib/types/subagent/types.d.ts +58 -0
  97. package/lib/types/subagent/types.js +2 -0
  98. package/lib/types/threats.js +338 -0
  99. package/lib/types/tool-dispatch.d.ts +5 -0
  100. package/lib/types/tool-dispatch.js +535 -0
  101. package/lib/types/tree-write.js +40 -0
  102. package/lib/types/unit-members.js +121 -0
  103. package/lib/types/usage.js +474 -0
  104. package/lib/types/write-authority.js +23 -0
  105. package/lib/types/write-gates.js +344 -0
  106. package/lib/types/write-inventory.js +158 -0
  107. package/package.json +39 -11
package/lib/index.js CHANGED
@@ -1,4 +1,9 @@
1
- import { copyFileSync, existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
1
+ import { _ as scopedPackageName, c as MAINTAINER_PRESET_ID, d as RESTRICTED_TOOLS_ROW_ID, f as RESTRICTOR_MODULE, g as scopeOfNearestManifest, p as REVIEWER_PRESET_ID, s as EVOLUTION_PRESET_IDS, u as RESTRICTED_PRESET_DISPLAY } from "./ensure-MEYMj9jn.js";
2
+ import { a as methodNotAllowed, c as asRecord, i as json, l as isRecord, n as MAX_REQUEST_BODY_BYTES, o as readJsonObject, r as hostSurface, s as registerHostRoutes, t as HOST_ROUTES } from "./http-routes-BP-S3KV-.js";
3
+ import { w as allowRowCollisions } from "./composition-D8Y3-ROd.js";
4
+ import { n as errorText, t as EVOLUTION_ERRORS } from "./errors-Br2-uUSK.js";
5
+ import { createSubagentChannel, probeChannelState, subagentChannelState } from "./subagent/index.js";
6
+ import { copyFileSync, existsSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
2
7
  import { basename, dirname, join, resolve } from "node:path";
3
8
  import { fileURLToPath } from "node:url";
4
9
  import { parse } from "yaml";
@@ -244,50 +249,6 @@ async function withTreeWrite(acquire, view, fn) {
244
249
  }
245
250
  }
246
251
  //#endregion
247
- //#region lib/types/package-identity.js
248
- /**
249
- * G1/1.8 (G-04): the SCOPE this build was published under, DERIVED from a manifest instead of written as a
250
- * literal in running code.
251
- *
252
- * The family used to hard-code `@deepseek-ai/dsh-...` names and let `prepare-release.mjs --scope @lmzhen`
253
- * patch the text afterwards. A published install therefore depended on a release-time string rewrite
254
- * reaching every copy, and the copies had already drifted (doctor.ts, index.ts, preset-composition.ts and
255
- * its .mjs mirror each spelled the identity). The scope is knowable at runtime: it is the scope segment of
256
- * the nearest `package.json` above the calling module. Nothing to rewrite, one owner of the answer.
257
- *
258
- * @module @lmzhen/dsh-evolution-core/package-identity
259
- */
260
- /**
261
- * The scope segment of the nearest `package.json` at or above `from`.
262
- * @param from - the calling module URL (its own manifest is the answer).
263
- * @returns the scope, e.g. `@deepseek-ai` or `@lmzhen`; an unscoped manifest yields an empty string.
264
- */
265
- function scopeOfNearestManifest(from) {
266
- let dir = dirname(from instanceof URL ? fileURLToPath(from) : fileURLToPath(new URL(from)));
267
- for (let hops = 0; hops < 32; hops += 1) {
268
- const manifest = join(dir, "package.json");
269
- if (existsSync(manifest)) {
270
- const parsed = JSON.parse(readFileSync(manifest, "utf8"));
271
- const name = typeof parsed.name === "string" ? parsed.name : "";
272
- return name.startsWith("@") ? name.split("/")[0] ?? "" : "";
273
- }
274
- const parent = dirname(dir);
275
- if (parent === dir) break;
276
- dir = parent;
277
- }
278
- return "";
279
- }
280
- /**
281
- * One family/platform package name under the RUNNING scope.
282
- * @param bareName - the name without any scope, e.g. `dsh-agent-preset`.
283
- * @param from - the calling module URL.
284
- * @returns `scope/bareName`, or `bareName` when the manifest carries no scope.
285
- */
286
- function scopedPackageName(bareName, from) {
287
- const scope = scopeOfNearestManifest(from);
288
- return scope === "" ? bareName : scope + "/" + bareName;
289
- }
290
- //#endregion
291
252
  //#region lib/types/write-authority.js
292
253
  /** The mode a deployment that configures nothing gets. */
293
254
  const DEFAULT_WRITE_CONFIRM_MODE = "auto";
@@ -928,34 +889,6 @@ const MEMORY_GUIDANCE_SECTION_ORDER = 11e3;
928
889
  /** Order of the `evolution-skills-guidance` section (last of the two). */
929
890
  const SKILLS_GUIDANCE_SECTION_ORDER = 11100;
930
891
  //#endregion
931
- //#region lib/types/guards.js
932
- /**
933
- * The family's plain-object predicate, in ONE place (v46 G7.1 / audit A8).
934
- *
935
- * The audit found the same guard — "a non-null object that is not an array" — rewritten fifteen
936
- * times across nine packages, four times inside core itself, and with two byte-identical `asRecord`
937
- * copies in evolution-policy and evolution-threat. This module is the single definition; callers
938
- * import it instead of spelling the clause again.
939
- *
940
- * @module @lmzhen/dsh-evolution-core
941
- */
942
- /**
943
- * Is this a non-null, non-array object (what JSON calls an object)?
944
- * @param value - the value to test.
945
- * @returns whether it is a plain object.
946
- */
947
- function isRecord(value) {
948
- return typeof value === "object" && value !== null && !Array.isArray(value);
949
- }
950
- /**
951
- * Read a value as a record, or undefined when it is not one.
952
- * @param value - the value to read.
953
- * @returns the value typed as a record, or undefined.
954
- */
955
- function asRecord(value) {
956
- return isRecord(value) ? value : void 0;
957
- }
958
- //#endregion
959
892
  //#region lib/types/limits.js
960
893
  /**
961
894
  * Skill content limits: the byte/char budgets every write path validates against.
@@ -3532,194 +3465,6 @@ function fuzzyPatch(content, oldString, newString, replaceAll = false) {
3532
3465
  }
3533
3466
  /** Deterministic section-extraction plan facts; the caller owns the IO and the append semantics. */
3534
3467
  //#endregion
3535
- //#region lib/types/http-routes.js
3536
- /**
3537
- * The loopback HTTP plumbing every family route surface shares (E-302-era conventions).
3538
- *
3539
- * WHY THIS IS IN CORE: the fence, the bounded body reader and the JSON writer are the same for every
3540
- * family route surface, and `evolution-core` is the one package all of them already depend on. The
3541
- * canonical platform implementation (packages/client/connection/src/api-request-trust.ts) is not part
3542
- * of that package's published surface, so the family keeps its own copy of the RULES here — once.
3543
- *
3544
- * Layering: pure transport. Nothing here knows about skills, approvals, curators or route tables.
3545
- * @module @lmzhen/dsh-evolution-core/http-routes
3546
- */
3547
- /** Largest request body a family route accepts; a bigger one reads as "no body" (400 from the handler). */
3548
- const MAX_REQUEST_BODY_BYTES = 64 * 1024;
3549
- /** One header as a single string (a repeated header reads as absent rather than as its first value). */
3550
- function header(request, name) {
3551
- const value = request.headers[name];
3552
- return typeof value === "string" ? value : void 0;
3553
- }
3554
- /** IPv4 127/8 predicate (four decimal octets, first == 127). */
3555
- function isIPv4Loopback(value) {
3556
- const parts = value.split(".");
3557
- return parts.length === 4 && parts[0] === "127" && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
3558
- }
3559
- /** Whether a socket remote address names the loopback range (127/8, ::1, IPv4-mapped). */
3560
- function isLoopbackAddress(address) {
3561
- if (address === void 0) return false;
3562
- const normalized = address.toLowerCase();
3563
- if (normalized === "::1") return true;
3564
- if (normalized.startsWith("::ffff:")) return isIPv4Loopback(normalized.slice(7));
3565
- return isIPv4Loopback(normalized);
3566
- }
3567
- /** Whether a hostname names the loopback authority (localhost, [::1], 127/8). */
3568
- function isLoopbackHostname(hostname) {
3569
- if (hostname === "localhost" || hostname === "[::1]") return true;
3570
- return isIPv4Loopback(hostname);
3571
- }
3572
- /**
3573
- * Whether one request may enter a family route.
3574
- *
3575
- * Rules, in order: the SOCKET must be loopback (authoritative — X-Forwarded-For is never trusted), the
3576
- * Host header must name a loopback authority (DNS-rebinding defense), an explicit cross-site marker is
3577
- * refused, and an attached Origin must be exactly this authority.
3578
- * @param request - the request to judge.
3579
- * @returns true when the request may proceed.
3580
- */
3581
- function isLoopbackRequest(request) {
3582
- if (!isLoopbackAddress(request.socket?.remoteAddress)) return false;
3583
- const host = header(request, "host");
3584
- if (host === void 0) return false;
3585
- let hostUrl;
3586
- try {
3587
- hostUrl = new URL("http://" + host);
3588
- } catch {
3589
- return false;
3590
- }
3591
- if (!isLoopbackHostname(hostUrl.hostname)) return false;
3592
- if (header(request, "sec-fetch-site") === "cross-site") return false;
3593
- const origin = header(request, "origin");
3594
- if (origin === void 0) return true;
3595
- try {
3596
- return new URL(origin).host === hostUrl.host;
3597
- } catch {
3598
- return false;
3599
- }
3600
- }
3601
- /** Write one JSON response. The routes own their response lifecycle, so every path ends here. */
3602
- function writeJson(res, status, value) {
3603
- const body = JSON.stringify(value);
3604
- res.writeHead(status, {
3605
- "content-type": "application/json; charset=utf-8",
3606
- "content-length": Buffer.byteLength(body)
3607
- });
3608
- res.end(body);
3609
- }
3610
- /**
3611
- * Read one JSON request body, bounded.
3612
- * @param req - the request.
3613
- * @returns the parsed value, or null when the body is oversized, empty, or not JSON.
3614
- */
3615
- async function readJsonBody(req) {
3616
- const chunks = [];
3617
- let size = 0;
3618
- for await (const chunk of req) {
3619
- const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
3620
- size += buffer.byteLength;
3621
- if (size > 65536) return null;
3622
- chunks.push(buffer);
3623
- }
3624
- if (size === 0) return null;
3625
- try {
3626
- return JSON.parse(Buffer.concat(chunks).toString("utf8"));
3627
- } catch {
3628
- return null;
3629
- }
3630
- }
3631
- /**
3632
- * Read one JSON request body that must be an OBJECT.
3633
- *
3634
- * The three route surfaces all take named fields, so "array or scalar" is a bad request rather than a
3635
- * shape each handler has to re-check.
3636
- * @param req - the request.
3637
- * @returns the body as a plain object, or null when it is absent, oversized, not JSON, or not an object.
3638
- */
3639
- async function readJsonObject(req) {
3640
- const body = await readJsonBody(req);
3641
- return isRecord(body) ? body : null;
3642
- }
3643
- //#endregion
3644
- //#region lib/types/env.js
3645
- /**
3646
- * Evolution environment-variable reading, single-source (WC, 0.3.56).
3647
- *
3648
- * Every `DSH_EVOLUTION_*` value the PLUGIN code reads goes through this module
3649
- * so the trim/whitelist patterns (v10 P2-12/13) live in exactly one place and
3650
- * the generated env reference can point at one behavior. Configuration-layer
3651
- * reads inside patch YAML `!!js` expressions (session-query path/openAt) stay
3652
- * in the profile config evaluation — they are NOT migrated (they are resolved
3653
- * at cordis config time, not plugin time) but are documented in the README
3654
- * env table. `EVOLUTION_SCOPE` is read by the source installers only
3655
- * (`packages/scripts/install-layered.mjs`, `packages/test-support/row-contract.ts`),
3656
- * never by plugin runtime code.
3657
- *
3658
- * v14 P3-1: the former `EVOLUTION_ENV_KEYS` export was deleted — nothing read
3659
- * it, so the "generated env reference" it claimed to source was never
3660
- * generated (the README table is maintained by hand and now lists every key).
3661
- */
3662
- const ALLOW_ROW_COLLISIONS = "1";
3663
- /** N-5 escape: `DSH_EVOLUTION_ALLOW_ROW_COLLISIONS=1` downgrades a delta-row
3664
- * collision from fail-loud to warn+keep-both. Any other value (including an
3665
- * EMPTY/whitespace string — a set-but-unset variable) keeps the fail-loud. */
3666
- function allowRowCollisions(env = process.env) {
3667
- return env.DSH_EVOLUTION_ALLOW_ROW_COLLISIONS === ALLOW_ROW_COLLISIONS;
3668
- }
3669
- //#endregion
3670
- //#region lib/types/errors.js
3671
- /**
3672
- * THE error-code table: E-301…E-322 with the exact text each one emits (E-317…E-319 are the
3673
- * write-admission gates' own refusals).
3674
- *
3675
- * The codes used to be spelled out at 33 call sites across two packages, so one
3676
- * code's wording could drift between the branches that answer it and nothing could
3677
- * see the drift. Keys name the SCENARIO rather than the code, because a code
3678
- * legitimately carries more than one text (E-301 answers pending / approve / reject
3679
- * with different guidance); the code still leads every string, so model- and
3680
- * user-facing output is unchanged. Interpolations are written `%aN%` and filled by
3681
- * `errorText` from the values the call site used to interpolate inline.
3682
- * @module @lmzhen/dsh-evolution-core
3683
- */
3684
- const EVOLUTION_ERRORS = Object.freeze({
3685
- "e-305-this-invocation-carries-no": "E-305: 本次调用没有携带 agent —— `%a1%` 需要由会话支撑的调用(请在 GUI 或 CLI 的会话里运行)。",
3686
- "e-306-this-deployment-stages-foreground": "E-306: 本部署会暂存前台写入,但 `/evolution %a1%` 无法通过技能运行器回放 —— 没有可暂存的内容。请在批准策略为 'never' 的会话里运行,或在 evolution-approval 行上设置 `stageForeground: false`,然后重试该命令。",
3687
- "e-301-approval-service-not-mounted": "E-301: 批准服务未挂载:待批准写入无法列出或回放。挂载 evolution-approval 行(随 evolution-host/evolution-all 提供)后,运行 /evolution doctor 可查看已挂载的服务。",
3688
- "e-301-approval-service-not-mounted-2": "E-301: 批准服务未挂载。下一步:evolution-approval 行随 evolution-host/evolution-all 一起提供 —— 运行 /evolution doctor 查看已挂载的服务。",
3689
- "e-304-the-mounted-approval-service": "E-304: 已挂载的批准服务早于本能力发布 —— 请改为驳回该记录。",
3690
- "e-302-curator-service-not-mounted": "E-302: 策展服务未挂载。下一步:挂载 evolution-curator 行(evolution-host/evolution-all),并运行 /evolution doctor。",
3691
- "e-305-the-invocation-agent-exposes": "E-305: 调用 agent 既没有暴露 `followup` 也没有 `inject` —— %a1% 没有投递通道。",
3692
- "e-306-this-deployment-stages-foreground-2": "E-306: 本部署会暂存前台写入,但本次调用没有 agent 会话 —— `/evolution restructure` 会暂存一条没有会话归属的记录。请在 GUI 或 CLI 的会话里运行,或在 `stageForeground: false` 行上设置,然后重试该命令。",
3693
- "e-303-replay-service-not-mounted": "E-303: 回放服务未挂载。下一步:挂载 evolution-replay 行(evolution-host/evolution-all),并运行 /evolution doctor。",
3694
- "e-307-the-settings-service-could": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution params 需要用户层来区分覆盖值与部署值;运行 /evolution doctor 查看已挂载的服务。",
3695
- "e-308-unknown-parameter-group-group": "E-308: 未知的参数分组「%a1%」—— 已知分组:%a2%。",
3696
- "e-311-no-settings-service-is": "E-311: 没有挂载设置服务,因此没有可写入的用户层。请挂载 settings 行(packages/settings/settings-file)后重试;本命令从不修改 cordis.yml —— 部署值属于部署。/evolution doctor 会列出已挂载的服务。",
3697
- "e-312-error-instanceof-Error-error": "E-312: %a1% —— /evolution params 会列出规范 id。",
3698
- "e-313-unknown-parameter-id-evolution": "E-313: 未知参数「%a1%」—— /evolution params 会列出每个已注册 id 及其层级与用户层。",
3699
- "e-314-id-is-a-deployment": "E-314: 「%a1%」是部署参数(层级 %a2%,归属 %a3%)—— 它写在 cordis.yml 的插件行或策略行上,而不是从会话写入。/evolution params 会显示每一行由谁可写。",
3700
- "e-315-id-has-no-user": "E-315: 「%a1%」没有用户层 —— 归属 %a2% 没有发布设置命名空间,因此该值留在部署层。",
3701
- "e-307-the-settings-service-could-2": "E-307: 设置服务无法报告它的分区(%a1%)。/evolution policy set 需要当前修订号才能安全写入;运行 /evolution doctor 查看已挂载的服务。",
3702
- "e-316-namespace-namespace-is-not": "E-316: namespace \"%a1%\" 未注册 —— 归属 %a2% 在本组合中未挂载,因此无法从这里写入它的参数。 /evolution params 会把那些行标为 'unregistered'.",
3703
- "e-309-revision-conflict-you-sent": "E-309: 修订冲突 —— 你提交的是 %a1%,文档当前为 %a2%。请用 /evolution params --json 重新读取后重试。",
3704
- "e-309-revision-conflict-message-Re": "E-309: 修订冲突 —— %a1%。请用 /evolution params --json 重新读取后重试。",
3705
- "e-310-the-settings-service-refused": "E-310: 设置服务拒绝了这次写入:%a1% —— 归属插件的规则优先(跨字段成对约束,或只能收紧的上限)。/evolution params 会显示当前值。",
3706
- "e-317-skill-write-not-confirmed": "E-317: 技能 \"%a1%\" 未被%a2% —— 确认提示被拒绝或关闭,因此没有写入任何内容。每次创建与裸删除都会出现该提示;只有操作者要求时才重复调用。",
3707
- "e-318-skill-write-without-a-read": "E-318: 本会话没有读取技能 \"%a1%\",因此拒绝这次写入。请先用 `skill` 工具读取它(失败的读取不算),然后重复该写入。",
3708
- "e-320-the-profile-home-is-not": "E-320: 本宿主没有报告 profile home,因此无法定位旧设置文档。/evolution migrate 读取 `<profile home>/settings.yaml.imported`;请在由 profile 启动的会话里运行。",
3709
- "e-322-a-parameter-would-break-a": "E-322: %a1% 至少应为 %a2%,但这次写入把它设成 %a3%。未写入任何内容。",
3710
- "e-321-no-io-provider-is": "E-321: 没有挂载 evolution-io 提供方,因此无法读取旧设置文档。请挂载 evolution-io 行(evolution-host/evolution-all)后重试。",
3711
- "e-319-skill-write-confirm-timed-out": "E-319: 技能 \"%a1%\" 未被%a2% —— 确认提示在 %a3% 秒内无人应答,因此该写入被取消。没有人在场决定:把 `skillWriteConfirm` 设为 `auto` 可免提示直接写入,保留 `ask` 则一直等到有人应答,或提高 `skillWriteConfirmTimeoutSeconds`。"
3712
- });
3713
- /**
3714
- * Render one table entry, substituting its interpolation slots.
3715
- * @param id - the scenario key.
3716
- * @param args - values for the entry's `%aN%` slots.
3717
- * @returns the message text the call site used to spell out inline.
3718
- */
3719
- function errorText(id, args = {}) {
3720
- return EVOLUTION_ERRORS[id].replace(/%a(\d+)%/g, (_match, n) => String(args["a" + n] ?? ""));
3721
- }
3722
- //#endregion
3723
3468
  //#region lib/types/learn-prompt.js
3724
3469
  /**
3725
3470
  * Open-ended `/evolution learn` prompt builder.
@@ -4299,24 +4044,6 @@ function render(entries) {
4299
4044
  function stripDatePrefix(entry) {
4300
4045
  return entry.replace(/^## \d{4}-\d{2}-\d{2}\n/, "");
4301
4046
  }
4302
- /**
4303
- * Neutralize the platform's `{{name}}` prompt-variable syntax in text that is
4304
- * about to be registered as a `systemPrompt.context` contribution.
4305
- *
4306
- * The platform interpolates every context/section text once per model step and
4307
- * THROWS on an unknown or malformed reference — so one stored memory entry
4308
- * containing `{{...}}` (a CI expression, a Jinja/Helm/Vue template, a doc
4309
- * placeholder) used to fail `assemble()` on EVERY later pre-step of every
4310
- * session under this DSH_HOME, while a registered name such as `{{cwd}}` was
4311
- * silently substituted into the "memory" the model reads. Only the INJECTED
4312
- * text is neutralized: stored entries keep their original bytes.
4313
- *
4314
- * @param text - rendered context text about to leave for the prompt surface.
4315
- * @returns the same text with every `{{` split so it cannot start a reference.
4316
- */
4317
- function neutralizePromptVariables(text) {
4318
- return text.includes("{{") ? text.replaceAll("{{", "{ {") : text;
4319
- }
4320
4047
  /** F-201: does `content` carry the on-disk entry delimiter or a trailing
4321
4048
  * `\n§` fragment that would combine with the render terminator into a real
4322
4049
  * delimiter boundary? Both split the fact into multiple entries on read-back
@@ -4883,7 +4610,7 @@ var MemoryStore = class {
4883
4610
  parts.push(`## ${label} (${safe.length} entries)${usage}${note}\n${body}`);
4884
4611
  } else if (entries.length > 0) parts.push(`## ${label} — ${entries.length} entries withheld by the security scan; none injected`);
4885
4612
  }
4886
- return neutralizePromptVariables(parts.join("\n\n"));
4613
+ return parts.join("\n\n");
4887
4614
  }
4888
4615
  /**
4889
4616
  * Detect on-disk drift for a caller that holds no locked view: `true` when the
@@ -4966,563 +4693,6 @@ async function recordMutation(root, io, record, cap = 500) {
4966
4693
  });
4967
4694
  }
4968
4695
  //#endregion
4969
- //#region lib/types/preset-composition.js
4970
- /**
4971
- * Build the user-root Evolution preset composition from the RUNTIME platform's
4972
- * `standard` preset rows plus the evolution delta rows (P1-1 follow-up,
4973
- * 0.3.15): the agent-preset registry mounts ONE composition file verbatim, so
4974
- * a delta-only `agent.cordis.yml` would produce an agent carrying only the
4975
- * delta rows.
4976
- *
4977
- * Same contract as `install-layered.mjs` `generateAgentPreset` (the source
4978
- * install path) — installer.spec pins byte parity between the two, and both
4979
- * apply the identical row-collision contract: a delta id that overlaps a
4980
- * standard row id fails loud by default, and `DSH_EVOLUTION_ALLOW_ROW_COLLISIONS=1`
4981
- * downgrades it to a warning that keeps both (the row mounts twice).
4982
- *
4983
- * V10-14 / 0.3.53 (P1-2): BOTH composers now inject the Hermes 60-char catalog
4984
- * cap onto the standard-sourced `- id: tool-skill` row of the composed preset
4985
- * (see injectCatalogDescriptionCap) — the session-visible tool-skill instance
4986
- * mounts in the preset's own standing scope, where no profile-root patch can
4987
- * reach it. 0.3.53 moved the injection INTO the composer so
4988
- * `/evolution preset install` (the npm user's only preset path) gets it too;
4989
- * install-layered applies the byte-identical rule, pinned by installer.spec.
4990
- *
4991
- * Row ids are read from `- id:` lines; an id present in both fragments would
4992
- * mount twice and could shadow the platform row, so it fails loud.
4993
- * @param standardComposition - the runtime `standard` preset composition.
4994
- * @param deltaComposition - the evolution delta fragment.
4995
- * @returns the composed preset composition (standard rows first, then delta).
4996
- */
4997
- function composePresetComposition(standardComposition, deltaComposition) {
4998
- const standardIds = compositionRowIds(standardComposition);
4999
- const collisions = [...compositionRowIds(deltaComposition)].filter((id) => standardIds.has(id)).sort();
5000
- if (collisions.length > 0 && !allowRowCollisions()) throw new Error(`evolution preset composition: delta rows collide with runtime standard rows: ${collisions.join(", ")}`);
5001
- if (collisions.length > 0) console.warn(`evolution preset composition: warning — delta rows collide with standard rows (${collisions.join(", ")}); keeping both (DSH_EVOLUTION_ALLOW_ROW_COLLISIONS=1)`);
5002
- return applyRowOverrides(`${standardComposition.replace(/\s+$/, "")}\n\n${deltaComposition.trim()}\n`);
5003
- }
5004
- /**
5005
- * One table, two generation paths: `row-overrides.json` at the PACKAGE ROOT
5006
- * states what a generated preset must carry beyond the platform composition,
5007
- * and the source installer (`scripts/install-layered.mjs`) reads the same file.
5008
- * The path resolves from both `src/` and the built `lib/`, which is why the
5009
- * file sits at the root and ships in `files`.
5010
- *
5011
- * Before 0.3.77 each side carried its own hand-kept copy of this table; they
5012
- * happened to stay byte-identical, which is exactly the kind of agreement no
5013
- * test can keep — the entries are now data, and a divergence is impossible.
5014
- */
5015
- const ROW_OVERRIDES_URL = new URL("../row-overrides.json", import.meta.url);
5016
- /** One cache per PATH: the installer reads the same file through its own root, and a single-slot
5017
- * cache would hand the second caller the first caller's bytes. */
5018
- const overridesCache = /* @__PURE__ */ new Map();
5019
- /**
5020
- * Read and validate the shared override table.
5021
- * G6.1/M1: the path is a PARAMETER (default: the table beside this module). The source installer
5022
- * runs the generated port from `scripts/`, so its own root has to be passed in — a module-relative
5023
- * read would look for `scripts/../row-overrides.json` and fail.
5024
- * @param path - the table to read.
5025
- * @returns the entries, in file order. A malformed table fails LOUD: a composer
5026
- * that silently skipped an entry would ship a preset running the platform
5027
- * default while nothing downstream reported it.
5028
- */
5029
- function loadRowOverrides(path = fileURLToPath(ROW_OVERRIDES_URL)) {
5030
- const cached = overridesCache.get(path);
5031
- if (cached !== void 0) return cached;
5032
- const parsed = JSON.parse(readFileSync(path, "utf8"));
5033
- if (!Array.isArray(parsed)) throw new Error("evolution-core: row-overrides.json must be an array of override entries");
5034
- const entries = [];
5035
- for (const [index, entry] of parsed.entries()) {
5036
- const candidate = entry;
5037
- if (candidate === null || typeof candidate !== "object" || typeof candidate.row !== "string" || candidate.row === "" || typeof candidate.key !== "string" || candidate.key === "" || !Array.isArray(candidate.lines) || candidate.lines.some((line) => typeof line !== "string") || typeof candidate.missingReason !== "string") throw new Error(`evolution-core: row-overrides.json entry ${index} is malformed (row/key/lines/missingReason are required)`);
5038
- entries.push({
5039
- row: candidate.row,
5040
- key: candidate.key,
5041
- lines: candidate.lines,
5042
- missingReason: candidate.missingReason
5043
- });
5044
- }
5045
- overridesCache.set(path, entries);
5046
- return entries;
5047
- }
5048
- /**
5049
- * Ensure every {@link RowOverride} inside its target row.
5050
- *
5051
- * Key-level by construction: we INSERT the override's own lines and leave a row
5052
- * that already carries the key byte-identical, so a re-run never doubles a key.
5053
- * This is the deliberate opposite of the platform's patch layers, where an
5054
- * override REPLACES `config` wholesale — the difference is why the composer can
5055
- * add a key without erasing the platform's own config defaults.
5056
- */
5057
- function applyRowOverrides(composition, overrides = loadRowOverrides()) {
5058
- let lines = composition.split("\n");
5059
- for (const override of overrides) lines = applyOneOverride(lines, override);
5060
- return lines.join("\n");
5061
- }
5062
- function applyOneOverride(lines, override) {
5063
- const rowRe = new RegExp("^- id:\\s*" + override.row.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*$");
5064
- let found = false;
5065
- for (let i = 0; i < lines.length; i += 1) {
5066
- if (!rowRe.test(lines[i] ?? "")) continue;
5067
- found = true;
5068
- let end = i;
5069
- let hasConfig = false;
5070
- for (let j = i + 1; j < lines.length; j += 1) {
5071
- const next = lines[j] ?? "";
5072
- if (next.trim() === "") break;
5073
- if (!/^\s/.test(next)) break;
5074
- if (new RegExp("^ {2}" + override.key + ":(\\s|$)").test(next)) hasConfig = true;
5075
- end = j;
5076
- }
5077
- if (hasConfig) continue;
5078
- lines.splice(end + 1, 0, ...override.lines);
5079
- i = end + override.lines.length;
5080
- }
5081
- if (!found) console.warn("evolution preset composition: warning — " + override.missingReason);
5082
- return lines;
5083
- }
5084
- /**
5085
- * Row ids of a composition fragment (line scan, no YAML library).
5086
- *
5087
- * PLAN S5.9 (2026-09-16, audit P2-27): the id extraction accepts INDENTED
5088
- * `- id:` rows too, so a collision hidden in a nested group is still caught —
5089
- * the old `^- id:` anchored at column 0 and was blind to exactly the rows an
5090
- * upstream group nesting would produce. Twin of `rowIds` in
5091
- * `scripts/install-layered.mjs` (installer.spec pins detection parity).
5092
- * Boundary (current, deliberate): DETECTION covers nested rows, while the
5093
- * override INJECTION anchors (`applyOneOverride` below) still match top-level
5094
- * rows only — the injection indent contract (`^ {2}key:`) is defined against a
5095
- * column-0 row.
5096
- */
5097
- function compositionRowIds(composition) {
5098
- const ids = /* @__PURE__ */ new Set();
5099
- for (const line of composition.split("\n")) {
5100
- const id = /^\s*- id:\s*(\S+)/.exec(line)?.[1];
5101
- if (id) ids.add(id);
5102
- }
5103
- return ids;
5104
- }
5105
- /** The platform package a DECLARATIVE preset row names (0.2.x replaced preset directories with it). */
5106
- const AGENT_PRESET_PACKAGE = scopedPackageName("dsh-agent-preset", import.meta.url);
5107
- /**
5108
- * Compose the DECLARATIVE preset row from the platform base composition and the family delta.
5109
- *
5110
- * 0.2.x removed the directory mechanism: `dsh-agent-preset` is an ordinary composition row whose
5111
- * `config.plugins` carries the preset's complete child plugin list
5112
- * (`packages/preset/agent-preset/src/index.ts`, `agent-preset-registry/src/definition.ts` — the
5113
- * definition has NO base/extends field, which is why the platform base is merged in here).
5114
- *
5115
- * The composition itself is {@link composePresetComposition}: same row-collision contract and the
5116
- * same `row-overrides.json` injection, applied while the composed rows are still at column 0 — the
5117
- * override anchors are defined against a column-0 `- id:` row, so the indentation that puts them
5118
- * under `plugins:` must come last.
5119
- * @param baseComposition - the platform base preset patch/composition rows.
5120
- * @param deltaComposition - the family delta rows.
5121
- * @param identity - row id, preset id, display fields and order.
5122
- * @returns the row text, newline-terminated, ready to merge into a profile patch.
5123
- */
5124
- /**
5125
- * Drop the blank lines that merely separate block-sequence items, keeping the ones INSIDE a block
5126
- * literal (`key: |` / `>`): there a blank line is part of the value, and filtering it flattened a
5127
- * platform base preset's multi-paragraph prompt into one paragraph (G6 review, P1).
5128
- * @param text - the composition rows.
5129
- * @returns the rows with the separating blank lines removed.
5130
- */
5131
- function denseBlockSequence(text) {
5132
- const out = [];
5133
- let scalarIndent = -1;
5134
- for (const line of text.split("\n")) {
5135
- const trimmed = line.trim();
5136
- const indent = line.length - line.trimStart().length;
5137
- if (scalarIndent >= 0 && trimmed !== "" && indent <= scalarIndent) scalarIndent = -1;
5138
- if (scalarIndent < 0) {
5139
- const match = /^(\s*)[\w".-]+:\s*[|>]/.exec(line);
5140
- if (match !== null) scalarIndent = (match[1] ?? "").length;
5141
- }
5142
- if (trimmed === "" && scalarIndent < 0) continue;
5143
- out.push(line);
5144
- }
5145
- return out.join("\n");
5146
- }
5147
- function composePresetRow(baseComposition, deltaComposition, identity) {
5148
- const plugins = denseBlockSequence(composePresetComposition(baseComposition, deltaComposition)).split("\n").map((line) => line === "" ? "" : " " + line).join("\n");
5149
- return [
5150
- "- id: " + identity.rowId,
5151
- " name: '" + AGENT_PRESET_PACKAGE + "'",
5152
- " config:",
5153
- " id: " + identity.id,
5154
- " name: " + JSON.stringify(identity.name),
5155
- " description: " + JSON.stringify(identity.description),
5156
- " order: " + String(identity.order),
5157
- " plugins:",
5158
- plugins,
5159
- ""
5160
- ].join("\n");
5161
- }
5162
- /** The patch-entry key that ADDS a row: a plain entry overrides the row with the same id, so a
5163
- * preset the platform does not ship has to arrive inside an `insert` entry
5164
- * (the platform's own `user-patches.spec.ts` proves the override-vs-insert split). */
5165
- const PATCH_INSERT_KEY = "insert";
5166
- /**
5167
- * Wrap a composed row as the patch entry a profile patch needs.
5168
- *
5169
- * The row {@link composePresetRow} returns starts at column 0; a patch entry's list nests it four
5170
- * spaces deeper. Wrapping is a separate step because the same row text is also what
5171
- * {@link mergePresetRow} matches by id, and because the composer's override anchors are defined
5172
- * against column-0 rows.
5173
- * @param row - the row text, as {@link composePresetRow} returns it.
5174
- * @returns the patch entry text, newline-terminated.
5175
- */
5176
- function composePresetInsert(row) {
5177
- return `- ${PATCH_INSERT_KEY}:\n${row.replace(/\s+$/, "").split("\n").map((line) => " " + line).join("\n")}\n`;
5178
- }
5179
- /**
5180
- * The row list a platform base preset patch carries under `config.plugins`, dedented to column 0.
5181
- *
5182
- * A base preset arrives as a whole patch entry — `- insert:` / `- id: preset-standard` /
5183
- * `config:` / `plugins:` — because that is how the platform ships it
5184
- * (`packages/bundle/web-app/presets/standard.patch.yml`). The preset's own list is the
5185
- * SHALLOWEST `plugins:` line: a child row may carry a key by that name far deeper.
5186
- * @param patchText - the base preset patch file's text.
5187
- * @returns the plugin rows, dedented and newline-terminated.
5188
- * @throws {Error} when the patch carries no `plugins:` list.
5189
- */
5190
- function basePresetPlugins(patchText) {
5191
- const lines = patchText.split("\n");
5192
- let at = -1;
5193
- let indent = Number.POSITIVE_INFINITY;
5194
- for (const [index, line] of lines.entries()) {
5195
- const width = /^(\s*)plugins:\s*$/.exec(line)?.[1]?.length;
5196
- if (width === void 0) continue;
5197
- if (width < indent) {
5198
- indent = width;
5199
- at = index;
5200
- }
5201
- }
5202
- if (at < 0) throw new Error("evolution preset composition: the base preset patch carries no `plugins:` list");
5203
- const block = [];
5204
- for (const line of lines.slice(at + 1)) {
5205
- if (line.trim() === "") {
5206
- block.push(line);
5207
- continue;
5208
- }
5209
- if (line.length - line.trimStart().length <= indent) break;
5210
- block.push(line);
5211
- }
5212
- while (block.length > 0 && block[block.length - 1]?.trim() === "") block.pop();
5213
- const widths = block.filter((line) => line.trim() !== "").map((line) => line.length - line.trimStart().length);
5214
- const dedent = widths.length === 0 ? 0 : Math.min(...widths);
5215
- return block.map((line) => line.slice(0, dedent).trim() === "" ? line.slice(dedent) : line).join("\n") + "\n";
5216
- }
5217
- /** The row id a family preset occupies: the platform ships \`preset-standard\` for id `standard`
5218
- * (`packages/bundle/web-app/presets/standard.patch.yml`), so a family preset id mirrors it. */
5219
- function presetRowId(id) {
5220
- return `preset-${id}`;
5221
- }
5222
- /**
5223
- * The patch entry a profile patch receives for one preset: base rows plus the family delta, wrapped
5224
- * as \`- insert:\`.
5225
- *
5226
- * The two halves are also exposed on their own — {@link composePresetRow} for the row, so
5227
- * {@link mergePresetRow} can match it by id, and {@link basePresetPlugins} to read the platform
5228
- * base out of a shipped patch.
5229
- * @param basePatchText - the platform base preset patch's text.
5230
- * @param deltaComposition - the family delta rows.
5231
- * @param identity - row id, preset id, display fields and order.
5232
- * @returns the patch entry text, newline-terminated.
5233
- */
5234
- function composePresetEntry(basePatchText, deltaComposition, identity) {
5235
- return composePresetInsert(composePresetRow(basePresetPlugins(basePatchText), deltaComposition, identity));
5236
- }
5237
- /**
5238
- * The text of one row's own patch item, or null when the patch carries no such row.
5239
- *
5240
- * The comparison unit for a freshness check: this is byte-for-byte what {@link composePresetEntry}
5241
- * writes for the same row id, whether the row sits inside an \`- insert:\` entry (installer-written)
5242
- * or at column 0 (saved by the Web editor).
5243
- * @param patchText - the patch text.
5244
- * @param rowId - the row id to read.
5245
- * @returns the item's text, newline-terminated, or null.
5246
- */
5247
- function presetRowBody(block) {
5248
- const lines = block.split("\n");
5249
- if (!/^- insert:\s*$/.test(lines[0] ?? "")) return block;
5250
- return lines.slice(1).map((line) => line.startsWith(" ") ? line.slice(4) : line).join("\n");
5251
- }
5252
- /**
5253
- * The row BODY of a patch block, whichever form it is in.
5254
- *
5255
- * {@link presetRowBlock} returns the enclosing item verbatim: an installer-written row sits inside
5256
- * `- insert:` while a row the platform's Web editor saved IS that column-0 item. A comparison must
5257
- * compare like with like — comparing the raw block against `composePresetEntry()` (which always
5258
- * starts with `- insert:`) reported DIFFERS for every editor-saved row, forever (G6 review, P2), and
5259
- * the remedy it printed (`re-run the installer`) was refused by the `exists; use --force` guard.
5260
- * @param block - a block as {@link presetRowBlock} returns it.
5261
- * @returns the row text, `- insert:` unwrapped and its four-space indent stripped.
5262
- */
5263
- function presetRowBlock(patchText, rowId) {
5264
- const lines = patchText.split("\n");
5265
- const span = rowBlockSpan(lines, rowId);
5266
- if (span === null) return null;
5267
- const block = [...lines.slice(span.start, span.end)];
5268
- while (block.length > 0 && (block[block.length - 1] ?? "").trim() === "") block.pop();
5269
- return block.join("\n") + "\n";
5270
- }
5271
- /** The package every shipped base preset patch declares its row with
5272
- * (`packages/bundle/web-app/presets/standard.patch.yml`). The platform's only bundle carrying
5273
- * preset patches today; a bundle a caller mounts is probed beside it. */
5274
- const PLATFORM_BUNDLE_PACKAGE = "@deepseek-ai/dsh-web-app";
5275
- /**
5276
- * What `DSH_AGENT_PRESET_ROOT` must name: a directory holding the platform base preset patches
5277
- * under their shipped file names (`<base>.patch.yml`) — e.g. a copy of a same-version platform
5278
- * tree's `packages/bundle/web-app/presets/`. The desktop application is the shape that needs it:
5279
- * its platform packages live inside `resources/app.asar`, so there is no plain
5280
- * `node_modules/@deepseek-ai/dsh-web-app/presets/` on disk for a source installer to read.
5281
- */
5282
- const AGENT_PRESET_ROOT_HINT = "point DSH_AGENT_PRESET_ROOT at a directory holding <base>.patch.yml (copy it out of a same-version dsh tree: packages/bundle/web-app/presets/)";
5283
- /**
5284
- * Resolve the platform patch file one `--base` names.
5285
- *
5286
- * 0.2.x ships each base preset as a bundle patch layer
5287
- * (`packages/bundle/web-app/presets/<base>.patch.yml`, declared in that package's
5288
- * `dsh.bundle.patch`), so the FILE — not a preset directory — is the base a family row is composed
5289
- * from. Candidates, in order: an explicit `DSH_AGENT_PRESET_ROOT`; a module-graph probe when the
5290
- * caller supplies one (a desktop/Electron host resolves the bundle inside `app.asar`, where no file
5291
- * walk can reach); the target profile's own `node_modules`; then, walking up from the caller,
5292
- * every `presets/` directory of a checkout's bundle packages, then every
5293
- * `@deepseek-ai/dsh-<name>/presets/` directory of an installed tree.
5294
- *
5295
- * A base with no patch file is REFUSED with every probed candidate listed: composing a family preset
5296
- * from another base's rows would mount rows the user did not ask for, with nothing saying so.
5297
- * @param base - the base name, already validated against `evolution-agent/bases.json`.
5298
- * @param sources - the roots to probe.
5299
- * @returns the patch file's absolute path.
5300
- * @throws {Error} when no candidate exists.
5301
- */
5302
- function resolveBasePresetPatch(base, sources) {
5303
- const file = `${base}.patch.yml`;
5304
- const explicit = sources.root?.trim();
5305
- if (explicit !== void 0 && explicit !== "") {
5306
- const path = join(explicit, file);
5307
- if (!existsSync(path)) throw new Error(`evolution preset composition: DSH_AGENT_PRESET_ROOT is set but ${path} does not exist — ${AGENT_PRESET_ROOT_HINT}`);
5308
- return path;
5309
- }
5310
- const probes = [];
5311
- const probe = (path) => {
5312
- probes.push(path);
5313
- return existsSync(path) ? path : void 0;
5314
- };
5315
- if (sources.resolve !== void 0) {
5316
- const probeSpecifier = `${PLATFORM_BUNDLE_PACKAGE}/presets/${file}`;
5317
- try {
5318
- const found = probe(sources.resolve(probeSpecifier));
5319
- if (found !== void 0) return found;
5320
- } catch {
5321
- probes.push(`node module graph: ${probeSpecifier}`);
5322
- }
5323
- }
5324
- if (sources.profileDir !== void 0 && sources.profileDir !== "") for (const name of new Set([PLATFORM_BUNDLE_PACKAGE, ...sources.bundles ?? []])) {
5325
- const found = probe(join(sources.profileDir, "node_modules", ...name.split("/"), "presets", file));
5326
- if (found !== void 0) return found;
5327
- }
5328
- for (let level = sources.fromDir; level !== dirname(level); level = dirname(level)) {
5329
- const bundleRoot = join(level, "packages", "bundle");
5330
- for (const name of listDirectories(bundleRoot)) {
5331
- const found = probe(join(bundleRoot, name, "presets", file));
5332
- if (found !== void 0) return found;
5333
- }
5334
- const scopeRoot = join(level, "node_modules", "@deepseek-ai");
5335
- for (const name of listDirectories(scopeRoot)) {
5336
- if (!name.startsWith("dsh-")) continue;
5337
- const found = probe(join(scopeRoot, name, "presets", file));
5338
- if (found !== void 0) return found;
5339
- }
5340
- }
5341
- for (const root of installRoots(sources.fromDir)) for (const shape of DESKTOP_RESOURCE_SHAPES) {
5342
- const found = probe(join(root, ...shape, "dsh", "node_modules", "@deepseek-ai", "dsh-web-app", "presets", file));
5343
- if (found !== void 0) return found;
5344
- const scanned = probeDesktopScope(join(root, ...shape), file, probes);
5345
- if (scanned !== void 0) return scanned;
5346
- }
5347
- throw new Error(`evolution preset composition: cannot find the runtime platform '${base}' preset patch — no candidate carries presets/${file}. A base with no runtime patch is refused rather than composed from another base: the generated preset must follow the platform it runs on. Probed (in order): ${probes.join(", ")}. On a desktop install the platform packages live inside resources/app.asar and no plain file exists — ${AGENT_PRESET_ROOT_HINT}`);
5348
- }
5349
- /** The Electron resource layouts a desktop installation exposes its unpacked packages under. */
5350
- const DESKTOP_RESOURCE_SHAPES = [["resources", "app.asar.unpacked"], [
5351
- "resources",
5352
- "app",
5353
- "resources",
5354
- "app.asar.unpacked"
5355
- ]];
5356
- /**
5357
- * Installation directories a desktop/Electron tree may sit at, probed before the source walk.
5358
- *
5359
- * The default Windows desktop location plus every ancestor that carries an
5360
- * `resources/app.asar.unpacked` directory (a script run from inside an installation).
5361
- * @param fromDir - the caller's own directory.
5362
- * @returns the candidate installation roots.
5363
- */
5364
- function installRoots(fromDir) {
5365
- const roots = [];
5366
- const localAppData = process.env.LOCALAPPDATA;
5367
- if (process.platform === "win32" && localAppData !== void 0 && localAppData !== "") roots.push(join(localAppData, "Programs", "DeepSeek Harness"));
5368
- for (let level = fromDir; level !== dirname(level); level = dirname(level)) if (existsSync(join(level, "resources", "app.asar.unpacked"))) roots.push(level);
5369
- return roots;
5370
- }
5371
- /**
5372
- * Probe every `@deepseek-ai/dsh-*` package under one unpacked resource root.
5373
- * @param resourceRoot - e.g. `<install>/resources/app.asar.unpacked`.
5374
- * @param file - the patch file name to look for.
5375
- * @param probes - the caller's probe log, appended to for the failure report.
5376
- * @returns the patch path when one exists.
5377
- */
5378
- function probeDesktopScope(resourceRoot, file, probes) {
5379
- const scopeRoot = join(resourceRoot, "dsh", "node_modules", "@deepseek-ai");
5380
- for (const name of listDirectories(scopeRoot)) {
5381
- if (!name.startsWith("dsh-")) continue;
5382
- const path = join(scopeRoot, name, "presets", file);
5383
- probes.push(path);
5384
- if (existsSync(path)) return path;
5385
- }
5386
- }
5387
- /**
5388
- * Subdirectories of one directory, or none when it does not exist: a missing level is an ordinary
5389
- * miss on a candidate walk, not a failure.
5390
- * @param dir - the directory to list.
5391
- * @returns the directory names.
5392
- */
5393
- function listDirectories(dir) {
5394
- try {
5395
- return readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
5396
- } catch {
5397
- return [];
5398
- }
5399
- }
5400
- /**
5401
- * The block of one row id: the enclosing column-0 list item and the row's own first line.
5402
- *
5403
- * An installer-written row sits inside `- insert:` (four spaces deeper); a row the Web editor
5404
- * saved is that column-0 item itself. Both must be replaced whole, so the span reaches back to the
5405
- * enclosing item and forward to the next one.
5406
- * @param lines - the patch text's lines.
5407
- * @param rowId - the row id to locate.
5408
- * @returns the span, or null when the patch carries no such row.
5409
- */
5410
- function rowBlockSpan(lines, rowId) {
5411
- const rowRe = new RegExp("^\\s*- id:\\s*" + rowId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") + "\\s*$");
5412
- const rowStart = lines.findIndex((line) => rowRe.test(line));
5413
- if (rowStart < 0) return null;
5414
- let start = rowStart;
5415
- while (start > 0 && !/^- /.test(lines[start] ?? "")) start -= 1;
5416
- let end = lines.length;
5417
- for (let index = start + 1; index < lines.length; index += 1) if (/^- /.test(lines[index] ?? "")) {
5418
- end = index;
5419
- break;
5420
- }
5421
- return {
5422
- start,
5423
- end,
5424
- rowStart
5425
- };
5426
- }
5427
- /**
5428
- * Remove one row from a patch, taking its `- insert:` item with it when that item holds nothing
5429
- * else (an empty insert entry is left-over structure the platform would keep parsing).
5430
- * @param patchText - the patch text to edit.
5431
- * @param rowId - the row id to remove.
5432
- * @returns the patch text, newline-terminated; byte-identical when the id is absent.
5433
- */
5434
- function removePresetRow(patchText, rowId) {
5435
- const lines = patchText.split("\n");
5436
- const span = rowBlockSpan(lines, rowId);
5437
- if (span === null) return patchText;
5438
- const rowLine = lines[span.rowStart] ?? "";
5439
- const rowIndent = rowLine.length - rowLine.trimStart().length;
5440
- const nested = rowIndent > 0;
5441
- const siblings = lines.slice(span.start + 1, span.end).filter((line) => {
5442
- return line.length - line.trimStart().length === rowIndent && line.trimStart().startsWith("- ");
5443
- }).length;
5444
- const cut = nested && siblings > 1 ? {
5445
- start: span.rowStart,
5446
- end: rowSiblingEnd(lines, span.rowStart, span.end)
5447
- } : span;
5448
- const kept = [...lines.slice(0, cut.start), ...lines.slice(cut.end)];
5449
- while (kept.length > 1 && (kept[kept.length - 1] ?? "") === "") kept.pop();
5450
- const text = kept.join("\n");
5451
- if (text === "") return "";
5452
- return text.endsWith("\n") ? text : text + "\n";
5453
- }
5454
- /**
5455
- * The end of one nested row's own block inside a patch entry.
5456
- * @param lines - the patch text's lines.
5457
- * @param rowStart - the row's `- id:` line.
5458
- * @param itemEnd - the enclosing item's end.
5459
- * @returns the index of the next sibling row, or the item's end.
5460
- */
5461
- function rowSiblingEnd(lines, rowStart, itemEnd) {
5462
- const indent = (lines[rowStart] ?? "").length - (lines[rowStart] ?? "").trimStart().length;
5463
- for (let index = rowStart + 1; index < itemEnd; index += 1) {
5464
- const line = lines[index] ?? "";
5465
- if (line.trim() === "") continue;
5466
- const width = line.length - line.trimStart().length;
5467
- if (width === indent && line.trimStart().startsWith("- ")) return index;
5468
- if (width < indent) return index;
5469
- }
5470
- return itemEnd;
5471
- }
5472
- /**
5473
- * Merge one generated row into a composition (a profile patch) by row id.
5474
- *
5475
- * Idempotent by construction: an existing row with the same `- id:` is REPLACED whole — its block
5476
- * is the enclosing column-0 list item, so an installer-written row inside `- insert:` and a
5477
- * Web-editor-saved row at column 0 are both matched by {@link rowBlockSpan} — and anything else in
5478
- * the patch stays byte-identical, because the patch usually carries rows this installer knows
5479
- * nothing about. A missing row is appended.
5480
- *
5481
- * The platform seeds a fresh profile patch with the empty list `[]`
5482
- * (`packages/boot/app-boot/src/profile.ts`, `PROFILE_PATCH_FILENAME`), which is a COMPLETE YAML
5483
- * document: a block sequence appended after it is not parsable. That seed is therefore replaced, not
5484
- * extended.
5485
- * @param patchText - the current patch text (`''` for a fresh one).
5486
- * @param row - the row text, as {@link composePresetRow} returns it.
5487
- * @param rowId - the row id to replace or append.
5488
- * @returns the merged patch text, newline-terminated.
5489
- */
5490
- function mergePresetRow(patchText, row, rowId) {
5491
- const lines = patchText.split("\n");
5492
- const block = row.replace(/\s+$/, "").split("\n");
5493
- const span = rowBlockSpan(lines, rowId);
5494
- if (span === null) {
5495
- const seedIndex = lines.findIndex((line) => EMPTY_PATCH_SEED_RE.test(line));
5496
- const kept = trimTrailingBlankLines(seedIndex >= 0 ? [...lines.slice(0, seedIndex), ...lines.slice(seedIndex + 1)] : [...lines]);
5497
- return (kept.length === 0 ? block : [...kept, ...block]).join("\n") + "\n";
5498
- }
5499
- return trimTrailingBlankLines([
5500
- ...lines.slice(0, span.start),
5501
- ...block,
5502
- ...lines.slice(span.end)
5503
- ]).join("\n") + "\n";
5504
- }
5505
- /** The empty patch list the platform seeds a profile patch with; see {@link mergePresetRow}. */
5506
- const EMPTY_PATCH_SEED = "[]";
5507
- /** The seed in any spelling the platform accepts (`[]`, `[ ]`, `[] # empty`). */
5508
- const EMPTY_PATCH_SEED_RE = /^\s*\[\s*\](\s*#.*)?\s*$/;
5509
- /** Drop trailing blank lines only: a kept line's own terminator (a CRLF file's `\r`) is never touched. */
5510
- function trimTrailingBlankLines(lines) {
5511
- const out = [...lines];
5512
- while (out.length > 0 && (out[out.length - 1] ?? "").trim() === "") out.pop();
5513
- return out;
5514
- }
5515
- /**
5516
- * The text to write when a patch is left with no entries.
5517
- *
5518
- * A zero-byte patch file is not what the platform writes or parses; its own seed is the empty list.
5519
- * @param patchText - the text {@link removePresetRow} returned.
5520
- * @returns the platform's empty-list seed when nothing is left, the text otherwise.
5521
- */
5522
- function presetPatchText(patchText) {
5523
- return patchText.trim() === "" ? `[]\n` : patchText;
5524
- }
5525
- //#endregion
5526
4696
  //#region lib/types/prompts.js
5527
4697
  /**
5528
4698
  * Review and curation prompts adapted from Hermes Agent
@@ -6835,7 +6005,7 @@ function renderLongRunTable() {
6835
6005
  /**
6836
6006
  * Every platform service the family reaches for with `ctx.get`, and what degrades without it.
6837
6007
  *
6838
- * O-2 (v46 audit): the family probes 28 platform services optionally — the platform has no
6008
+ * O-2 (v46 audit): the family probes 25 platform services optionally — the platform has no
6839
6009
  * formalized "optional dependency", so an ABSENT service is a silent downgrade and the diagnostic
6840
6010
  * surface is the only place a reader can learn about it. `/evolution doctor` used to judge six of
6841
6011
  * them; this table is the single source it judges the rest from, and the arch guard cross-checks it
@@ -6845,7 +6015,7 @@ function renderLongRunTable() {
6845
6015
  * `client` probes run in the browser half, where the host has no view of the seats.
6846
6016
  * @module @lmzhen/dsh-evolution-core
6847
6017
  */
6848
- /** The 28 probes, in service-name order (the guard checks this list against the tree). */
6018
+ /** The 25 probes, in service-name order (the guard checks this list against the tree). */
6849
6019
  const PLATFORM_SERVICE_PROBES = Object.freeze([
6850
6020
  {
6851
6021
  service: "agents",
@@ -6962,11 +6132,6 @@ const PLATFORM_SERVICE_PROBES = Object.freeze([
6962
6132
  feature: "the storage-domain state provider",
6963
6133
  side: "host"
6964
6134
  },
6965
- {
6966
- service: "subagents",
6967
- feature: "spawning review and maintenance subagents",
6968
- side: "host"
6969
- },
6970
6135
  {
6971
6136
  service: "systemPrompt",
6972
6137
  feature: "the protected-skills prompt variable",
@@ -6976,11 +6141,6 @@ const PLATFORM_SERVICE_PROBES = Object.freeze([
6976
6141
  service: "tools",
6977
6142
  feature: "tool registration and the guard seam",
6978
6143
  side: "host"
6979
- },
6980
- {
6981
- service: "webServer",
6982
- feature: "the skill-history panel routes",
6983
- side: "host"
6984
6144
  }
6985
6145
  ]);
6986
6146
  /**
@@ -7367,6 +6527,15 @@ const DISPATCH_EVENT_TYPES = [
7367
6527
  PTC_DISPATCH_EVENT
7368
6528
  ];
7369
6529
  /**
6530
+ * Model-visible session frames: the message types plus the ONE dispatch vocabulary above.
6531
+ * Consumers classify through this list instead of naming event literals (rule N11).
6532
+ */
6533
+ const MODEL_SESSION_EVENT_TYPES = [
6534
+ "user/message",
6535
+ "assistant/message",
6536
+ ...DISPATCH_EVENT_TYPES
6537
+ ];
6538
+ /**
7370
6539
  * \`TypeError\` thrown when a payload that claims to be a dispatch event
7371
6540
  * violates the platform's event declaration. Named so a deployment can tell this
7372
6541
  * refusal apart from a generic failure in a log line.
@@ -14704,4 +13873,51 @@ function foldUntrustedLines(text, limit) {
14704
13873
  return limit > 0 ? marked.slice(0, limit) : marked;
14705
13874
  }
14706
13875
  //#endregion
14707
- export { AGENT_PRESET_PACKAGE, AGENT_PRESET_ROOT_HINT, AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ADMISSION, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DEFAULT_WRITE_CONFIRM_MODE, DEFAULT_WRITE_CONFIRM_TIMEOUT_SECONDS, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, EMPTY_PATCH_SEED, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, RULES, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, STORE_LEDGER, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPORT_SPRAWL_FILES, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, admissionVerdict, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, authorityFrom, backupsReachability, basePresetPlugins, blobPath, bodyCost, buildCuratorRunReport, buildHealthBaseline, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, candidateSkills, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, clearReviewChannel, collectReadSkillNames, composePresetComposition, composePresetEntry, composePresetInsert, composePresetRow, compositionRowIds, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, countReferenceFiles, createGateSet, daysSinceIso, declaredReportStamp, denseBlockSequence, describeReferenceRewrite, diffFindings, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, h2Sections, historyIndexFile, historyReachability, historyRoot, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isLandmark, isLoopbackRequest, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, landmarkVerdict, latestActivityAt, latestVersionAt, lifecycleCandidate, lintBody, lintSkill, listDirectories, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadRowOverrides, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, mergePresetRow, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, neutralizePromptVariables, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, presetPatchText, presetRowBlock, presetRowBody, presetRowId, probeAbsent, probeDesktopScope, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, protectedSkillNamesOf, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonBody, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, referencedBlobs, relatedSkillNames, releaseInstance, removePresetRow, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsReachability, reportsSweepLockTarget, resolveBasePresetPatch, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, resolveUnitMembers, resolveUnitMembersFromIndexes, restructureCandidates, retainEventArchives, retirementReport, reviewPrompt, rowBlockSpan, rowSiblingEnd, runWriteGates, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopeOfNearestManifest, scopedPackageName, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillWriteGateContext, skillsRoot, staleRouteStubs, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, trimTrailingBlankLines, unhookedSupportPointers, unitMembersAreComplete, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, withTreeWrite, writeDurableTmp, writeJson, writePathSymbols, yamlPlainScalarNeedsQuotes };
13876
+ //#region lib/types/diagnostics.js
13877
+ const DIAGNOSTICS_VIEW_SCHEMA_VERSION = 2;
13878
+ const DIAGNOSTICS_FACT_KEYS = [
13879
+ "platform-line",
13880
+ "family-version",
13881
+ "install-form",
13882
+ "preset-ids",
13883
+ "readonly-contract",
13884
+ "upgrade-order",
13885
+ "fallback-map"
13886
+ ];
13887
+ const DIAGNOSTICS_RUNTIME_KEYS = [
13888
+ "subagent-channel",
13889
+ "restricted-preset",
13890
+ "host-surface",
13891
+ "session-fold",
13892
+ "platform-services",
13893
+ "runs",
13894
+ "platform-pairing",
13895
+ "install-form-detected"
13896
+ ];
13897
+ //#endregion
13898
+ //#region lib/types/platform-line.generated.js
13899
+ /** The frozen platform line this release targets (peers/engines/anchors derived from version+floor). */
13900
+ const PLATFORM_LINE = {
13901
+ version: "0.2.1-rc.2",
13902
+ floor: "0.2.1-rc.1",
13903
+ previousAnchor: "0.2.0-rc.2",
13904
+ peersRange: "^0.2.1-rc.1",
13905
+ enginesRange: ">=0.2.1-rc.1 <0.2.2-0",
13906
+ upstreamRef: "dsh-v0.2.1-rc.2"
13907
+ };
13908
+ //#endregion
13909
+ //#region lib/types/session-fold.js
13910
+ /** Frames the model authored/saw (the only frames evidence, reads and usage accounting may consume). */
13911
+ const MODEL_EVENT_TYPES = new Set(MODEL_SESSION_EVENT_TYPES);
13912
+ /** Classify one session event WITHOUT interpreting it (pure). */
13913
+ function classifySessionEvent(event) {
13914
+ const type = typeof event.type === "string" ? event.type : "";
13915
+ if (type.startsWith("plugin:")) return event.ignorable === true ? "plugin-record" : "unknown";
13916
+ return MODEL_EVENT_TYPES.has(type) ? "model" : "unknown";
13917
+ }
13918
+ /** True when the event must be skipped by evidence/usage readers (tolerant, never fatal). */
13919
+ function isIgnorableEvent(event) {
13920
+ return classifySessionEvent(event) !== "model";
13921
+ }
13922
+ //#endregion
13923
+ export { AUTHORING_DESCRIPTION_BAR, AUTHORING_SPLIT_LINE_CHARS, BASELINE_ACTION, CITATION_SUPPORT_DIRS, COMBINED_REVIEW_PLAN_PROMPT, COMBINED_REVIEW_PROMPT, COMPLETION_SKILL_REVIEW_PROMPT, CONTENT_SPLIT_HINT, COST_ASCII_TOKEN_HIGH, COST_ASCII_TOKEN_LOW, COST_CJK_TOKEN_HIGH, COST_CJK_TOKEN_LOW, CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEDUP_MAX_PAIR_COMPARISONS, DEDUP_SIMILARITY_THRESHOLD, DEFAULT_ADMISSION, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_ARCHIVE_RETENTION_POLICY, DEFAULT_CITATION_POLICY, DEFAULT_CITATION_REF_BUDGET, DEFAULT_CONSOLIDATION_FAILURES, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MAX_OPS_PER_PLAN, DEFAULT_MEMORY_CHAR_LIMIT, DEFAULT_MEMORY_REVIEW_MODEL, DEFAULT_MIN_IDLE_HOURS, DEFAULT_MUTATION_CAP, DEFAULT_PROBE_TIMEOUT_MS, DEFAULT_REFERENCE_REWRITE_POLICY, DEFAULT_REVIEW_CONTEXT_MESSAGES, DEFAULT_REVIEW_MEMORY_INTERVAL, DEFAULT_REVIEW_MESSAGE_CHARS, DEFAULT_REVIEW_SKILL_INTERVAL, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_RUN_RECORDS, DEFAULT_SKILL_CONTENT_CHARS, DEFAULT_SKILL_LIMITS, DEFAULT_SKILL_REVIEW_COMPLETION_MIN_TOOL_CALLS, DEFAULT_SKILL_REVIEW_MODEL, DEFAULT_SKILL_REVIEW_TRIGGER, DEFAULT_SKILL_VERSION_KEEP, DEFAULT_STALE_AFTER_DAYS, DEFAULT_SUBSTANTIVE_MIN_AGENT_CHARS, DEFAULT_SUBSTANTIVE_MIN_TOOL_CALLS, DEFAULT_SUBSTANTIVE_MIN_USER_CHARS, DEFAULT_SUPPORT_FILE_CHAR_POLICY, DEFAULT_SUPPORT_READ_TOOL_NAMES, DEFAULT_THREAT_MAX_SCAN_CHARS, DEFAULT_USER_CHAR_LIMIT, DEFAULT_WRITE_CONFIRM_MODE, DEFAULT_WRITE_CONFIRM_TIMEOUT_SECONDS, DIAGNOSTICS_FACT_KEYS, DIAGNOSTICS_RUNTIME_KEYS, DIAGNOSTICS_VIEW_SCHEMA_VERSION, DISPATCH_EVENT_TYPES, DRIFT_MAX_LINE_CHARS, DRIFT_SIGNALS_VERSION, DRIFT_SIGNAL_NOUNS, DSH_AUTHORING_STANDARDS, ENTRY_DELIMITER, EVENT_ARCHIVE_PREFIX, EVENT_LOG_RETAIN_ARCHIVES, EVENT_LOG_ROTATE_AT, EVENT_LOG_VERSION, EVOLUTION_ERRORS, EVOLUTION_PRESET_IDS, EVOLUTION_WRITE_TOOLS, EvolutionGateSet, FAMILY_SESSION_TOOL_NAMES, FORBIDDEN_CONTROL_KEYS, FUZZY_MAX_PATTERN_CHARS, FUZZY_MAX_WORK, HEALTH_STAMP_RE, HISTORY_DIR, HISTORY_INDEX_VERSION, HOOK_LINE_RE, HOST_ROUTES, INSTANCE_KEYS, KEEP_LINE_RE, LONG_RUNS, LOW_QUALITY_THRESHOLD, MAINTAINER_PRESET_ID, MAINTAIN_OUTPUT_INSTRUCTION, MAINTAIN_PROMPT, MAX_DESCRIPTION_LENGTH, MAX_REQUEST_BODY_BYTES, MAX_RESTRUCTURE_MOVES, MAX_SKILL_CONTENT_CHARS, MAX_SKILL_FILE_BYTES, MAX_SKILL_NAME_LENGTH, MAX_SUPPORT_READ_PATHS, MAX_TIMER_DELAY_MS, MEMORY_GUIDANCE_SECTION_ORDER, MEMORY_REVIEW_PROMPT, MIN_STAMP_BODY_CHARS, MODEL_SESSION_EVENT_TYPES as MODEL_SESSION_EVENTS, MODEL_SESSION_EVENT_TYPES, MUTATIONS_FILE_VERSION, MemoryStore, NAMESPACE_MIGRATIONS, NATIVE_CALL_EVENT, NATIVE_RESULT_EVENT, PARAM_ALIASES, PARAM_EXPOSURE, PARAM_GROUP_LABELS, PARAM_NAMESPACES, PATTERN_OVERLAP, PLATFORM_CATALOG_DESCRIPTION_DEFAULT, PLATFORM_ESTIMATE_CHARS_PER_TOKEN, PLATFORM_LINE, PLATFORM_SERVICE_PROBES, POLICY_STAGE_DEFAULTS, PROMPT_BUNDLE, PROMPT_BUNDLE_ID, PROMPT_BUNDLE_VERSION, PROTECTED_BUILTIN_SKILLS, PTC_DISPATCH_EVENT, PTC_DISPATCH_START_EVENT, QUALITY_WEIGHTS, READ_REQUIRED_ACTIONS, RESTRICTED_PRESET_DISPLAY, RESTRICTED_TOOLS_ROW_ID, RESTRICTOR_MODULE, RESTRUCTURE_TARGET_RE, RETENTION_FEEDBACK_KEEP_RATIO, RETENTION_FEEDBACK_MIN_CHARS, REVIEWER_PRESET_ID, RULES, RUNS_SCHEMA_VERSION, SKILLS_GUIDANCE, SKILLS_GUIDANCE_SECTION_ORDER, SKILL_ACTION_REQUIRED_FIELDS, SKILL_NAME_RE, SKILL_REVIEW_PLAN_PROMPT, SKILL_REVIEW_PROMPT, SNAPSHOT_EXTRA_NAME_RE, STORE_LEDGER, SUMMARY_DUPLICATE_HINT_THRESHOLD, SUPPORT_DIRS, SUPPORT_SPRAWL_FILES, SUPPRESSED_FILE_VERSION, SkillLibrary, TEXT_DIFF_MAX_CHARS, TEXT_DIFF_MAX_LINES, THREAT_EXEMPTION_HINT, TOKEN_ASCII_WEIGHT, TOKEN_CJK_WEIGHT, ToolDispatchNormalizer, ToolDispatchPayloadError, UNTRUSTED_LINE_BREAK, UNTRUSTED_LINE_MARK, UPSTREAM_LIMIT_CHARS_PER_TOKEN, VERSION_SUMMARY_MAX_CHARS, absentHostServices, admissionVerdict, advanceReview, affinity, ageCopyKey, allowRowCollisions, anchorVerdict, appendEvolutionEvent, applyCuratorLifecycleFields, applyCuratorMetaFields, applyNamespaceMigration, applyReferenceRewrite, asRecord, assertDispatchPayload, assertSkillsRootAliasRetired, assessStructureHealth, atomicWriteFiles, authoringFeedback, authorityFrom, backupsReachability, blobPath, bodyCost, buildCuratorRunReport, buildHealthBaseline, buildLearnPrompt, bumpPatch, bumpSupportRead, bumpUse, bumpView, callingScope, candidateSkills, canonicalWriteId, claimInstance, clampOnce, clampedNumber, classifyNamespaceMigration, classifySessionEvent, clearReviewChannel, collectReadSkillNames, computeDedupGroups, computeDriftSignals, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, contentRetentionFeedback, countDispatches, countReferenceFiles, createGateSet, createSubagentChannel, daysSinceIso, declaredReportStamp, describeReferenceRewrite, diffFindings, dispatchKeyOf, dispatchStepOf, displayBodyOf, duplicateHeadings, elapsedSince, emptyRecord, entryTarget, errorText, evaluateThreat, eventsFile, evidenceKindIndex, evolutionActivityProjection, evolutionEventPayloadIssue, evolutionEvidenceProjection, evolutionHome, evolutionIoAdapter, evolutionReadsProjection, evolutionRoot, evolutionSignalsProjection, filterUnreadSkillOps, findDriftSignal, foldCuratorFields, foldToolDispatches, foldTurnEvent, foldTurnEvents, foldUntrustedLines, frontmatterBlock, frontmatterCatalogInvalid, fuzzyPatch, getRecord, h2Sections, historyIndexFile, historyReachability, historyRoot, hostSurface, identity, idleDays, initialTurnFold, installEvolutionProjections, instanceClaimKey, instanceClaimedWriteSites, instanceHolder, isAbsent, isBookkeepingFrame, isCommittedWarning, isDeprecatedParamId, isFileShapedPath, isIgnorableEvent, isLandmark, isMissingPath, isPresent, isProcessAlive, isProgramToolName, isRecord, isReviewChannelSession, isSkillReadToolName, isSkillToolName, isUnknown, isUnreadWrite, json, landmarkVerdict, latestActivityAt, latestVersionAt, lifecycleCandidate, lintBody, lintSkill, listEventArchives, liveBool, liveEnum, liveNumber, liveString, loadMutations, loadSuppressedNames, loadUsage, loadVersionContent, loadVersions, makeSerialQueue, mapProbe, markAgentCreated, markReviewChannel, markerEntryName, memoryRoot, methodNotAllowed, missingSupportPointers, mutateUsage, mutationsFile, narrowNameMatches, nearDuplicateSummaries, newRunRegistry, newSkillLibrary, nextHistoryIndex, nodeEvolutionIo, normalizeFrontmatter, normalizeUsageRecord, normalizeVersionSummary, observeEvent, orderVersions, overlongLines, paramNamespace, paramRowId, paramSettingsId, parseCuratorNominations, parseEvolutionEvents, parseFrontmatter, parseHistoryIndex, partitionVersions, payloadText, persistedWriteSite, persistedWriteSites, pickWithPolicy, planNamespaceMigration, planReferenceRewrite, planRehoming, policyStageLimits, probeAbsent, probeChannelState, probeList, probeMtime, probePresent, probeReason, probeText, probeUnknown, projectionText, protectedSkillNamesOf, readDispatchSignal, readEvolutionEvents, readEvolutionTimeline, readHistoryIndex, readJsonObject, readNumberParam, readParam, readingStats, recordMutation, recordVersions, redactSecrets, referencedBlobs, registerHostRoutes, relatedSkillNames, releaseInstance, renameWithRetry, renderCuratorReportMarkdown, renderLongRunTable, reportTime, reportsReachability, reportsSweepLockTarget, resolveCitations, resolveExecOrigins, resolveOrigins, resolveParamId, resolveRootConfig, resolveSkillsRoot, resolveUnitMembers, resolveUnitMembersFromIndexes, restructureCandidates, retainEventArchives, retirementReport, reviewPrompt, runWriteGates, runsFile, saveSuppressedNames, saveUsage, scanBodyHooks, scanContentThreats, scanMemoryThreats, scanThreats, scopeOfNearestManifest, scopedPackageName, scopedProbeReport, sessionAudited, sessionEvidenceIndex, sessionLastEventTime, sessionReadNames, sessionReadSkillNames, sessionSeesFamilyTools, sessionTurnSignals, skillReadNameOf, skillRowCells, skillStateKey, skillWriteGateContext, skillsRoot, staleRouteStubs, subagentChannelState, supportFileReadOf, suppressedFile, sweepReports, sweepReviewChannelSessions, textDiffFacts, tokenLineFor, transactIo, transactTaskGuard, trimPatternBoundaries, unhookedSupportPointers, unitMembersAreComplete, unwrapLive, updateSuppressedNames, usageFile, usageObserved, userSetKeys, validateFrontmatter, validateRestructureTarget, valueOr, verifyPromptBundle, versionActionKind, versionRowCells, versionSummaryPrompt, versionTarget, vocabulary, withTreeWrite, writeDurableTmp, writePathSymbols, yamlPlainScalarNeedsQuotes };