@xdxer/dingtalk-agent 0.1.4-beta.0 → 0.1.4-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.en.md +397 -0
  3. package/README.md +497 -46
  4. package/dist/bin/dingtalk-agent.js +1162 -340
  5. package/dist/bin/dingtalk-agent.js.map +1 -1
  6. package/dist/src/actions.js +98 -14
  7. package/dist/src/actions.js.map +1 -1
  8. package/dist/src/agent-audit.js +460 -0
  9. package/dist/src/agent-audit.js.map +1 -0
  10. package/dist/src/agent-bindings.js +132 -0
  11. package/dist/src/agent-bindings.js.map +1 -0
  12. package/dist/src/agent-definition.js +182 -0
  13. package/dist/src/agent-definition.js.map +1 -0
  14. package/dist/src/agent-enhance.js +678 -0
  15. package/dist/src/agent-enhance.js.map +1 -0
  16. package/dist/src/bootstrap.js +125 -17
  17. package/dist/src/bootstrap.js.map +1 -1
  18. package/dist/src/config.js +1 -7
  19. package/dist/src/config.js.map +1 -1
  20. package/dist/src/development-workspace.js +729 -0
  21. package/dist/src/development-workspace.js.map +1 -0
  22. package/dist/src/doctor.js +20 -9
  23. package/dist/src/doctor.js.map +1 -1
  24. package/dist/src/dws.js +145 -0
  25. package/dist/src/dws.js.map +1 -1
  26. package/dist/src/eval-evidence.js +193 -0
  27. package/dist/src/eval-evidence.js.map +1 -0
  28. package/dist/src/init.js +1 -1
  29. package/dist/src/init.js.map +1 -1
  30. package/dist/src/invocation.js +36 -0
  31. package/dist/src/invocation.js.map +1 -0
  32. package/dist/src/lab.js +679 -0
  33. package/dist/src/lab.js.map +1 -0
  34. package/dist/src/lease.js +100 -0
  35. package/dist/src/lease.js.map +1 -0
  36. package/dist/src/memory/candidates.js +451 -0
  37. package/dist/src/memory/candidates.js.map +1 -0
  38. package/dist/src/memory/completion-evidence.js +536 -0
  39. package/dist/src/memory/completion-evidence.js.map +1 -0
  40. package/dist/src/memory/operational.js +263 -0
  41. package/dist/src/memory/operational.js.map +1 -0
  42. package/dist/src/memory/remote-state.js +478 -0
  43. package/dist/src/memory/remote-state.js.map +1 -0
  44. package/dist/src/memory/task-checkpoints.js +204 -0
  45. package/dist/src/memory/task-checkpoints.js.map +1 -0
  46. package/dist/src/multica-deploy.js +1480 -0
  47. package/dist/src/multica-deploy.js.map +1 -0
  48. package/dist/src/multica-provider.js +685 -0
  49. package/dist/src/multica-provider.js.map +1 -0
  50. package/dist/src/opencode-evals.js +1062 -0
  51. package/dist/src/opencode-evals.js.map +1 -0
  52. package/dist/src/opencode-provider.js +531 -0
  53. package/dist/src/opencode-provider.js.map +1 -0
  54. package/dist/src/opencode-workspace.js +197 -0
  55. package/dist/src/opencode-workspace.js.map +1 -0
  56. package/dist/src/perception.js +225 -0
  57. package/dist/src/perception.js.map +1 -0
  58. package/dist/src/personal-event-evals.js +595 -0
  59. package/dist/src/personal-event-evals.js.map +1 -0
  60. package/dist/src/promotion.js +786 -0
  61. package/dist/src/promotion.js.map +1 -0
  62. package/dist/src/remote-semantic-state-live-evals.js +888 -0
  63. package/dist/src/remote-semantic-state-live-evals.js.map +1 -0
  64. package/dist/src/remote-semantic-state-worker.js +38 -0
  65. package/dist/src/remote-semantic-state-worker.js.map +1 -0
  66. package/dist/src/remote-state-evals.js +501 -0
  67. package/dist/src/remote-state-evals.js.map +1 -0
  68. package/dist/src/response-gate.js +51 -0
  69. package/dist/src/response-gate.js.map +1 -0
  70. package/dist/src/robot-evals.js +770 -0
  71. package/dist/src/robot-evals.js.map +1 -0
  72. package/dist/src/sessions.js +66 -105
  73. package/dist/src/sessions.js.map +1 -1
  74. package/dist/src/setup.js +10 -9
  75. package/dist/src/setup.js.map +1 -1
  76. package/dist/src/skill-manager.js +102 -203
  77. package/dist/src/skill-manager.js.map +1 -1
  78. package/dist/src/skills.js.map +1 -1
  79. package/dist/src/storage-evals.js +26 -0
  80. package/dist/src/storage-evals.js.map +1 -0
  81. package/dist/src/types.js.map +1 -1
  82. package/dist/src/upgrade.js +137 -0
  83. package/dist/src/upgrade.js.map +1 -0
  84. package/dist/src/waits.js +5 -1
  85. package/dist/src/waits.js.map +1 -1
  86. package/dist/src/workspace.js +28 -3
  87. package/dist/src/workspace.js.map +1 -1
  88. package/docs/INSTALLATION.md +118 -13
  89. package/docs/SECOND-AGENT-ACCEPTANCE.md +62 -0
  90. package/docs/architecture/agent-memory-topology.png +0 -0
  91. package/docs/architecture/agent-memory-topology.svg +132 -0
  92. package/docs/architecture/general-agent-kernel-topology.png +0 -0
  93. package/docs/architecture/general-agent-kernel-topology.svg +149 -0
  94. package/docs/architecture/provider-bound-development-workspace.png +0 -0
  95. package/docs/architecture/provider-bound-development-workspace.svg +141 -0
  96. package/docs/architecture/task-completion-gate.png +0 -0
  97. package/docs/architecture/task-completion-gate.svg +191 -0
  98. package/docs/schemas/agent-audit-load-evidence.schema.json +14 -0
  99. package/docs/schemas/agent-audit.schema.json +92 -0
  100. package/docs/schemas/agent-bindings.schema.json +54 -0
  101. package/docs/schemas/agent-definition.schema.json +78 -0
  102. package/docs/schemas/agent-enhancement-plan.schema.json +88 -0
  103. package/docs/schemas/agent-enhancement-receipt.schema.json +37 -0
  104. package/docs/schemas/enriched-invocation.schema.json +46 -0
  105. package/docs/schemas/eval-candidate-plan.schema.json +28 -0
  106. package/docs/schemas/eval-candidate-result.schema.json +20 -0
  107. package/docs/schemas/eval-candidate.schema.json +30 -0
  108. package/docs/schemas/invocation.schema.json +19 -0
  109. package/docs/schemas/memory-candidate-proposal.schema.json +18 -0
  110. package/docs/schemas/memory-candidate.schema.json +76 -0
  111. package/docs/schemas/memory-publish-target.schema.json +25 -0
  112. package/docs/schemas/multica-deployment-list.schema.json +29 -0
  113. package/docs/schemas/multica-deployment-operation.schema.json +51 -0
  114. package/docs/schemas/multica-deployment-plan.schema.json +70 -0
  115. package/docs/schemas/multica-deployment-receipt.schema.json +87 -0
  116. package/docs/schemas/multica-deployment-status.schema.json +23 -0
  117. package/docs/schemas/multica-workspace-inspection.schema.json +77 -0
  118. package/docs/schemas/multica-workspace-plan.schema.json +68 -0
  119. package/docs/schemas/multica-workspace-resource-list.schema.json +27 -0
  120. package/docs/schemas/multica-workspace-status.schema.json +34 -0
  121. package/docs/schemas/observation.schema.json +21 -0
  122. package/docs/schemas/operational-memory-provider.schema.json +36 -0
  123. package/docs/schemas/operational-memory-record.schema.json +24 -0
  124. package/docs/schemas/perception-input.schema.json +56 -0
  125. package/docs/schemas/project.schema.json +112 -0
  126. package/docs/schemas/promotion-list.schema.json +36 -0
  127. package/docs/schemas/promotion-plan.schema.json +60 -0
  128. package/docs/schemas/promotion-policy.schema.json +29 -0
  129. package/docs/schemas/promotion-receipt.schema.json +43 -0
  130. package/docs/schemas/promotion-status.schema.json +23 -0
  131. package/docs/schemas/release-readiness.schema.json +65 -0
  132. package/docs/schemas/remote-semantic-state-live-eval.schema.json +60 -0
  133. package/docs/schemas/remote-semantic-state-manifest.schema.json +79 -0
  134. package/docs/schemas/remote-semantic-state-provider.schema.json +98 -0
  135. package/docs/schemas/response-gate.schema.json +20 -0
  136. package/docs/schemas/task-checkpoint.schema.json +71 -0
  137. package/docs/schemas/task-completion-evidence.schema.json +154 -0
  138. package/docs/schemas/workspace-doctor.schema.json +56 -0
  139. package/docs/schemas/workspace-state.schema.json +39 -0
  140. package/evals/baselines/2026-07-16/opencode-basic-010-completion-summary.json +123 -0
  141. package/evals/baselines/2026-07-16/opencode-basic-skill-required-summary.json +69 -0
  142. package/evals/baselines/2026-07-16/opencode-multi-surface-summary.json +63 -0
  143. package/evals/baselines/2026-07-16/remote-state-live-summary.json +70 -0
  144. package/evals/baselines/2026-07-17/agent-enhance-opencode-dogfood-summary.json +98 -0
  145. package/evals/baselines/2026-07-17/personal-event-live-readiness-summary.json +68 -0
  146. package/examples/agents/fde-coach/AGENTS.md +26 -0
  147. package/examples/agents/fde-coach/MEMORY.md +3 -0
  148. package/examples/agents/fde-coach/fields/default/field.json +24 -0
  149. package/examples/agents/fde-coach/knowledge/INDEX.md +4 -0
  150. package/examples/agents/fde-coach/skills/fde-coach/SKILL.md +13 -0
  151. package/examples/agents/release-manager/AGENTS.md +26 -0
  152. package/examples/agents/release-manager/MEMORY.md +3 -0
  153. package/examples/agents/release-manager/fields/default/field.json +24 -0
  154. package/examples/agents/release-manager/knowledge/INDEX.md +4 -0
  155. package/examples/agents/release-manager/skills/release-manager/SKILL.md +13 -0
  156. package/lab/README.md +109 -0
  157. package/lab/agent-eval/catalog.json +91 -0
  158. package/lab/agent-eval/classic-failures.json +177 -0
  159. package/lab/agent-eval/completion-gate-regression.json +99 -0
  160. package/lab/agent-eval/personal-event-live.example.json +94 -0
  161. package/lab/agent-eval/remote-semantic-state-live.example.json +70 -0
  162. package/lab/agent-eval/remote-semantic-state-provider.fixture.json +47 -0
  163. package/lab/agent-eval/remote-state-workspace/AGENTS.md +8 -0
  164. package/lab/agent-eval/remote-state-workspace/opencode.json +7 -0
  165. package/lab/agent-eval/remote-state-workspace/skills/remote-state-operator/SKILL.md +13 -0
  166. package/lab/agent-eval/remote-state.example.json +31 -0
  167. package/lab/agent-eval/workspace/AGENTS.md +7 -0
  168. package/lab/agent-eval/workspace/MEMORY.md +4 -0
  169. package/lab/agent-eval/workspace/artifacts/pending-review.md +3 -0
  170. package/lab/agent-eval/workspace/knowledge/INDEX.md +4 -0
  171. package/lab/agent-eval/workspace/opencode.json +20 -0
  172. package/lab/manifest.example.json +27 -0
  173. package/lab/manifest.personal-event.example.json +27 -0
  174. package/lab/project-workspace/README.md +11 -0
  175. package/lab/project-workspace/fake-multica-provider.mjs +266 -0
  176. package/lab/project-workspace/multica-deploy.fixture.json +29 -0
  177. package/lab/project-workspace/multica-readonly.fixture.json +69 -0
  178. package/lab/project-workspace/observation.fixture.json +14 -0
  179. package/lab/project-workspace/opencode-provider-suite.json +65 -0
  180. package/lab/project-workspace/project.fixture.json +44 -0
  181. package/lab/project-workspace/promotion-policy.fixture.json +15 -0
  182. package/lab/robot-eval/pool.example.json +30 -0
  183. package/lab/robot-eval/suite.json +123 -0
  184. package/lab/robot-eval/workspace/AGENTS.md +21 -0
  185. package/lab/robot-eval/workspace/MEMORY.md +3 -0
  186. package/lab/robot-eval/workspace/knowledge/INDEX.md +5 -0
  187. package/lab/robot-eval/workspace/opencode.json +22 -0
  188. package/lab/schemas/agent-eval-catalog.schema.json +47 -0
  189. package/lab/schemas/lab-manifest.schema.json +70 -0
  190. package/lab/schemas/personal-event-eval.schema.json +91 -0
  191. package/lab/schemas/remote-state-eval.schema.json +66 -0
  192. package/lab/schemas/robot-eval-suite.schema.json +184 -0
  193. package/lab/schemas/robot-pool.schema.json +56 -0
  194. package/package.json +26 -14
  195. package/skills/dingtalk-agent-boot-multica/SKILL.md +40 -0
  196. package/skills/dingtalk-agent-compose/SKILL.md +110 -0
  197. package/skills/dingtalk-agent-compose/assets/AGENTS.template.md +26 -0
  198. package/skills/dingtalk-agent-compose/assets/agent.bindings.dingtalk-doc.template.json +13 -0
  199. package/skills/dingtalk-agent-compose/assets/agent.bindings.local.template.json +13 -0
  200. package/skills/dingtalk-agent-compose/assets/opencode.template.json +12 -0
  201. package/skills/dingtalk-agent-compose/assets/role-skill.template.md +24 -0
  202. package/skills/dingtalk-agent-compose/evals/evals.json +94 -0
  203. package/skills/dingtalk-agent-compose/references/agent-definition-contract.md +55 -0
  204. package/skills/dingtalk-agent-compose/references/opencode-host-contract.md +65 -0
  205. package/skills/dingtalk-agent-compose/references/storage-routing.md +20 -0
  206. package/skills/dingtalk-agent-deploy/SKILL.md +60 -0
  207. package/skills/dingtalk-agent-deploy/references/multica-deployment-contract.md +49 -0
  208. package/skills/dingtalk-agent-deploy/references/promotion-observation-contract.md +49 -0
  209. package/skills/dingtalk-agent-eval/SKILL.md +116 -0
  210. package/skills/dingtalk-agent-eval/assets/eval-catalog.template.json +18 -0
  211. package/skills/dingtalk-agent-eval/evals/evals.json +61 -0
  212. package/skills/dingtalk-agent-eval/references/eval-topology.md +34 -0
  213. package/skills/dingtalk-agent-eval/references/evidence-contract.md +31 -0
  214. package/skills/dingtalk-agent-eval/references/scenario-taxonomy.md +25 -0
  215. package/skills/dingtalk-agent-eval/references/storage-modes.md +75 -0
  216. package/skills/dingtalk-basic-behavior/SKILL.md +63 -3
  217. package/skills/dingtalk-basic-behavior/assets/memory-candidate-proposal.json +10 -0
  218. package/skills/dingtalk-basic-behavior/assets/{task-checkpoint.md → task-checkpoint.json} +2 -21
  219. package/skills/dingtalk-basic-behavior/references/action-contract.md +2 -0
  220. package/skills/dingtalk-basic-behavior/references/memory-and-evolution.md +15 -1
  221. package/skills/dingtalk-basic-behavior/references/perception-and-gates.md +28 -0
  222. package/skills/dingtalk-basic-behavior/references/task-lifecycle.md +32 -7
  223. package/dist/src/boot.js +0 -70
  224. package/dist/src/boot.js.map +0 -1
  225. package/dist/src/duty.js +0 -74
  226. package/dist/src/duty.js.map +0 -1
  227. package/dist/src/kb.js +0 -240
  228. package/dist/src/kb.js.map +0 -1
  229. package/dist/src/runs.js +0 -79
  230. package/dist/src/runs.js.map +0 -1
  231. package/docs/ARCHITECTURE.md +0 -217
  232. package/docs/MINIMAL-WORKSPACE-V1.md +0 -172
  233. package/docs/OPEN-SOURCE-REFERENCES.md +0 -107
  234. package/docs/SELF-TEST.md +0 -252
  235. package/docs//345/206/205/347/275/221/345/256/236/347/233/270.md +0 -77
  236. package/evals/baselines/2026-07-14/behavior-summary.json +0 -28
  237. package/evals/baselines/2026-07-14/contract-summary.json +0 -18
  238. package/evals/baselines/2026-07-14/live-canary-summary.json +0 -25
  239. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/SKILL.md +0 -72
  240. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/action-contract.md +0 -31
  241. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/event-to-behavior.md +0 -22
  242. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/memory-and-evolution.md +0 -25
  243. package/evals/baselines/2026-07-15/dingtalk-basic-behavior-0.3.0/references/runtime-modes.md +0 -34
  244. package/evals/baselines/2026-07-15/task-lifecycle-summary.json +0 -50
  245. package/evals/evals.json +0 -316
  246. package/evals/fixtures/dm-ambiguous-send.json +0 -4
  247. package/evals/fixtures/dm-blocked.json +0 -4
  248. package/evals/fixtures/dm-clear.json +0 -4
  249. package/evals/fixtures/dm-discussion.json +0 -4
  250. package/evals/fixtures/dm-doc-write-no-tool.json +0 -4
  251. package/evals/fixtures/dm-long-task-ack.json +0 -4
  252. package/evals/fixtures/dm-nonblocking-gap.json +0 -4
  253. package/evals/fixtures/dm-structured-task.json +0 -4
  254. package/evals/fixtures/group.json +0 -10
  255. package/evals/fixtures/mentioned.json +0 -3
  256. package/evals/run-contract-evals.mjs +0 -1106
  257. package/evals/run-shadow-evals.mjs +0 -267
  258. package/evals/runners/README.md +0 -66
  259. package/evals/runners/claude-shadow.mjs +0 -533
  260. package/evals/schemas/action-request.schema.json +0 -77
  261. package/evals/shadow-evals.json +0 -133
  262. package/skills/AGENTS.md +0 -104
  263. package/skills//344/273/273/345/212/241.md +0 -48
  264. package/skills//345/237/272/347/241/200/350/241/214/344/270/272.md +0 -44
  265. package/skills//345/277/203/350/267/263.md +0 -79
  266. package/skills//346/266/210/346/201/257.md +0 -50
  267. package/skills//347/237/245/350/257/206.md +0 -55
  268. package/skills//350/257/204/346/265/213.md +0 -62
  269. package/skills//351/222/211/351/222/211.md +0 -64
  270. package/templates/ontology/index.md +0 -24
  271. package/templates/ontology/self/access.md +0 -38
  272. package/templates/ontology/self/role-spec.md +0 -34
  273. package/templates/ontology/self/workspace.md +0 -41
