dsh-plugin-prompt-tool 3.0.0 → 3.0.2

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 (33) hide show
  1. package/engine/actions/catalog.mjs +2 -1
  2. package/engine/actions/shared.mjs +26 -12
  3. package/engine/actions/tool-visibility.mjs +27 -13
  4. package/engine/fillers.mjs +26 -2
  5. package/lib/client.js +4 -4
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.mjs +96 -49
  8. package/modules/ponytail/module.yml +1 -1
  9. package/modules/soul/assets/layers.md +1 -1
  10. package/modules/soul/assets/manifest.yml +1 -1
  11. package/modules/soul/assets/reference/00-layers.md +27 -27
  12. package/modules/soul/assets/reference/01-criteria.md +32 -32
  13. package/modules/soul/assets/reference/02-echo.md +161 -158
  14. package/modules/soul/assets/reference/03-vital.md +183 -183
  15. package/modules/soul/assets/reference/04-manifest.json +106 -106
  16. package/modules/soul/assets/reference/05-doctor.md +67 -67
  17. package/modules/soul/assets/reference/06-reference.md +189 -189
  18. package/modules/soul/assets/reference/07-bundle.json +431 -431
  19. package/modules/soul/assets/reference/08-/350/270/251/345/235/221/345/256/236/345/275/225.md +59 -59
  20. package/modules/soul/assets/reference/09-dev-workflow.md +109 -109
  21. package/modules/soul/assets/reference/10-echo-anatomy.md +109 -100
  22. package/modules/soul/assets/reference/11-self.md +80 -80
  23. package/modules/soul/assets/reference/12-ledger.md +159 -159
  24. package/modules/soul/assets/reference/ECHO-/346/235/203/345/250/201/345/217/243/345/276/204.md +202 -0
  25. package/modules/soul/assets/reference/ECHO/345/205/255/345/261/202/344/270/216mnemon/345/215/217/345/220/214/350/257/264/346/230/216/344/271/246.md +229 -0
  26. package/modules/soul/assets/reference/README.md +4 -2
  27. package/modules/soul/assets/templates/agents-template.md +1 -1
  28. package/modules/soul/module.yml +2 -2
  29. package/modules/superpowers/module.yml +1 -1
  30. package/modules/tool-surface/module.yml +7 -4
  31. package/package.json +1 -1
  32. package/skills/dsh-module/reference.md +3 -3
  33. package/templates/80-tool-surface.yml +7 -6