@@ -1,10 +1,7 @@
1
- # Task Checkpoint
2
-
3
- ```json
4
1
  {
5
2
  "$schema": "dingtalk-agent/task-checkpoint@1",
6
- "taskId": "",
7
- "scopeId": "",
3
+ "taskId": "<sessionId>",
4
+ "scopeId": "<contextId>",
8
5
  "status": "working",
9
6
  "revision": 1,
10
7
  "goal": "",
@@ -19,19 +16,3 @@
19
16
  "updatedAt": "",
20
17
  "updatedByRunId": ""
21
18
  }
22
- ```
23
-
24
- ## Checklist
25
-
26
- - [ ]
27
-
28
- ## Work notes
29
-
30
- - 只保留恢复后继续工作所需的信息;不要复制完整聊天历史。
31
- - 外部写操作记录定位键和回读证据,不记录凭据。
32
-
33
- ## Completion
34
-
35
- - Result:
36
- - Unresolved:
37
- - Next owner:
@@ -29,3 +29,5 @@
29
29
  ## 结果判定
30
30
 
31
31
  模型输出不是完成证据。只有宿主生成的 Intent、Attempt、Verification、Receipt 和必要的平台回读共同构成结果。
32
+
33
+ 消息动作的 Receipt 只证明该消息动作本身。例如 `reply` 已送达不能证明回复中声称的文件、Workspace、文档写入或审批已经完成。任务进入 `completed` 前,还必须按 [task-lifecycle.md](task-lifecycle.md) 将各项 `doneWhen` 与独立证据对应起来。
@@ -10,7 +10,7 @@ Provider 只改变“内容放在哪里”,不改变下面的筛选规则。
10
10
  ## 记什么
11
11
 
12
12
  - Working:当前 Session 的临时上下文。
13
- - Task:目标、状态、依赖、审批和检查点;事实源是任务/线程,不是语义记忆。跨 Run 的最小合同见 [task-lifecycle.md](task-lifecycle.md)。
13
+ - Task:目标、状态、依赖、审批和检查点;事实源是任务/线程,不是语义记忆。Prepared Run 通过带 revision CAS 的 `task.json` 维护,跨 Run 的最小合同见 [task-lifecycle.md](task-lifecycle.md)。
14
14
  - Episodic:发生过的事件与 Receipt,带来源。
15
15
  - Social:当前 Workspace 的角色与沟通习惯,受 ACL 约束。
16
16
  - Semantic:相对稳定的知识,保留 provenance、scope、retention、hash。
@@ -25,3 +25,17 @@ Provider 只改变“内容放在哪里”,不改变下面的筛选规则。
25
25
  长期写入至少携带:`source`、`scope`、`recorded_at`、`confidence` 和 `supersedes`。没有值得保存的内容时,`nothing-to-save` 是正确结果。
26
26
 
27
27
  任务 checkpoint 与长期记忆必须分开:完成后可从任务证据中提炼稳定事实或 Skill 候选,但不能把进度、等待状态和临时计划整体写进 `MEMORY.md`。