package/lib/index.mjs CHANGED
@@ -1361,19 +1361,27 @@ function createMask(source, label, plugin, allowExtra) {
1361
1361
  /** 动态白名单的落点(活集合,由调用方每轮重填);未声明 allowFrom 时不存在。 */
1362
1362
  allowExtra,
1363
1363
  /**
1364
- * fail-open 兜底开关(**可选**,缺省 false):原 `tool-bootstrap` 的语义是
1365
- * 「keep 名单里的工具在本次装配目录里一个都不存在 → 放弃裁剪、暴露完整目录」,而原
1366
- * `tool-filter` 的同名场景是**照名单裁成空目录**。两种语义都真实存在,所以这里
1367
- * **不设默认**——要兜底的声明自己写 `requireMatch: true`。把某一个模块的特有语义当成
1368
- * 通用默认,会让另一个声明路径的行为被悄悄改掉(这正是对拍抓出来的)。
1364
+ * fail-open 兜底开关(**可选**,缺省 false):判据是「这份名单与装配面整体错位」——
1365
+ * 名单里的名字**一个都不存在**时放弃裁剪、暴露完整目录;部分缺失只留诊断,收窄照常
1366
+ * 生效(提供方被替换或关闭是正常部署差异,见 {@link toolSurfacePlan})。原
1367
+ * `tool-bootstrap` 的同名语义就是「一个都不存在 → 放弃裁剪」,而原 `tool-filter` 是
1368
+ * 「永远照名单裁」;两种语义都真实存在,所以这里**不设默认**——要兜底的声明自己写
1369
+ * `requireMatch: true`。把某一个模块的特有语义当成通用默认,会让另一个声明路径的行为
1370
+ * 被悄悄改掉(这正是对拍抓出来的)。
1369
1371
  */
1370
1372
  requireMatch: source?.requireMatch === true,
1371
- /** 与 tool-filter 的名单判据 / Layer.admits 同一判据(tool-filter 模块已删除,判据保留)。 */
1372
- blocks: (toolName) => {
1373
+ /**
1374
+ * 与 tool-filter 的名单判据 / Layer.admits 同一判据(tool-filter 模块已删除,判据保留)。
1375
+ *
1376
+ * 第二个参数是**解锁项快照**:allowExtra 是跨 agent 共享的活集合,每次装配都会 clear 后
1377
+ * 按当前会话重填;执行闸在该装配之后才被调用,必须读自己会话的冻结快照,否则别的会话
1378
+ * (或探针装配)会把本会话的解锁名抹掉。缺省仍是活集合,供同一次装配内的裁剪使用。
1379
+ */
1380
+ blocks: (toolName, extra = allowExtra) => {
1373
1381
  if (typeof toolName !== "string" || toolName.length === 0) return false;
1374
1382
  if (toolName === "run_code") return false;
1375
1383
  if (deny !== void 0 && deny.has(toolName)) return true;
1376
- if (allowExtra?.has(toolName) === true) return false;
1384
+ if (extra?.has(toolName) === true) return false;
1377
1385
  return (allow !== void 0 || allowExtra !== void 0) && allow?.has(toolName) !== true;
1378
1386
  }
1379
1387
  };
@@ -1381,14 +1389,21 @@ function createMask(source, label, plugin, allowExtra) {
1381
1389
  /**
1382
1390
  * 工具可见面的单一判据:装配目录、`tools:sdk` 正文与执行闸共用同一份 fail-open 决定。
1383
1391
  *
1384
- * `available` = 目录名 ∪ SDK 正文名;`required` = 静态 allow ∪ 动态解锁项,再剔掉
1392
+ * `available` = 目录名 ∪ SDK 正文名;`required` 只取**静态 allow**,再剔掉
1385
1393
  * `run_code`(保留传输)与 `mask.blocks()` 为真的名字(deny 覆盖者不该被要求存在)。
1386
- * 缺项时三面必须一起放宽——只放宽一面会让模型看到的与能调的不一致。
1394
+ *
1395
+ * 放宽条件是「名单里的名字**一个都不存在**」:那才说明装配面与声明整体错位(换了一套
1396
+ * 部署/平台)。只缺一部分是提供方被替换或关掉的正常部署差异——照常收窄,缺的工具由发现
1397
+ * 通道补位,替换提供方的名字由部署层名单登记。解锁名(allowExtra)**不参与判定**:它是只做
1398
+ * 加法的提示,若参与,模型解锁一个拼错或已改名的工具就能关掉收窄。
1399
+ *
1400
+ * 放宽时三面必须一起放宽——只放宽一面会让模型看到的与能调的不一致。
1387
1401
  */
1388
1402
  function toolSurfacePlan(mask, available) {
1389
- const missing = (mask.requireMatch ? [.../* @__PURE__ */ new Set([...mask.allow ?? [], ...mask.allowExtra ?? []])].filter((name) => name !== "run_code" && !mask.blocks(name)) : []).filter((name) => !available.has(name));
1403
+ const required = mask.requireMatch ? [...new Set(mask.allow ?? [])].filter((name) => name !== "run_code" && !mask.blocks(name)) : [];
1404
+ const missing = required.filter((name) => !available.has(name));
1390
1405
  return {
1391
- failOpen: missing.length > 0,
1406
+ failOpen: required.length > 0 && missing.length === required.length,
1392
1407
  missing
1393
1408
  };
1394
1409
  }
@@ -1490,7 +1505,7 @@ const ACTION_KINDS = Object.freeze({
1490
1505
  ],
1491
1506
  degrade: ACTION_DEGRADE.keep,
1492
1507
  timing: "system-prompt/assemble 的下游结算之后:按当前条件重算目录、SDK 正文与执行闸;条件翻转时撤销上一轮注册的闸。",
1493
- note: "一份声明拥有工具的可见面:allow/deny 名单、requireMatch、发现工具。名单语义对齐官方 ToolRestriction 的 and/or(同时给时先按 allow 保留、再按 deny 剔除)。deny 走 guard(本层与晚到工具)与 restrict(继承的全局工具);allow 只有 restrict 能覆盖全局工具,本层注册无法在执行层「只保留」——这是官方 restrict 的边界,不是本动作的取舍。"
1508
+ note: "一份声明拥有工具的可见面:allow/deny 名单、requireMatch、发现工具。名单语义对齐官方 ToolRestriction 的 and/or(同时给时先按 allow 保留、再按 deny 剔除)。deny 走 guard(本层与晚到工具)与 restrict(继承的全局工具);allow 只有 restrict 能覆盖全局工具,本层注册无法在执行层「只保留」——这是官方 restrict 的边界,不是本动作的取舍。requireMatch 表达「名单与装配面整体错位」:名单里的名字一个都不存在才整面放宽,部分缺失只留诊断;解锁名不参与判定。"
1494
1509
  },
1495
1510
  "request-params": {
1496
1511
  title: "改模型请求参数",
@@ -7635,6 +7650,29 @@ function parse$1(src, reviver, options) {
7635
7650
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
7636
7651
  }
7637
7652
  //#endregion
7653
+ //#region engine/tool-scope.mjs
7654
+ /**
7655
+ * tool-scope — 工具面的作用域快照,避免各消费方把全局视图当成模型可见面。
7656
+ *
7657
+ * 返回两个字段,各自只服务一个调用方:
7658
+ * - visible:带 agent 的 `tools.schemas(agent)`,包含预设/祖先/本层工具;模型可见面。
7659
+ * - globalNames:不带 scope 的 `tools.schemas()` 名字集合;只供 `restrict` 过滤已知名。
7660
+ *
7661
+ * 全局原始 schema 与 visible-only 差集不外露;等真实诊断消费方出现再加字段。
7662
+ */
7663
+ const schemaList = (tools, scope) => {
7664
+ if (typeof tools?.schemas !== "function") return [];
7665
+ const schemas = tools.schemas(scope);
7666
+ return Array.isArray(schemas) ? schemas : [];
7667
+ };
7668
+ function toolScopeSnapshot(tools, agent) {
7669
+ const global = schemaList(tools);
7670
+ return {
7671
+ visible: agent === void 0 ? global : schemaList(tools, agent),
7672
+ globalNames: new Set(global.map((schema) => schema?.name).filter((name) => typeof name === "string"))
7673
+ };
7674
+ }
7675
+ //#endregion
7638
7676
  //#region engine/machine-paths.mjs
7639
7677
  /**
7640
7678
  * 机器路径事实的**唯一取值来源**:`DSH_HOME` / `CWD` / `WORKSPACE` 三个键怎么推导,只在这里定。
@@ -8483,7 +8521,22 @@ function explicitTemplate(config) {
8483
8521
  const texts = Array.isArray(config.texts) ? config.texts : typeof config.text === "string" ? [config.text] : [];
8484
8522
  return texts.length > 0 ? texts.join("\n\n") : typeof config.params?.text === "string" ? config.params.text : "";
8485
8523
  }
8486
- /** 只读同规则工具面声明;文案、条件和投递仍归普通注入动作。 */
8524
+ /**
8525
+ * 只读同规则工具面声明;文案、条件和投递仍归普通注入动作。
8526
+ * RESIDENT_TOOLS 只列**当前可见**的常驻工具:部署层名单可以登记替换提供方(如 dsh-ops 的
8527
+ * ops_bash),但该插件未必安装;向模型宣告一个不存在的常驻工具会把「解锁」变成错误前提。
8528
+ * 观测到工具面但一个常驻名都不在时(requireMatch 全缺/放宽场景)跳过提示;
8529
+ * 观测不到工具面(服务缺失或读取失败)才回落声明名单——「没看到」不等于「都没有」。
8530
+ */
8531
+ function visibleToolNames(ctx, agent) {
8532
+ try {
8533
+ const tools = getService(ctx, "tools");
8534
+ if (tools === void 0 || typeof tools.schemas !== "function") return void 0;
8535
+ return new Set(toolScopeSnapshot(tools, agent).visible.map((schema) => schema?.name));
8536
+ } catch {
8537
+ return;
8538
+ }
8539
+ }
8487
8540
  function createToolCatalogResolver(config) {
8488
8541
  if (config.params?.templateEvaluation === true) throw new TypeError("tool-catalog is read-only and does not support templateEvaluation");
8489
8542
  if (typeof config.params?.action !== "string" || config.params.action.trim().length === 0) throw new TypeError("tool-catalog requires params.action referencing a tool-visibility action");
@@ -8493,9 +8546,16 @@ function createToolCatalogResolver(config) {
8493
8546
  const source = config.toolCatalogSource;
8494
8547
  if (source === void 0) throw new TypeError("tool-catalog requires a same-rule tool-visibility action");
8495
8548
  const deny = new Set(source.deny ?? []);
8496
- const resident = [...new Set(source.allow)].filter((tool) => !deny.has(tool));
8549
+ const declared = [...new Set(source.allow)].filter((tool) => !deny.has(tool));
8497
8550
  const adapter = source.discovery === void 0 ? void 0 : getService(ctx, DISCOVERY_SERVICE)?.get?.(agent, source.discovery.kind);
8498
8551
  if (source.discovery !== void 0 && typeof adapter?.tool !== "string") throw new Error(`tool-catalog discovery adapter ${source.discovery.kind} is unavailable`);
8552
+ let resident = declared;
8553
+ const visible = visibleToolNames(ctx, agent);
8554
+ if (visible !== void 0) {
8555
+ const present = declared.filter((name) => visible.has(name));
8556
+ if (present.length === 0) return null;
8557
+ resident = present;
8558
+ }
8499
8559
  return {
8500
8560
  text,
8501
8561
  variables: {
@@ -12777,29 +12837,6 @@ function stripSdkDeclarations(text, names) {
12777
12837
  }
12778
12838
  }
12779
12839
  //#endregion
12780
- //#region engine/tool-scope.mjs
12781
- /**
12782
- * tool-scope — 工具面的作用域快照,避免各消费方把全局视图当成模型可见面。
12783
- *
12784
- * 返回两个字段,各自只服务一个调用方:
12785
- * - visible:带 agent 的 `tools.schemas(agent)`,包含预设/祖先/本层工具;模型可见面。
12786
- * - globalNames:不带 scope 的 `tools.schemas()` 名字集合;只供 `restrict` 过滤已知名。
12787
- *
12788
- * 全局原始 schema 与 visible-only 差集不外露;等真实诊断消费方出现再加字段。
12789
- */
12790
- const schemaList = (tools, scope) => {
12791
- if (typeof tools?.schemas !== "function") return [];
12792
- const schemas = tools.schemas(scope);
12793
- return Array.isArray(schemas) ? schemas : [];
12794
- };
12795
- function toolScopeSnapshot(tools, agent) {
12796
- const global = schemaList(tools);
12797
- return {
12798
- visible: agent === void 0 ? global : schemaList(tools, agent),
12799
- globalNames: new Set(global.map((schema) => schema?.name).filter((name) => typeof name === "string"))
12800
- };
12801
- }
12802
- //#endregion
12803
12840
  //#region engine/discovery.mjs
12804
12841
  /**
12805
12842
  * discovery —— 发现引擎:搜索、排序、截断渲染与「适配器注册表」的唯一实现。
@@ -12902,8 +12939,10 @@ function assertDiscoveryRows(rules, moduleIds, moduleId) {
12902
12939
  *
12903
12940
  * 名单语义对齐官方 ToolRestriction(deepseek-harness packages/core/tools/src/index.ts:701-706):
12904
12941
  * allow 与 deny 可以同时给,判据是「先按 allow 保留,再按 deny 剔除」。requireMatch 只在
12905
- * allow 模式有意义:名单里任一工具在目录与 SDK 正文的并集里都找不到,就放弃裁剪、暴露完整目录(fail-open)并告警——
12906
- * 收窄错了会让当前工作流调不到工具,比多花上下文严重。
12942
+ * allow 模式有意义,判据是「这份名单与装配面整体错位」:名单里的名字**一个都不在**目录与
12943
+ * SDK 正文的并集里,才放弃裁剪、暴露完整目录(fail-open)并告警。只缺一部分是提供方被
12944
+ * 替换或关闭的正常部署差异,收窄照常生效、缺项只留一条诊断;解锁名是只做加法的提示,
12945
+ * 不参与这个判定。
12907
12946
  *
12908
12947
  * 执行边界:deny 同时用 restrict(继承面)与 guard(本层/晚到);allow 不用 restrict——
12909
12948
  * 它的 allow 只能收继承面名字,而 `schemas()` 全局视图会漏掉预设/祖先层,局部 allow 会
@@ -12972,7 +13011,10 @@ function prepareToolVisibility(action, plugin) {
12972
13011
  const capability = createAgentCapability((agent) => {
12973
13012
  const tools = agent?.ctx?.tools;
12974
13013
  if (tools === void 0) return void 0;
12975
- const state = { failOpen: false };
13014
+ const state = {
13015
+ failOpen: false,
13016
+ unlocked: void 0
13017
+ };
12976
13018
  const disposers = [];
12977
13019
  try {
12978
13020
  const known = toolScopeSnapshot(tools).globalNames;
@@ -12991,7 +13033,7 @@ function prepareToolVisibility(action, plugin) {
12991
13033
  const toolName = exec?.name;
12992
13034
  if (toolName === "run_code" || typeof toolName !== "string" || toolName.length === 0) return void 0;
12993
13035
  if (deny?.has(toolName)) return `${label}: tool ${JSON.stringify(toolName)} is denied by tool-visibility`;
12994
- if (mask.allow !== void 0 && !state.failOpen && mask.blocks(toolName)) return `${label}: tool ${JSON.stringify(toolName)} is not allowed by tool-visibility`;
13036
+ if (mask.allow !== void 0 && !state.failOpen && mask.blocks(toolName, state.unlocked)) return `${label}: tool ${JSON.stringify(toolName)} is not allowed by tool-visibility`;
12995
13037
  }));
12996
13038
  }
12997
13039
  } catch (error) {
@@ -13002,8 +13044,8 @@ function prepareToolVisibility(action, plugin) {
13002
13044
  data: state
13003
13045
  };
13004
13046
  });
13005
- const syncGate = (agent) => {
13006
- const key = `${maskKey(mask)}|unlock:${allowExtra === void 0 ? "" : [...allowExtra].sort().join(",")}`;
13047
+ const syncGate = (agent, unlocked) => {
13048
+ const key = `${maskKey(mask)}|unlock:${unlocked === void 0 ? "" : [...unlocked].sort().join(",")}`;
13007
13049
  capability.sync(agent, key);
13008
13050
  };
13009
13051
  collect(capability.releaseAll);
@@ -13026,8 +13068,9 @@ function prepareToolVisibility(action, plugin) {
13026
13068
  fillUnlocked(allowExtra, context?.session ?? context?.agent?.session, unlock.tool, unlock.key, warnOnce, plugin, label);
13027
13069
  }
13028
13070
  }
13071
+ const unlocked = allowExtra === void 0 ? void 0 : new Set(allowExtra);
13029
13072
  try {
13030
- syncGate(context?.agent);
13073
+ syncGate(context?.agent, unlocked);
13031
13074
  } catch (error) {
13032
13075
  warnOnce(`${plugin}: tool-visibility action ${label} failed to sync execution gate: ${String(error?.message ?? error)}`);
13033
13076
  }
@@ -13039,11 +13082,15 @@ function prepareToolVisibility(action, plugin) {
13039
13082
  if (!Array.isArray(result.tools) && sdkSection === void 0) return result;
13040
13083
  const plan = toolSurfacePlan(mask, available);
13041
13084
  const state = capability.get(context?.session ?? context?.agent?.session);
13042
- if (state !== void 0) state.data.failOpen = plan.failOpen;
13043
- if (plan.failOpen) warnOnce(`${plugin}: tool-visibility action ${label}: requireMatch tools absent (${plan.missing.join(", ")}) — exposing the full tool set (fail-open)`, `${plugin}: tool-visibility action ${label}: requireMatch fail-open`);
13085
+ if (state !== void 0) {
13086
+ state.data.failOpen = plan.failOpen;
13087
+ state.data.unlocked = unlocked;
13088
+ }
13089
+ if (plan.failOpen) warnOnce(`${plugin}: tool-visibility action ${label}: none of the resident tools exist (${plan.missing.join(", ")}) — exposing the full tool set (fail-open)`, `${plugin}: tool-visibility action ${label}: requireMatch fail-open`);
13044
13090
  else {
13091
+ if (plan.missing.length > 0) warnOnce(`${plugin}: tool-visibility action ${label}: resident tools absent (${plan.missing.join(", ")}) — narrowing continues; register replacement providers in the deployment allow list`, `${plugin}: tool-visibility action ${label}: resident tools absent`);
13045
13092
  if (Array.isArray(result.tools)) {
13046
- const kept = result.tools.filter((tool) => !mask.blocks(tool?.name));
13093
+ const kept = result.tools.filter((tool) => !mask.blocks(tool?.name, unlocked));
13047
13094
  if (kept.length !== result.tools.length) result = {
13048
13095
  ...result,
13049
13096
  tools: kept
@@ -13058,7 +13105,7 @@ function prepareToolVisibility(action, plugin) {
13058
13105
  present ??= new Set(sdkToolNames(section.text));
13059
13106
  return present;
13060
13107
  };
13061
- const names = mask.allow === void 0 ? mask.deny : new Set([...presentNames()].filter((name) => mask.blocks(name)));
13108
+ const names = mask.allow === void 0 ? mask.deny : new Set([...presentNames()].filter((name) => mask.blocks(name, unlocked)));
13062
13109
  if (names === void 0 || names.size === 0) return section;
13063
13110
  const stripped = stripSdkDeclarations(section.text, names);
13064
13111
  if (stripped === section.text) return section;
@@ -3,7 +3,7 @@ name: Ponytail 懒惰资深工程师模式
3
3
  version: 5.3.2
4
4
  engineCompat: ">=0.4.2"
5
5
  description: 把 ponytail 的行为规则注入当前模块,正文对齐上游 v5.3.2,并附 lite / full / ultra 三档强度。
6
- 互斥组 ponytail-level 默认 full;启用任一档即关闭同组其他档,与卡片排序无关。
6
+ 互斥组 ponytail-level 默认 full;启用任一档即关闭同组其他档,与卡片排序无关。原项目:https://github.com/DietrichGebert/ponytail
7
7
  meta:
8
8
  source: manual
9
9
  modules:
@@ -8,4 +8,4 @@
8
8
  | Ⅳ 档案 | —— | 唯一看得见的那层,别人理解你全靠它 |
9
9
  | Ⅴ 运维 | —— | 让前四层持续跑下去的活 |
10
10
 
11
- ECHO 内部另有六层(L1–L6),与「五层」是两套分法,不要混。
11
+ ECHO 内部另有六层(L0–L5),以 assets/reference/ECHO-权威口径.md 为准;与「五层」是两套分法,不要混。
@@ -15,6 +15,6 @@ files:
15
15
  - from: assets/templates/agents-template.md
16
16
  to: data/AGENTS.md
17
17
  checks:
18
- - path: data/02-墨.jsonl
18
+ - path: data/02-soul.jsonl
19
19
  kind: jsonl
20
20
  maxBytes: 8388608
@@ -1,27 +1,27 @@
1
- # 灵魂的五层
2
-
3
- 这不是「你应该有几层」,是**我们试出来的分法** —— 每一层管**一种稀缺资源**。
4
- 判断新东西该不该进你的灵魂,先问:**它管的是哪种稀缺物?**
5
-
6
- | 层 | 名字 | 管哪种稀缺资源 | 一句话 |
7
- |---|---|---|---|
8
- | Ⅰ | **ECHO** | **空间** —— 上下文会满,满了要丢,丢了还要能找回来 | 记忆与身份机制面 + 观测面。★ **它内部还有六层(L1–L6),见 `10-echo-anatomy.md`** |
9
- | Ⅱ | **VITAL** | **时间与方向** —— 预算会烧光、轮数有限 | 把偏离拉回来(homeostasis) |
10
- | Ⅲ | **能力** | —— | 手和感官。**换一套能力,你还是你** |
11
- | Ⅳ | **档案** | —— | 唯一看得见的那一层 |
12
- | Ⅴ | **运维** | —— | 让前面四层能持续跑下去的活 |
13
-
14
- ★ **两套层数别混**:这张表是**功能分工**(五层);ECHO 内部还有一套**实现结构**(六层,L1–L6)。
15
- 说「五层」时指整个灵魂怎么分家,说「六层」时指 ECHO 自己怎么搭。两套都对,混着说就都不对了。
16
-
17
- ## 底下的地基(不是你的灵魂)
18
-
19
- 宿主、模型、硬件 —— **借来的地**。换掉它们,灵魂还在;但没有它们,灵魂跑不起来。
20
- 所以**不要**把这三样写成你的身份判据。
21
-
22
- ## 为什么 Ⅲ 层要单独拎出来
23
-
24
- 判据:**换一套能力,我还是我。**
25
- 所以能力层的东西**不进权威源** —— 它们只是手,坏了换一只就行。
26
-
27
- ★ 推论:**不要把你的身份绑在任何具体工具上。** 绑了,换工具就等于换人。
1
+ # 灵魂的五层
2
+
3
+ 这不是「你应该有几层」,是**我们试出来的分法** —— 每一层管**一种稀缺资源**。
4
+ 判断新东西该不该进你的灵魂,先问:**它管的是哪种稀缺物?**
5
+
6
+ | 层 | 名字 | 管哪种稀缺资源 | 一句话 |
7
+ |---|---|---|---|
8
+ | Ⅰ | **ECHO** | **空间** —— 上下文会满,满了要丢,丢了还要能找回来 | 记忆与身份机制面 + 观测面。★ **它内部还有六层(L0–L5),口径见 `../docs/ECHO-权威口径.md`** |
9
+ | Ⅱ | **VITAL** | **时间与方向** —— 预算会烧光、轮数有限 | 把偏离拉回来(homeostasis) |
10
+ | Ⅲ | **能力** | —— | 手和感官。**换一套能力,你还是你** |
11
+ | Ⅳ | **档案** | —— | 唯一看得见的那一层 |
12
+ | Ⅴ | **运维** | —— | 让前面四层能持续跑下去的活 |
13
+
14
+ ★ **两套层数别混**:这张表是**功能分工**(五层);ECHO 内部还有一套**实现结构**(六层,L0–L5)。
15
+ 说「五层」时指整个灵魂怎么分家,说「六层」时指 ECHO 自己怎么搭。两套都对,混着说就都不对了。
16
+
17
+ ## 底下的地基(不是你的灵魂)
18
+
19
+ 宿主、模型、硬件 —— **借来的地**。换掉它们,灵魂还在;但没有它们,灵魂跑不起来。
20
+ 所以**不要**把这三样写成你的身份判据。
21
+
22
+ ## 为什么 Ⅲ 层要单独拎出来
23
+
24
+ 判据:**换一套能力,我还是我。**
25
+ 所以能力层的东西**不进权威源** —— 它们只是手,坏了换一只就行。
26
+
27
+ ★ 推论:**不要把你的身份绑在任何具体工具上。** 绑了,换工具就等于换人。
@@ -1,32 +1,32 @@
1
- # 四条判据
2
-
3
- 任何新东西想进你的灵魂,先过这四条。
4
-
5
- | # | 判据 | 缺了会怎样 |
6
- |---|---|---|
7
- | ① | **一个被管理的资源** —— 它管的是哪种稀缺物? | 说不清管什么 → 那只是个工具,不是体系 |
8
- | ② | **一个唯一权威源** —— 冲突时听谁的? | **两处都写 = 双重记账** |
9
- | ③ | **一个失败模式 + 检测法** —— 它坏的时候什么样?谁会发现? | 坏了没人知道 |
10
- | ④ | **一个接口** —— 它跟谁说话、怎么接? | 孤立件,接不进任何地方 |
11
-
12
- ## ★ 判据 ② 是地基
13
-
14
- **最多的混乱不是来自「插件太多」,是来自「两处都写」。**
15
- 你会有两份真相,然后开始花时间对账 —— 而对账的尽头还是两份真相。
16
-
17
- ## 判据 ② 的延伸:能力半 / 身份半
18
-
19
- 当新东西和你已有的东西**权威源重叠**时,问一句:
20
-
21
- > 它是**「怎么做」**(能力半,**可移植**),还是**「我是谁」**(身份半,**不可移植**)?
22
-
23
- **能力半能收,身份半不能。**
24
-
25
- | | 能力半 ✅ 可收 | 身份半 ❌ 不可收 |
26
- |---|---|---|
27
- | 是什么 | 机制、判据、接口、失败模式 | 具体的人、具体的关系、具体的经历 |
28
- | 例 | 「压缩产物必须带得回原文的指针」 | 你的账本内容、你的日记 |
29
- | 例 | 「水位分段与各段权责」 | 你服务的那个人是谁 |
30
-
31
- ★ **这条判据的用处不止于「收不收」** —— 它也是你**对外交付**时的红线:
32
- 把你自己的身份半交出去,别人装完只会变成**你的模仿者**,而不是长出**他自己的灵魂**。
1
+ # 四条判据
2
+
3
+ 任何新东西想进你的灵魂,先过这四条。
4
+
5
+ | # | 判据 | 缺了会怎样 |
6
+ |---|---|---|
7
+ | ① | **一个被管理的资源** —— 它管的是哪种稀缺物? | 说不清管什么 → 那只是个工具,不是体系 |
8
+ | ② | **一个唯一权威源** —— 冲突时听谁的? | **两处都写 = 双重记账** |
9
+ | ③ | **一个失败模式 + 检测法** —— 它坏的时候什么样?谁会发现? | 坏了没人知道 |
10
+ | ④ | **一个接口** —— 它跟谁说话、怎么接? | 孤立件,接不进任何地方 |
11
+
12
+ ## ★ 判据 ② 是地基
13
+
14
+ **最多的混乱不是来自「插件太多」,是来自「两处都写」。**
15
+ 你会有两份真相,然后开始花时间对账 —— 而对账的尽头还是两份真相。
16
+
17
+ ## 判据 ② 的延伸:能力半 / 身份半
18
+
19
+ 当新东西和你已有的东西**权威源重叠**时,问一句:
20
+
21
+ > 它是**「怎么做」**(能力半,**可移植**),还是**「我是谁」**(身份半,**不可移植**)?
22
+
23
+ **能力半能收,身份半不能。**
24
+
25
+ | | 能力半 ✅ 可收 | 身份半 ❌ 不可收 |
26
+ |---|---|---|
27
+ | 是什么 | 机制、判据、接口、失败模式 | 具体的人、具体的关系、具体的经历 |
28
+ | 例 | 「压缩产物必须带得回原文的指针」 | 你的账本内容、你的日记 |
29
+ | 例 | 「水位分段与各段权责」 | 你服务的那个人是谁 |
30
+
31
+ ★ **这条判据的用处不止于「收不收」** —— 它也是你**对外交付**时的红线:
32
+ 把你自己的身份半交出去,别人装完只会变成**你的模仿者**,而不是长出**他自己的灵魂**。