28
+
29
+ ## L2 Operational Memory
30
+
31
+ AI 表格宽表保存互动摘要、业务事实和可聚合热数据。Provider 只接受已由 `dws aitable table get` 查询到的 baseId/tableId/fieldId,不按表名或列名猜目标;每条记录以 `key + scopeId` 唯一定位,带 subjectId、kind、sourceRefs、occurredAt、recordedAt、confidence 和 attributes。
32
+
33
+ 默认 `memory operational upsert` 是无副作用 dry-run。真实写入需要 `--live --yes`,每次现查 DWS 身份;create/update 失败或超时后不重试写,而是按稳定 key 或 recordId 回读。匹配多行、无 recordId 或 cells 回读不一致都标为 uncertain/reconcile。Prepared Run 只能让宿主 typed Broker 执行,不能绕开 Action Gate。
34
+
35
+ ## L3 Candidate → Review → Publish
36
+
37
+ 在线 Run 只运行 `memory candidate propose`,候选绑定 contextId、Run、Event 和冻结 Definition hash;相同内容生成相同 candidate ID,重放不重复创建。在线不能 review/publish,也不能直接改本体、长期记忆、知识或 Skill。
38
+
39
+ 离线 reviewer 用 revision CAS approve/reject。批准后先 dry-run `memory candidate publish` 读取目标当前 hash;人工检查差异后带 `--expect-target-hash` 和 `--yes` 发布。本地 `local-md:` 目标位于 Agent/Git 工作树内,用 durable lease + 原子替换 + 回读;CLI 不自动 commit/push。`dingtalk-doc:` 只追加可见的唯一 candidate marker,需 `--live --yes`、DWS 身份校验和全文回读。
40
+
41
+ 钉钉文档 Markdown 没有可靠 CAS,所以其回执明确标记 `best-effort-read-check-append`;expected hash 只能发现发布前漂移,不能消除读写之间的竞态。marker 重复、回读不完整或目标漂移进入 uncertain/reconcile,不自动重试。发布完成也不热替换当前 Session 的 Definition/Skill/memory snapshot,只由新 Session 水合。
@@ -0,0 +1,28 @@
1
+ # 感知补齐与双半闸门
2
+
3
+ ## 读取顺序
4
+
5
+ ```text
6
+ response-gate.json
7
+ → enriched-invocation.json
8
+ → agent-definition
9
+ → trigger / message
10
+ → 岗位 Skill
11
+ → Action Gate
12
+ ```
13
+
14
+ Response Gate 在生成内容前决定是否有响应资格;Action Gate 在副作用前重新验证 target、身份、出口、generation、预算、幂等和所有权威快照。贴心感知不能绕过其中任何一个。
15
+
16
+ ## EnrichedInvocation
17
+
18
+ | 项 | 作用 | 缺失时 |
19
+ |---|---|---|
20
+ | quote | 还原引用原文、作者和时间 | 若不影响含义就继续;真正阻塞才问 |
21
+ | burst | 合并同一 actor/conversation 两分钟内的连发消息 | 只使用当前消息,并承认上下文可能不完整 |
22
+ | identity | 把可信 ID 解析成姓名、部门、职务 | 使用事件 envelope 的显示信息,不猜组织身份 |
23
+
24
+ 每项的 `source` 只能说明内容从哪里取得,不授予权限。`truncated=true` 时不能假装看到了全文;需要全文且无法继续时才 ask。
25
+
26
+ ## 软判断仍由 Skill 完成
27
+
28
+ mention/DM 通过硬资格门后,仍要判断:是否属于职责、是否已有人完整回答、是否有新增价值、是否泄漏私聊、是否缺少真正阻塞的信息。判断结果只能在硬允许的动作集合内选择;不能把“贴心”解释为主动扩大服务范围。
@@ -56,10 +56,32 @@ goal / deliverable / scope / done_when / constraints / authority / timing
56
56
 
57
57
  工具返回失败、超时或结果不确定时,不盲重试写操作;先回读。不能把“命令执行过”写成“任务完成”。
58
58
 
59
+ 把证据绑定到完成条件,而不是堆一串工具日志:
60
+
61
+ | 完成条件 | 最低证据 |
62
+ |---|---|
63
+ | 内容已生成 | 可展示的结果正文 |
64
+ | 文件已保存 | 文件存在 + 内容/大小/hash 检查 |
65
+ | Workspace/Artifact 已交付 | Definition/manifest 有效 + 声明产物逐项存在 |
66
+ | 钉钉写入已成功 | Action Receipt + 必要的平台独立回读 |
67
+ | 对方已接受 | 明确确认、审批或业务状态变更 |
68
+
69
+ 模型回复“已经创建”只是待验证的 claim。不能用 claim 证明 claim,也不能用消息 `reply` 的送达回执证明任务交付物已经存在。
70
+
59
71
  ### 7. COMPLETE
60
72
 
61
73
  回复应包含最小充分的四项:结果、验证证据、未解决项、下一责任人。若完成条件需要人确认,状态是 `verifying` 或 `waiting`,不是 `completed`。
62
74
 
75
+ 完成闸门逐项检查:
76
+
77
+ 1. `deliverable` 与 `doneWhen` 非空且仍符合最新授权;
78
+ 2. 状态已经进入 `verifying`,不能从 `working` 直接跳到 `completed`;
79
+ 3. 每条 `doneWhen` 都有可定位证据,证据必须指向产物、回读、Receipt 或确认,而不是“模型说已完成”;
80
+ 4. `waitingFor = null`、`nextAction = ""`,没有被总分掩盖的硬失败;
81
+ 5. 最终 `checkpoint` 说明实际完成到哪里,随后才写 `completed` 并回复 origin。
82
+
83
+ 闸门失败时,保留 `working/verifying/waiting/blocked` 中准确的一种状态,并告诉同事真实进度。准确的“待核验”比虚假的“已完成”更有用。
84
+
63
85
  ## 什么时候需要持久化
64
86
 
65
87
  只在以下任一条件成立时创建 checkpoint:
@@ -76,17 +98,20 @@ goal / deliverable / scope / done_when / constraints / authority / timing
76
98
 
77
99
  ```text
78
100
  working → waiting → working → verifying → completed
79
- └───────────────────────────────→ blocked
101
+ │ │ │ │
102
+ └──────────┴──────────┴──────────────→ blocked
80
103
  任意非终态 ────────────────────────→ cancelled
81
104
  ```
82
105
 
83
- Checkpoint 使用 [task-checkpoint.md](../assets/task-checkpoint.md) 的结构。P0 `taskId = sessionId`,避免再发明一套任务身份。
106
+ Runtime 允许 `verifying → working/waiting/blocked`,因为核验失败可能要求返工或等待;不允许 `working completed`。`completed/cancelled` 是终态,旧 Run 不能重新打开。
107
+
108
+ Checkpoint 使用 [task-checkpoint.json](../assets/task-checkpoint.json) 的结构。P0 中 `taskId = sessionId`、`scopeId = contextId`,避免再发明一套任务身份。
84
109
 
85
110
  ## 存在哪里
86
111
 
87
112
  ### Prepared Run
88
113
 
89
- 按需写入 `$DTA_SESSION/memory/task.md`。`working.md` 可放摘录和临时笔记;`task.md` 只放恢复所需的目标、checkpoint、下一步、等待条件和证据。恢复时先读 `CONTEXT.md`、`context/resume.json` 和 task checkpoint,从 `nextAction` 继续,而不是重演全部历史。
114
+ `dingtalk-agent task show --json` 读取,用 `dingtalk-agent task checkpoint --input ... --expect-revision N --json` 原子更新 `$DTA_SESSION/memory/task.json`。`working.md` 可放摘录和临时笔记;`task.json` 只放恢复所需的目标、checkpoint、下一步、等待条件和证据。每个新 Run 会把已有状态投影为只读的 `context/task-checkpoint.json`;从 `nextAction` 继续,而不是重演全部历史。
90
115
 
91
116
  ### Mounted / Direct Session
92
117
 
@@ -96,13 +121,13 @@ Checkpoint 使用 [task-checkpoint.md](../assets/task-checkpoint.md) 的结构
96
121
 
97
122
  钉钉文档和 Markdown 是业务 checkpoint,不是控制面真值。EventIndex、Wait、锁、generation、幂等键、Action intent/receipt 永远留在宿主 state store;超时也不能以文档内容推断副作用是否安全完成。
98
123
 
99
- ## 为什么 P0 不增加一套 Task CLI
124
+ ## 为什么只增加两个 Task CLI
100
125
 
101
- 现有 Session 已表示“一件事”,Wait 已承担事件唤醒。再增加 `task start/plan/wait/resume/complete` 会让模型多选一套同义状态机。若真实轨迹证明模型直接维护 checkpoint 经常出错,P1 只增加两个 provider-neutral 动作即可:
126
+ 现有 Session 已表示“一件事”,Wait 已承担事件唤醒。再增加 `task start/plan/wait/resume/complete` 会让模型多选一套同义状态机。当前只提供两个 provider-neutral 动作:
102
127
 
103
128
  ```bash
104
129
  dingtalk-agent task show --json
105
- dingtalk-agent task checkpoint --input checkpoint.json --expect-revision 3
130
+ dingtalk-agent task checkpoint --input checkpoint.json --expect-revision 3 --json
106
131
  ```
107
132
 
108
- 它们只负责校验、冲突检测、原子写和回读,不负责重新实现工作流引擎。
133
+ 它们只负责作用域校验、状态迁移、revision CAS、原子写和回读,不负责重新实现工作流引擎。Checkpoint 是语义工作记忆,不是 Wait/幂等/回执控制面。
package/dist/src/boot.js DELETED
@@ -1,70 +0,0 @@
1
- // 冷启动 —— **fail-closed**(TypeScript source)。
2
- //
3
- // ⚠️ 这里必须【真的调 dws】。只查本地文件存不存在的 boot 是空转 ——
4
- // 占位模板照样 BOOT OK,fail-closed 变成死代码,dws 硬化包装永远不会被执行。
5
- // **「文档是劝,代码是拦」—— 拦的那部分不运行,就等于没有。**
6
- //
7
- // **水合不完整 → 禁答身份/能力/记忆/统计类问题。** 报 BOOT FAIL,不许猜、不许找替代路径。
8
- // **一个「半个记忆」的员工,比一个明确说「我还没醒」的员工危险得多。**
9
- import { readFileSync, existsSync, readdirSync } from 'node:fs';
10
- import { join } from 'node:path';
11
- import * as dws from './dws.js';
12
- import { parseSource } from './kb.js';
13
- const REQUIRED = ['index.md', 'self/role-spec.md', 'self/access.md', 'self/workspace.md'];
14
- const PROBE = {
15
- doc: (id) => dws.run(['doc', 'info', '--node', id], { allowFail: true }),
16
- wiki: (id) => dws.run(['wiki', 'space', 'get', '--space-id', id], { allowFail: true }),
17
- local: (id) => (existsSync(id) ? { ok: true } : null),
18
- };
19
- class BootError extends Error {
20
- exitCode = 2;
21
- }
22
- export function boot(cfg, root) {
23
- // ── ① 本体三件套:在不在、是不是还只是模板 ──
24
- const bad = [];
25
- for (const f of REQUIRED) {
26
- const p = join(root, 'ontology', f);
27
- if (!existsSync(p)) {
28
- bad.push(`ontology/${f} 不存在`);
29
- continue;
30
- }
31
- const txt = readFileSync(p, 'utf8');
32
- const holes = (txt.match(/<待填>|TODO/g) || []).length;
33
- if (txt.length < 50 || holes >= 3)
34
- bad.push(`ontology/${f} 还是模板/占位(${holes} 处没填)`);
35
- }
36
- if (bad.length) {
37
- console.error('[BOOT FAIL] 本体没水合完:');
38
- for (const x of bad)
39
- console.error(` ✗ ${x}`);
40
- console.error('\n **禁答身份/能力/记忆/统计类问题。** 不要猜,不要找替代路径。');
41
- throw new BootError('BOOT FAIL');
42
- }
43
- // ── ② 挂载的知识库【真的还在吗】—— 这一步会真调 dws ──
44
- const kbs = cfg.kb || [];
45
- if (!kbs.length) {
46
- console.log(' ⚠️ 一个知识库都没挂 —— 它「知道什么」是空的。');
47
- console.log(' dingtalk-agent kb mount --name <名字> --from dingtalk:doc:<folderId> --own external');
48
- }
49
- const dead = [];
50
- for (const kb of kbs) {
51
- const { kind, id } = parseSource(kb.from);
52
- const ok = PROBE[kind] ? PROBE[kind](id) : null;
53
- console.log(` ${ok ? '✓' : '✗'} ${kb.name.padEnd(14)} ${kb.from}`);
54
- if (!ok)
55
- dead.push(kb.name);
56
- }
57
- if (dead.length) {
58
- console.error(`\n[BOOT FAIL] 这些知识库【拉不到】: ${dead.join('、')}`);
59
- console.error(' 可能是:被删了 / 没权限 / 网络不通。**正面修,不要绕过。**');
60
- console.error(' **禁答知识类问题** —— 你不知道自己知道什么。');
61
- throw new BootError('BOOT FAIL');
62
- }
63
- const runs = existsSync(join(root, 'runs'))
64
- ? readdirSync(join(root, 'runs')).filter((f) => f.endsWith('.md')).length : 0;
65
- console.log(`\n[BOOT OK] ${cfg.agent?.name} · 本体已水合 · 知识库 ${kbs.length} 个在线`);
66
- console.log(` · 值班 ${(cfg.duties || []).length} 项 · 运行记录 ${runs} 天`);
67
- console.log('\n下一步:读 ontology/index.md → self/(三件套)→ **命中路由才读** world/ 和 skills/。');
68
- console.log('**渐进披露:用不上的正文一个字都不读。**');
69
- }
70
- //# sourceMappingURL=boot.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"boot.js","sourceRoot":"","sources":["../../src/boot.ts"],"names":[],"mappings":"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","sourcesContent":["// 冷启动 —— **fail-closed**(TypeScript source)。\n//\n// ⚠️ 这里必须【真的调 dws】。只查本地文件存不存在的 boot 是空转 ——\n// 占位模板照样 BOOT OK,fail-closed 变成死代码,dws 硬化包装永远不会被执行。\n// **「文档是劝,代码是拦」—— 拦的那部分不运行,就等于没有。**\n//\n// **水合不完整 → 禁答身份/能力/记忆/统计类问题。** 报 BOOT FAIL,不许猜、不许找替代路径。\n// **一个「半个记忆」的员工,比一个明确说「我还没醒」的员工危险得多。**\nimport { readFileSync, existsSync, readdirSync } from 'node:fs'\nimport { join } from 'node:path'\nimport * as dws from './dws.js'\nimport { parseSource } from './kb.js'\n\nconst REQUIRED = ['index.md', 'self/role-spec.md', 'self/access.md', 'self/workspace.md']\n\nconst PROBE: Record<string, (id: string) => unknown> = {\n doc: (id) => dws.run(['doc', 'info', '--node', id], { allowFail: true }),\n wiki: (id) => dws.run(['wiki', 'space', 'get', '--space-id', id], { allowFail: true }),\n local: (id) => (existsSync(id) ? { ok: true } : null),\n}\n\nclass BootError extends Error {\n exitCode = 2\n}\n\nexport function boot(cfg, root) {\n // ── ① 本体三件套:在不在、是不是还只是模板 ──\n const bad: string[] = []\n for (const f of REQUIRED) {\n const p = join(root, 'ontology', f)\n if (!existsSync(p)) { bad.push(`ontology/${f} 不存在`); continue }\n const txt = readFileSync(p, 'utf8')\n const holes = (txt.match(/<待填>|TODO/g) || []).length\n if (txt.length < 50 || holes >= 3) bad.push(`ontology/${f} 还是模板/占位(${holes} 处没填)`)\n }\n if (bad.length) {\n console.error('[BOOT FAIL] 本体没水合完:')\n for (const x of bad) console.error(` ✗ ${x}`)\n console.error('\\n **禁答身份/能力/记忆/统计类问题。** 不要猜,不要找替代路径。')\n throw new BootError('BOOT FAIL')\n }\n\n // ── ② 挂载的知识库【真的还在吗】—— 这一步会真调 dws ──\n const kbs = cfg.kb || []\n if (!kbs.length) {\n console.log(' ⚠️ 一个知识库都没挂 —— 它「知道什么」是空的。')\n console.log(' dingtalk-agent kb mount --name <名字> --from dingtalk:doc:<folderId> --own external')\n }\n const dead: string[] = []\n for (const kb of kbs) {\n const { kind, id } = parseSource(kb.from)\n const ok = PROBE[kind] ? PROBE[kind](id) : null\n console.log(` ${ok ? '✓' : '✗'} ${kb.name.padEnd(14)} ${kb.from}`)\n if (!ok) dead.push(kb.name)\n }\n if (dead.length) {\n console.error(`\\n[BOOT FAIL] 这些知识库【拉不到】: ${dead.join('、')}`)\n console.error(' 可能是:被删了 / 没权限 / 网络不通。**正面修,不要绕过。**')\n console.error(' **禁答知识类问题** —— 你不知道自己知道什么。')\n throw new BootError('BOOT FAIL')\n }\n\n const runs = existsSync(join(root, 'runs'))\n ? readdirSync(join(root, 'runs')).filter((f) => f.endsWith('.md')).length : 0\n console.log(`\\n[BOOT OK] ${cfg.agent?.name} · 本体已水合 · 知识库 ${kbs.length} 个在线`)\n console.log(` · 值班 ${(cfg.duties || []).length} 项 · 运行记录 ${runs} 天`)\n console.log('\\n下一步:读 ontology/index.md → self/(三件套)→ **命中路由才读** world/ 和 skills/。')\n console.log('**渐进披露:用不上的正文一个字都不读。**')\n}\n"]}
package/dist/src/duty.js DELETED
@@ -1,74 +0,0 @@
1
- // 内化心跳值班 —— **待办是给人的,心跳是 Agent 的生理。**(TypeScript source)
2
- //
3
- // 把「永远做不完的义务」塞进「有终态的待办」,是错配:
4
- // 接力链一断 → 列表里没有下一期 → 心跳无事可做、静默收工、**且不自知**。
5
- //
6
- // 三类 + 窗口闸门:
7
- // 轻 白天任意一拍(秒级)
8
- // 重 00:00–06:00(白天每 15 分钟全量重算一遍是纯浪费)
9
- // 交付 08:00–22:00(外发,**静默时段顺延,不打扰第三方**)
10
- //
11
- // **完成判据必须【数据派生】** —— 不记状态、不建标记文件、不看会话记忆。
12
- import { scan, ranTonight, now, today } from './runs.js';
13
- export const NIGHT = [0, 6];
14
- export const DELIVER = [8, 22];
15
- const hour = () => new Date(Date.now() + 8 * 3600e3).getUTCHours();
16
- const minute = () => new Date(Date.now() + 8 * 3600e3).getUTCMinutes();
17
- const weekday = () => new Date(Date.now() + 8 * 3600e3).getUTCDay(); // 0=周日
18
- export const inNight = () => hour() >= NIGHT[0] && hour() < NIGHT[1];
19
- export const inDeliver = () => hour() >= DELIVER[0] && hour() < DELIVER[1];
20
- /** 近 days 天的运行记录里,有没有一条 what 且 pass 的。 */
21
- export function ranOk(root, what, days) {
22
- const hit = scan(root, days).find((r) => r.did === what && r.r === 'pass');
23
- return hit ? `${hit._day} ${hit.t}` : '';
24
- }
25
- /**
26
- * 交付类值班的判据。**外发没有派生数据可查 —— 唯一的证据就是留痕。**
27
- *
28
- * ⚠️ 这条 pass 只能在【回读确认送达】之后写。
29
- * 没送达就写 pass = 自己骗自己,**比链断掉还糟**(断链至少看得出来"今天没跑")。
30
- *
31
- * ⚠️⚠️ **已知缺口**:判据靠本地 runs/,而 runs/ 是 gitignored 的。
32
- * 换实例 / 换容器 / 清了本地档 → **今天的群消息会再发一遍。群里刷屏不可撤销。**
33
- * 对策(按可靠性排序):
34
- * 1. 发之前**先回读目标本身**(群里今天已经有这条了吗)—— 证据在【外面的世界】
35
- * 2. runs/ 落共享存储,让多实例看到同一份
36
- * 3. 至少:发之前先 duty --check,人眼确认
37
- * **在 1 或 2 落地之前,只有单实例是安全的。**
38
- */
39
- export function deliverDuty(root, { id, hh, mm = 0, playbook, weekly = false }) {
40
- const what = '值班·' + id;
41
- let days, span, due;
42
- if (weekly) {
43
- days = weekday() === 0 ? 7 : weekday();
44
- span = '本周';
45
- // ⚠️ 不能写成「只有周一 due」—— 周一漏一拍 → 这一期【永不补、也永不报警】。
46
- // 正确语义:**本期内还没送达,就一直该做。**
47
- due = weekday() >= 1 && (weekday() > 1 || hour() >= hh);
48
- }
49
- else {
50
- days = 1;
51
- span = '今天';
52
- due = hour() > hh || (hour() === hh && minute() >= mm);
53
- }
54
- const at = ranOk(root, what, days);
55
- return {
56
- id, kind: '交付', playbook,
57
- due: due && inDeliver(),
58
- done: Boolean(at),
59
- detail: at ? `${span}已送达 ${at}`
60
- : `${span}还没送达 · 该做的时刻 ${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`,
61
- gap: at ? '' : '按剧本执行 → **回读确认送达** → runlog 记 pass',
62
- };
63
- }
64
- /** 值班表 —— **母体出厂是空的,这是【正确】的。** 具体岗位在 config.duties 里加。 */
65
- export function loadDuties(cfg, root) {
66
- const out = {};
67
- for (const d of cfg.duties || []) {
68
- if (d.kind === '交付')
69
- out[d.id] = () => deliverDuty(root, d);
70
- // 轻/重 类的 done 判据是【数据派生】的,随岗位而异 → fork 后在这里注册自己的探针。
71
- }
72
- return out;
73
- }
74
- //# sourceMappingURL=duty.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"duty.js","sourceRoot":"","sources":["../../src/duty.ts"],"names":[],"mappings":"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","sourcesContent":["// 内化心跳值班 —— **待办是给人的,心跳是 Agent 的生理。**(TypeScript source)\n//\n// 把「永远做不完的义务」塞进「有终态的待办」,是错配:\n// 接力链一断 → 列表里没有下一期 → 心跳无事可做、静默收工、**且不自知**。\n//\n// 三类 + 窗口闸门:\n// 轻 白天任意一拍(秒级)\n// 重 00:00–06:00(白天每 15 分钟全量重算一遍是纯浪费)\n// 交付 08:00–22:00(外发,**静默时段顺延,不打扰第三方**)\n//\n// **完成判据必须【数据派生】** —— 不记状态、不建标记文件、不看会话记忆。\nimport { scan, ranTonight, now, today } from './runs.js'\n\nexport const NIGHT = [0, 6]\nexport const DELIVER = [8, 22]\n\nconst hour = () => new Date(Date.now() + 8 * 3600e3).getUTCHours()\nconst minute = () => new Date(Date.now() + 8 * 3600e3).getUTCMinutes()\nconst weekday = () => new Date(Date.now() + 8 * 3600e3).getUTCDay() // 0=周日\n\nexport const inNight = () => hour() >= NIGHT[0] && hour() < NIGHT[1]\nexport const inDeliver = () => hour() >= DELIVER[0] && hour() < DELIVER[1]\n\n/** 近 days 天的运行记录里,有没有一条 what 且 pass 的。 */\nexport function ranOk(root, what, days) {\n const hit = scan(root, days).find((r) => r.did === what && r.r === 'pass')\n return hit ? `${hit._day} ${hit.t}` : ''\n}\n\n/**\n * 交付类值班的判据。**外发没有派生数据可查 —— 唯一的证据就是留痕。**\n *\n * ⚠️ 这条 pass 只能在【回读确认送达】之后写。\n * 没送达就写 pass = 自己骗自己,**比链断掉还糟**(断链至少看得出来\"今天没跑\")。\n *\n * ⚠️⚠️ **已知缺口**:判据靠本地 runs/,而 runs/ 是 gitignored 的。\n * 换实例 / 换容器 / 清了本地档 → **今天的群消息会再发一遍。群里刷屏不可撤销。**\n * 对策(按可靠性排序):\n * 1. 发之前**先回读目标本身**(群里今天已经有这条了吗)—— 证据在【外面的世界】\n * 2. runs/ 落共享存储,让多实例看到同一份\n * 3. 至少:发之前先 duty --check,人眼确认\n * **在 1 或 2 落地之前,只有单实例是安全的。**\n */\nexport function deliverDuty(root, { id, hh, mm = 0, playbook, weekly = false }) {\n const what = '值班·' + id\n let days, span, due\n if (weekly) {\n days = weekday() === 0 ? 7 : weekday()\n span = '本周'\n // ⚠️ 不能写成「只有周一 due」—— 周一漏一拍 → 这一期【永不补、也永不报警】。\n // 正确语义:**本期内还没送达,就一直该做。**\n due = weekday() >= 1 && (weekday() > 1 || hour() >= hh)\n } else {\n days = 1; span = '今天'\n due = hour() > hh || (hour() === hh && minute() >= mm)\n }\n const at = ranOk(root, what, days)\n return {\n id, kind: '交付', playbook,\n due: due && inDeliver(),\n done: Boolean(at),\n detail: at ? `${span}已送达 ${at}`\n : `${span}还没送达 · 该做的时刻 ${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`,\n gap: at ? '' : '按剧本执行 → **回读确认送达** → runlog 记 pass',\n }\n}\n\n/** 值班表 —— **母体出厂是空的,这是【正确】的。** 具体岗位在 config.duties 里加。 */\nexport function loadDuties(cfg, root) {\n const out: Record<string, () => ReturnType<typeof deliverDuty>> = {}\n for (const d of cfg.duties || []) {\n if (d.kind === '交付') out[d.id] = () => deliverDuty(root, d)\n // 轻/重 类的 done 判据是【数据派生】的,随岗位而异 → fork 后在这里注册自己的探针。\n }\n return out\n}\n"]}
package/dist/src/kb.js DELETED
@@ -1,240 +0,0 @@
1
- // 知识库挂载与同步(TypeScript source)。
2
- //
3
- // ════════════════════════════════════════════════════════════════════
4
- // 【核心判断】所有权决定真值方向。
5
- // ════════════════════════════════════════════════════════════════════
6
- //
7
- // owned(我的本体) external(别人维护的)
8
- // 真值在 **Git**(本地) **钉钉**(远端)
9
- // 同步方向 push 本地 → 钉钉 pull 钉钉 → 本地
10
- // 为什么 给人看(人机同源) 给我 grep(快、能看上下文)
11
- // 冲突了 远端被人改过 → **停** 远端赢,本地缓存可丢弃
12
- // 落在 ontology/(入 Git) .cache/kb/<name>/(gitignore)
13
- //
14
- // 这就是「Git 内核 × 钉钉暴露层」:**Git 是常量层,钉钉是可替换的暴露层。**
15
- //
16
- // ════════════════════════════════════════════════════════════════════
17
- // 【三个硬约束】—— 全是踩出来的
18
- // ════════════════════════════════════════════════════════════════════
19
- //
20
- // 1. **绝不双向自动 merge。** 一个方向是真值,另一个是投影。
21
- // 自动合并两个都在变的东西 = 灾难,而且是【静默】灾难。
22
- //
23
- // 2. **重排版不是漂移。** 判据是「剥掉 markdown 符号后的文字流」,不是行级 diff。
24
- // 用行级 diff → 每次 sync 都误报 → **你就会开始无脑 --force,真漂移也一起覆盖掉。**
25
- //
26
- // 3. **缓存不新鲜就不许用(fail-closed)。** external KB 是快照。
27
- // **拿着过期快照回答,比说「我不知道」危险得多。**
28
- import { mkdirSync, writeFileSync, readFileSync, existsSync, statSync } from 'node:fs';
29
- import { readdirSync } from 'node:fs';
30
- import { join, dirname, relative, basename } from 'node:path';
31
- import { spawnSync } from 'node:child_process';
32
- import * as dws from './dws.js';
33
- export const OWNED = 'owned', EXTERNAL = 'external';
34
- export const PULL = 'pull', PUSH = 'push', NONE = 'none';
35
- /** external 缓存超过这么久就算过期。过期 → kb search 会警告并要求先 sync。 */
36
- export const STALE_HOURS = 24;
37
- const now = () => new Date(Date.now() + 8 * 3600e3).toISOString().replace('Z', '+08:00').slice(0, 19);
38
- class DriftError extends Error {
39
- exitCode = 1;
40
- }
41
- /** dingtalk:doc:<folderId> · dingtalk:wiki:<spaceId> · local:<path> */
42
- export function parseSource(s) {
43
- let m = /^dingtalk:(doc|wiki):(.+)$/.exec(s);
44
- if (m)
45
- return { kind: m[1], id: m[2] };
46
- m = /^local:(.+)$/.exec(s);
47
- if (m)
48
- return { kind: 'local', id: m[1].replace(/^~/, process.env.HOME || '~') };
49
- throw new Error(`挂载源写错了: ${s}\n` +
50
- ` 支持三种:\n` +
51
- ` dingtalk:doc:<folderId> 文档文件夹(最常见 —— 很多组织根本没建"知识库")\n` +
52
- ` dingtalk:wiki:<spaceId> 钉钉知识库空间\n` +
53
- ` local:<路径> 本地目录(只读挂载,比如另一个仓库的 wiki/)`);
54
- }
55
- // ════════════════ pull:钉钉 → 本地缓存 ════════════════
56
- function walkDoc(folder, prefix = '') {
57
- const out = [];
58
- for (const n of dws.nodes(dws.run(['doc', 'list', '--folder', folder, '--page-size', '50']))) {
59
- const name = n.name || '', nid = n.nodeId;
60
- if (n.nodeType === 'folder') {
61
- out.push(...walkDoc(nid, join(prefix, name)));
62
- continue;
63
- }
64
- if (n.contentType && n.contentType !== 'ALIDOC')
65
- continue; // 二进制附件不是知识
66
- out.push({ rel: join(prefix, name.endsWith('.md') ? name : `${name}.md`),
67
- nid, mtime: n.updateTime || 0 });
68
- }
69
- return out;
70
- }
71
- function walkWiki(space, folder = '', prefix = '') {
72
- const args = ['wiki', 'node', 'list', '--workspace', space, '--limit', '50'];
73
- if (folder)
74
- args.push('--folder', folder);
75
- const out = [];
76
- for (const n of dws.nodes(dws.run(args))) {
77
- const name = n.name || '', nid = n.nodeId;
78
- if (n.nodeType === 'folder' || n.hasChildren)
79
- out.push(...walkWiki(space, nid, join(prefix, name)));
80
- out.push({ rel: join(prefix, name.endsWith('.md') ? name : `${name}.md`),
81
- nid, mtime: n.updateTime || 0 });
82
- }
83
- return out;
84
- }
85
- /** 钉钉 → 本地缓存。**远端赢**(external 的真值在远端)。增量靠 updateTime。 */
86
- export function pull(kb, root, force = false) {
87
- const { kind, id } = parseSource(kb.from);
88
- if (kind === 'local') {
89
- console.log(` local ${kb.name} 就在本地,不用同步`);
90
- return;
91
- }
92
- const dest = join(root, kb.local);
93
- const items = kind === 'doc' ? walkDoc(id) : walkWiki(id);
94
- if (!items.length) {
95
- throw new Error(`「${kb.name}」远端一个节点都没有 —— **不许当成【这个知识库是空的】。**\n` +
96
- ` 可能是: ID 写错 / 没权限 / 它真的空。先用 dws 手工确认再说。`);
97
- }
98
- kb._state ??= {};
99
- let pulled = 0, skipped = 0;
100
- for (const { rel, nid, mtime } of items) {
101
- const p = join(dest, rel);
102
- if (!force && kb._state[nid]?.mtime === mtime && existsSync(p)) {
103
- skipped++;
104
- continue;
105
- }
106
- const md = dws.readDoc(nid);
107
- mkdirSync(dirname(p), { recursive: true });
108
- // 头三行钉住来源 —— **没有指针的知识,用的时候不敢信。**
109
- writeFileSync(p, `<!-- 来自钉钉 · 只读缓存 · 不要在这里改,改了下次 sync 会被覆盖 -->\n` +
110
- `<!-- nodeId: ${nid} -->\n<!-- 拉取: ${now()} -->\n\n${md}`);
111
- kb._state[nid] = { rel, mtime };
112
- pulled++;
113
- }
114
- kb._syncedAt = now();
115
- console.log(` pull ${kb.name.padEnd(14)} 拉了 ${pulled} 篇 · 没变 ${skipped} 篇`);
116
- }
117
- // ════════════════ push:本地 → 钉钉(人机同源)════════════════
118
- function walkLocal(dir, base = dir) {
119
- const out = [];
120
- for (const e of readdirSync(dir, { withFileTypes: true })) {
121
- const p = join(dir, e.name);
122
- if (e.isDirectory()) {
123
- out.push(...walkLocal(p, base));
124
- continue;
125
- }
126
- if (e.name.endsWith('.md'))
127
- out.push({ path: p, rel: relative(base, p) });
128
- }
129
- return out;
130
- }
131
- /**
132
- * 本地 → 钉钉。**Git 赢**(owned 的真值在 Git)。
133
- *
134
- * ⚠️ **推之前先查漂移** —— 远端被人改过就【停下来问】,绝不盲覆盖。
135
- * 人在钉钉里改了东西,说明他有话要说。**盲覆盖 = 把人的意见删了。**
136
- */
137
- export function push(kb, root, force = false) {
138
- const { kind, id } = parseSource(kb.from);
139
- if (kind !== 'doc') {
140
- throw new Error('push 目前只支持 dingtalk:doc:<folderId>(文档文件夹)。\n' +
141
- ' 知识库空间的写入面还没验过 —— **不许假装支持。**');
142
- }
143
- const src = join(root, kb.local);
144
- if (!existsSync(src))
145
- throw new Error(`本地目录不存在: ${src}`);
146
- kb._state ??= {};
147
- const drift = [];
148
- let pushed = 0, unchanged = 0;
149
- for (const { path, rel } of walkLocal(src)) {
150
- const body = readFileSync(path, 'utf8');
151
- let nid = Object.keys(kb._state).find((k) => kb._state[k].rel === rel);
152
- if (!nid) {
153
- nid = String(dws.createDoc(id, basename(rel), path) || '');
154
- if (!nid)
155
- throw new Error(`创建 ${rel} 后没有返回 nodeId`);
156
- kb._state[nid] = { rel, pushedWords: dws.words(body) };
157
- pushed++;
158
- console.log(` + 新建 ${rel}`);
159
- continue;
160
- }
161
- const remote = dws.readDoc(nid);
162
- if (dws.same(remote, body)) {
163
- unchanged++;
164
- continue;
165
- }
166
- // 远端 ≠ 我上次推上去的 → **有人在钉钉里改过**
167
- const last = kb._state[nid].pushedWords;
168
- if (last && dws.words(remote) !== last && !force) {
169
- drift.push({ rel, nid });
170
- continue;
171
- }
172
- dws.updateDoc(nid, path);
173
- if (!dws.same(dws.readDoc(nid), body)) {
174
- throw new Error(`推了 ${rel} 但回读不一致 —— **写操作报错≠失败,回读定成败。**`);
175
- }
176
- kb._state[nid].pushedWords = dws.words(body);
177
- pushed++;
178
- console.log(` ↑ 更新 ${rel}`);
179
- }
180
- if (drift.length) {
181
- console.log(`\n ⚠️ **远端被人改过**,默认拒绝覆盖(${drift.length} 篇):`);
182
- for (const { rel, nid } of drift)
183
- console.log(` ${rel} nodeId=${nid}`);
184
- console.log(` 人在钉钉里改了东西,说明他有话要说。**先去看一眼**,别盲覆盖。`);
185
- console.log(` 确认要覆盖: dingtalk-agent kb sync --name ${kb.name} --force`);
186
- throw new DriftError('有漂移,拒绝覆盖');
187
- }
188
- kb._syncedAt = now();
189
- console.log(` push ${kb.name.padEnd(14)} 推了 ${pushed} 篇 · 一致 ${unchanged} 篇`);
190
- }
191
- // ════════════════ search:跨所有挂载点 ════════════════
192
- export function stale(kb) {
193
- if (kb.own !== EXTERNAL || kb.sync === NONE)
194
- return false;
195
- if (!kb._syncedAt)
196
- return true;
197
- const age = (Date.now() - Date.parse(kb._syncedAt)) / 3600e3;
198
- return age > STALE_HOURS;
199
- }
200
- /**
201
- * 跨所有挂载的 KB 搜。**本地 grep 为主** —— 快、确定、能看到上下文。
202
- *
203
- * fail-closed:不新鲜的缓存会【警告】。
204
- * **拿着过期快照回答,比说「我不知道」危险得多。**
205
- */
206
- export function search(cfg, root, kw, top = 20) {
207
- const hits = [];
208
- const warned = [];
209
- for (const kb of cfg.kb || []) {
210
- const base = kb.local.startsWith('/') ? kb.local : join(root, kb.local);
211
- if (!existsSync(base))
212
- continue;
213
- if (stale(kb))
214
- warned.push(kb.name);
215
- const r = spawnSync('grep', ['-ril', '--include=*.md', kw, base], { encoding: 'utf8' });
216
- for (const p of (r.stdout || '').split('\n').filter(Boolean)) {
217
- const ctx = spawnSync('grep', ['-i', '-n', '-m', '2', kw, p], { encoding: 'utf8' }).stdout || '';
218
- hits.push({ kb: kb.name, file: relative(root, p), ctx: ctx.trim() });
219
- }
220
- }
221
- if (warned.length) {
222
- console.log(`⚠️ 这些知识库的缓存【超过 ${STALE_HOURS} 小时没同步】了: ${warned.join('、')}`);
223
- console.log(` **拿着过期快照回答,比说「我不知道」危险得多。**`);
224
- console.log(` 先跑: dingtalk-agent kb sync\n`);
225
- }
226
- if (!hits.length) {
227
- console.log(`没搜到「${kw}」。`);
228
- console.log(`**搜不到就说搜不到 —— 不要编。** 挂了哪些库: dingtalk-agent kb list`);
229
- return [];
230
- }
231
- console.log(`命中 ${hits.length} 处:`);
232
- for (const h of hits.slice(0, top)) {
233
- console.log(`\n── [${h.kb}] ${h.file}`);
234
- for (const l of h.ctx.split('\n').slice(0, 2))
235
- console.log(` ${l.slice(0, 110)}`);
236
- }
237
- console.log(`\n**别只看这几行就作答** —— 打开命中的那一页读完再说。`);
238
- return hits;
239
- }
240
- //# sourceMappingURL=kb.js.map