@xdxer/dingtalk-agent 0.1.4 → 0.1.5-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 (186) hide show
  1. package/CHANGELOG.md +232 -0
  2. package/README.en.md +115 -89
  3. package/README.md +111 -86
  4. package/dist/bin/dingtalk-agent.js +742 -152
  5. package/dist/bin/dingtalk-agent.js.map +1 -1
  6. package/dist/src/actions.js +3 -2
  7. package/dist/src/actions.js.map +1 -1
  8. package/dist/src/agent-audit.js +202 -85
  9. package/dist/src/agent-audit.js.map +1 -1
  10. package/dist/src/agent-definition.js +7 -3
  11. package/dist/src/agent-definition.js.map +1 -1
  12. package/dist/src/agent-enhance.js +51 -32
  13. package/dist/src/agent-enhance.js.map +1 -1
  14. package/dist/src/agent-platform.js +4 -4
  15. package/dist/src/agent-platform.js.map +1 -1
  16. package/dist/src/bootstrap.js +6 -2
  17. package/dist/src/bootstrap.js.map +1 -1
  18. package/dist/src/development-workspace.js +210 -34
  19. package/dist/src/development-workspace.js.map +1 -1
  20. package/dist/src/doctor.js +65 -9
  21. package/dist/src/doctor.js.map +1 -1
  22. package/dist/src/dws.js +67 -3
  23. package/dist/src/dws.js.map +1 -1
  24. package/dist/src/init.js +2 -1
  25. package/dist/src/init.js.map +1 -1
  26. package/dist/src/memory/noop-receipt.js +306 -0
  27. package/dist/src/memory/noop-receipt.js.map +1 -0
  28. package/dist/src/memory/operational.js +27 -3
  29. package/dist/src/memory/operational.js.map +1 -1
  30. package/dist/src/memory/remote-state.js +2 -1
  31. package/dist/src/memory/remote-state.js.map +1 -1
  32. package/dist/src/multica-deploy.js +692 -125
  33. package/dist/src/multica-deploy.js.map +1 -1
  34. package/dist/src/multica-provider.js +303 -25
  35. package/dist/src/multica-provider.js.map +1 -1
  36. package/dist/src/multica-runtime-vocabulary.js +110 -0
  37. package/dist/src/multica-runtime-vocabulary.js.map +1 -0
  38. package/dist/src/opencode-evals.js +6 -6
  39. package/dist/src/opencode-evals.js.map +1 -1
  40. package/dist/src/opencode-provider.js +21 -7
  41. package/dist/src/opencode-provider.js.map +1 -1
  42. package/dist/src/opencode-workspace.js +3 -3
  43. package/dist/src/opencode-workspace.js.map +1 -1
  44. package/dist/src/personal-event-evals.js +4 -2
  45. package/dist/src/personal-event-evals.js.map +1 -1
  46. package/dist/src/promotion.js +2 -1
  47. package/dist/src/promotion.js.map +1 -1
  48. package/dist/src/remote-semantic-state-live-evals.js +14 -8
  49. package/dist/src/remote-semantic-state-live-evals.js.map +1 -1
  50. package/dist/src/remote-state-evals.js +2 -2
  51. package/dist/src/remote-state-evals.js.map +1 -1
  52. package/dist/src/robot-evals.js +3 -3
  53. package/dist/src/robot-evals.js.map +1 -1
  54. package/dist/src/schedule-plan.js +380 -0
  55. package/dist/src/schedule-plan.js.map +1 -0
  56. package/dist/src/sessions.js +1 -1
  57. package/dist/src/sessions.js.map +1 -1
  58. package/dist/src/skill-manager.js +145 -13
  59. package/dist/src/skill-manager.js.map +1 -1
  60. package/dist/src/skills.js +2 -0
  61. package/dist/src/skills.js.map +1 -1
  62. package/dist/src/tui.js +369 -0
  63. package/dist/src/tui.js.map +1 -0
  64. package/dist/src/upgrade.js +113 -33
  65. package/dist/src/upgrade.js.map +1 -1
  66. package/dist/src/waits.js +2 -1
  67. package/dist/src/waits.js.map +1 -1
  68. package/dist/src/workspace.js +12 -7
  69. package/dist/src/workspace.js.map +1 -1
  70. package/docs/AGENT-IN-PRODUCTION.md +255 -0
  71. package/docs/ARCHITECTURE.md +366 -0
  72. package/docs/INSTALLATION.md +8 -8
  73. package/docs/PLATFORM-GUARDRAILS.md +188 -0
  74. package/docs/PRIOR-ART.md +126 -0
  75. package/docs/SELF-TEST.md +182 -0
  76. package/docs/architecture/agent-platform-connection-layer.svg +120 -0
  77. package/docs/architecture/digital-employee-composition.svg +92 -0
  78. package/docs/architecture/dingtalk-agent-architecture.svg +125 -0
  79. package/docs/assets/digital-employee-at-work.svg +77 -0
  80. package/docs/schemas/multica-deployment-plan.schema.json +3 -1
  81. package/docs/schemas/multica-deployment-receipt.schema.json +17 -3
  82. package/docs/schemas/multica-deployment-status.schema.json +6 -2
  83. package/docs/schemas/multica-workspace-inspection.schema.json +16 -0
  84. package/docs/schemas/multica-workspace-run-plan.schema.json +31 -0
  85. package/docs/schemas/multica-workspace-run.schema.json +161 -0
  86. package/docs/schemas/multica-workspace-status.schema.json +2 -0
  87. package/docs/schemas/project.schema.json +54 -3
  88. package/docs/schemas/workspace-scaffold.schema.json +38 -0
  89. package/examples/agents/README.md +45 -0
  90. package/examples/agents/fde-coach/AGENTS.md +2 -25
  91. package/examples/agents/fde-coach/agent/AGENTS.md +35 -0
  92. package/examples/agents/fde-coach/agent.bindings.json +10 -0
  93. package/examples/agents/release-manager/AGENTS.md +2 -25
  94. package/examples/agents/release-manager/agent/AGENTS.md +35 -0
  95. package/examples/agents/release-manager/agent.bindings.json +10 -0
  96. package/lab/agent-eval/catalog.json +5 -5
  97. package/lab/agent-eval/classic-failures.json +4 -4
  98. package/lab/agent-eval/completion-gate-regression.json +9 -9
  99. package/lab/agent-eval/personal-event-live.example.json +3 -3
  100. package/lab/agent-eval/remote-semantic-state-live.example.json +1 -1
  101. package/lab/agent-eval/workspace/opencode.json +2 -2
  102. package/lab/project-workspace/fake-multica-provider.mjs +171 -17
  103. package/lab/project-workspace/multica-deploy.fixture.json +2 -2
  104. package/lab/project-workspace/multica-readonly.fixture.json +4 -16
  105. package/lab/project-workspace/opencode-provider-suite.json +3 -3
  106. package/lab/project-workspace/project.fixture.json +2 -6
  107. package/lab/robot-eval/suite.json +1 -1
  108. package/lab/robot-eval/workspace/AGENTS.md +1 -1
  109. package/lab/robot-eval/workspace/opencode.json +2 -2
  110. package/lab/schemas/personal-event-eval.schema.json +1 -1
  111. package/package.json +18 -11
  112. package/skills/README.md +10 -8
  113. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/SKILL.md +49 -24
  114. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/AGENTS.template.md +1 -1
  115. package/skills/core/dta-agent-compose/assets/REPOSITORY.template.md +10 -0
  116. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/agent.bindings.dingtalk-doc.template.json +2 -2
  117. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/agent.bindings.local.template.json +2 -2
  118. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/hosts/opencode/opencode.template.json +3 -2
  119. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/evals/evals.json +4 -4
  120. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/agent-definition-contract.md +7 -7
  121. package/skills/core/dta-agent-compose/references/drive-and-schedules.md +166 -0
  122. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/host-loading-contract.md +11 -13
  123. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/hosts/claude-code.md +13 -12
  124. package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/hosts/opencode.md +14 -13
  125. package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/SKILL.md +28 -3
  126. package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/assets/eval-catalog.template.json +1 -1
  127. package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/evals/evals.json +1 -1
  128. package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/eval-topology.md +3 -3
  129. package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/interactive-debug-channels.md +10 -4
  130. package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/SKILL.md +21 -5
  131. package/skills/core/dta-basic-behavior/references/event-to-behavior.md +38 -0
  132. package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/memory-and-evolution.md +3 -1
  133. package/skills/core/dta-basic-behavior/references/perception-and-gates.md +87 -0
  134. package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/risk-authority-and-privacy.md +12 -0
  135. package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/truth-and-recovery.md +4 -2
  136. package/skills/core/dta-people-group-memory/COMPLETENESS.md +36 -0
  137. package/skills/core/dta-people-group-memory/SKILL.md +69 -0
  138. package/skills/core/dta-people-group-memory/references/adapters.md +273 -0
  139. package/skills/core/dta-people-group-memory/references/assembly-guidance.md +40 -0
  140. package/skills/core/dta-people-group-memory/references/binding.md +110 -0
  141. package/skills/core/dta-people-group-memory/references/cold-start.md +70 -0
  142. package/skills/core/dta-people-group-memory/references/config-binding.md +89 -0
  143. package/skills/core/dta-people-group-memory/references/consent-and-visibility.md +83 -0
  144. package/skills/core/dta-people-group-memory/references/consolidation.md +162 -0
  145. package/skills/core/dta-people-group-memory/references/event-ingest.md +103 -0
  146. package/skills/core/dta-people-group-memory/references/guided-setup.md +70 -0
  147. package/skills/core/dta-people-group-memory/references/model.md +148 -0
  148. package/skills/core/dta-people-group-memory/references/storage-port.md +107 -0
  149. package/skills/platforms/deap/PLATFORM.md +30 -1
  150. package/skills/platforms/multica-dingtalk/PLATFORM.md +35 -9
  151. package/skills/platforms/multica-dingtalk/{dingtalk-agent-deploy-multica → dta-deploy-multica}/SKILL.md +10 -8
  152. package/skills/platforms/multica-dingtalk/dta-deploy-multica/references/multica-deployment-contract.md +67 -0
  153. package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/SKILL.md +81 -11
  154. package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/scripts/bootstrap.sh +2 -2
  155. package/skills/platforms/multica-dingtalk/{multica-external → dta-ops-multica}/scripts/multica_ext.py +264 -16
  156. package/dist/src/map.js +0 -157
  157. package/dist/src/map.js.map +0 -1
  158. package/docs/SECOND-AGENT-ACCEPTANCE.md +0 -62
  159. package/docs/architecture/agent-memory-topology.png +0 -0
  160. package/docs/architecture/agent-memory-topology.svg +0 -132
  161. package/docs/architecture/dingtalk-agent-blueprint.png +0 -0
  162. package/docs/architecture/durable-async-agent-runtime.png +0 -0
  163. package/docs/architecture/general-agent-kernel-topology.png +0 -0
  164. package/docs/architecture/provider-bound-development-workspace.png +0 -0
  165. package/docs/architecture/task-completion-gate.png +0 -0
  166. package/docs/assets/agent-delivery-lifecycle.svg +0 -103
  167. package/skills/core/dingtalk-basic-behavior/references/event-to-behavior.md +0 -24
  168. package/skills/core/dingtalk-basic-behavior/references/perception-and-gates.md +0 -28
  169. package/skills/platforms/deap/README.md +0 -3
  170. package/skills/platforms/multica-dingtalk/dingtalk-agent-boot-multica/SKILL.md +0 -40
  171. package/skills/platforms/multica-dingtalk/dingtalk-agent-deploy-multica/references/multica-deployment-contract.md +0 -49
  172. /package/examples/agents/fde-coach/{skills → agent/skills}/fde-coach/SKILL.md +0 -0
  173. /package/examples/agents/release-manager/{skills → agent/skills}/release-manager/SKILL.md +0 -0
  174. /package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/assets/role-skill.template.md +0 -0
  175. /package/skills/core/{dingtalk-agent-compose → dta-agent-compose}/references/storage-routing.md +0 -0
  176. /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/evidence-contract.md +0 -0
  177. /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/failure-to-case.md +0 -0
  178. /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/local-connector-smoke.md +0 -0
  179. /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/scenario-taxonomy.md +0 -0
  180. /package/skills/core/{dingtalk-agent-eval → dta-agent-eval}/references/storage-modes.md +0 -0
  181. /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/assets/memory-candidate-proposal.json +0 -0
  182. /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/assets/task-checkpoint.json +0 -0
  183. /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/action-contract.md +0 -0
  184. /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/runtime-modes.md +0 -0
  185. /package/skills/core/{dingtalk-basic-behavior → dta-basic-behavior}/references/task-lifecycle.md +0 -0
  186. /package/skills/platforms/multica-dingtalk/{dingtalk-agent-deploy-multica → dta-deploy-multica}/references/promotion-observation-contract.md +0 -0
@@ -0,0 +1,107 @@
1
+ # 存储端口契约
2
+
3
+ 本篇定义 Fact 怎么落地。**模型层(`model.md`)与事件层(`event-ingest.md`)只对本端口编程,不认识钉钉。** 介质特有的坑全部收在 `adapters.md`。
4
+
5
+ **解耦检验**:把某适配器的能力位调成最宽松档后,通用算法里的完整性校验、写后回读、判重读、翻页判存在四条分支应**自动全灭,Skill 正文一字不改**。
6
+
7
+ ## 零、先用端口形状消解,剩下的才用能力位描述
8
+
9
+ 三条病理不进能力位,靠端口形状让它**说不出来**:
10
+
11
+ - **没有 delete / update 操作** → `raw` 只增不删由形状强制,遗忘只能 append tombstone。
12
+ - **水位是 meta 键空间的一等对象,不是任何页里的一行** → 「水位必须放短页」这件事无从发生。
13
+ - **模型层词汇里没有页、分区、seg、月份** → 切的是 Fact,不是页;物理分区如何滚动是适配器私事。
14
+
15
+ **适配器私有地址空间**:适配器可在自己的地址空间保存分区表、哨兵、物理 ID。模型层不可读不可写;端口签名里**不得出现任何介质地址类型**(nodeId / URL / 路径),它们只出现在 `resolve` 返回的不透明句柄与绑定档案里。
16
+
17
+ 🔴 **用户可见链接的锚点必须是 `(subjectKey, section, evidenceId)` 三元**,永不含物理分区名——否则换介质即死链。
18
+
19
+ ## 一、操作集(10 个)
20
+
21
+ | 操作 | 语义 / 前置 | 返回与失败模式 |
22
+ |---|---|---|
23
+ | `resolve(agentIdentity)` | Boot 期一次解析两个 root 与能力位;同 Session 缓存 | `{roots[], capabilities}`;`NOT_CONFIGURED`(确证未建,可引导)/`UNAVAILABLE`(读失败,**禁止折成 NOT_CONFIGURED**)/`DRIFT(level, observed, expected)`(只报双边真实值,不自愈)。两 root 各自独立 |
24
+ | `probeWrite(root)` | **显式、昂贵、绑定期一次** | `writable` / `read-only` / `write-unverified`。结果写进绑定档案,运行期不重复探测 |
25
+ | `ensureEntity(root, subjectKey, displayHint)` | 唯一的建档动作。**禁止在 ingest/消息触发路径调用** | `exists` / `created` / `ambiguous`(同键多实例=残片)→ `ambiguous` 一律 HALT,不自愈、不猜正本 |
26
+ | `listEntities(root, cursor)` | 判「不存在」的唯一权威来源 | `{keys[], cursor, complete}`;`complete=false` 的结果**只能用于发现,不能用于否定** |
27
+ | `appendFacts(subject, section, facts[])` | 只增追加;facts 自带 `evidenceId` | `receipt{accepted[], skipped[]}`;`PARTIAL` / `UNVERIFIED` / `TOO_LARGE` / `READ_ONLY` |
28
+ | `readFacts(subject, section, range)` | 跨物理分区连续读 | `{facts[], cursor, complete}`;`complete=false` **等同读失败**,不是空 |
29
+ | `putView(subject, section, content, {expectVersion?})` | 整体重写;端口按 section 的 `layer` **拒绝对 `raw` 调用** | `receipt{version}`;`STALE` / `TOO_LARGE`(降级为摘要视图,溢出内容降为 Fact) |
30
+ | `readView(subject, section)` | 召回主路径 | `{content, version, complete}`;`NOT_FOUND` 是**合法初态**,必须与 `UNAVAILABLE` 严格可区分 |
31
+ | `metaGet / metaPut(scope, key, value, {expectVersion?})` | A0 键空间:水位、writeState、绑定档案、停用账本、tombstone 索引、欠账指针 | `{value, version}` / `NOT_FOUND` / `CONFLICT`。🔴 **水位键固定为 `watermark:(subjectKey, channel)`,与 section 正交** |
32
+ | `confirm(receipt)` | 通用算法**无条件调用**;`write.confirm=implicit` 时 O(1) 返回、零往返 | `confirmed` / `unconfirmed` / `failed`。未 confirmed 不得声称已记 |
33
+
34
+ 🔴 **水位键必须是 `(subject, channel)`,绝不能是 `(subject, section)`。** 一个 section 可能同时吃多个渠道的事件(`activity` 同时吃待办与文档),两渠道追平进度天然不同;用 section 当键会让追平待办后推进的水位把文档的未消化区间一起判成已消化,**那批事件永远不会被再消化**——正好踩中「水位虚高不可逆」。
35
+
36
+ ## 二、能力位(8 位 + 容量参数)
37
+
38
+ **准入规则(硬性)**:新增一位必须能**同时改变 ≥2 个适配器的行为**,否则它是适配器内部细节,不许上浮。**位数上限 8。**
39
+
40
+ | 位 | 取值 | 通用算法据此改变什么 |
41
+ |---|---|---|
42
+ | `read.completeness` | `exact` / `verified-partial` | `verified-partial` 时 `complete=false` 按读失败处理:记欠账、跳过本轮。**完整性如何自证全在适配器内部,模型层不知道「截断」这个词**。🔴 **静默截断介质(doc read 不报错)上,唯一可靠的自证是 count 对账**:写侧在 meta 记 `count:(subject,section,partition)`,读回条数 ≠ 记录条数即 `complete=false`。哨兵尾行会被静默截断吃掉,不可靠。详见 `consolidation.md` §0 |
43
+ | `limits.maxFactBytes` / `maxViewBytes` | 整数 / ∞ | 超限时**切 Fact**(同 `evidenceId` 拆多条)或把 View 降级为摘要 |
44
+ | `write.confirm` | `implicit` / `required` | `required` 时每次写后真回读 |
45
+ | `write.cas` | `none` / `revision` | `none`:append → confirm → metaPut 三步,任一失败整批作废、水位不动。`revision`:两步原子提交,`CONFLICT` 靠 `evidenceId` 幂等重放 |
46
+ | `dedupe.enforcement` | `server` / `client` | `client` 时重放前必须 `readFacts` 尾窗判重,且**要求 `complete=true` 才敢重放**;否则不写、记欠账、水位不动 |
47
+ | `identity.collision` | `reject` / `silent-rename` | `silent-rename` 时 `ensureEntity` 内部回读核对,检出残片返回 `ambiguous` |
48
+ | `lookup.negative` | `immediate` / `list-only` | `list-only` 时任何搜索的 0 结果**不构成否定证据**;否定结论只能来自 `listEntities(complete=true)` |
49
+ | `access.scope` | `container` / `leaf` | `leaf` 时只接受叶子锚点 seed;容器枚举缺席一律映射为 `UNAVAILABLE`,**禁止**推出 `NOT_CONFIGURED` |
50
+ | `access.probe` | `declarative` / `empirical` | `empirical` 时 `resolve` 不承诺写权,须显式 `probeWrite` 一次并缓存 |
51
+
52
+ ## 三、病理逐条映射
53
+
54
+ | 病理 | 归宿 | 通用算法的行为 |
55
+ |---|---|---|
56
+ | 原始层按月分页 | `read.completeness` + `limits.*` | 只调 `appendFacts/readFacts(range)`。「按月」降为适配器把一个 section 映射到多个物理对象的**内部分区策略**;本地文件上就是一个文件 |
57
+ | 水位必须放短页 | **端口形状消解** | `metaGet/Put` 独立寻址,落在哪是适配器的事 |
58
+ | 判存在只认列父目录翻完页 | `lookup.negative=list-only` | 先读模型层自持的索引 View 快判(有界成本);索引未命中且真要建时才付 `listEntities` 全量代价;`complete=false` → 记欠账、本拍跳过,**绝不新建** |
59
+ | 建后必回读核对残片 | `identity.collision=silent-rename` + `write.confirm` | 回读关进 `ensureEntity`,模型层只看三态;`ambiguous` → HALT 报人 |
60
+ | 先追加后推水位、任一失败整批作废 | `write.cas` + `write.confirm` | 这是 `cas=none` 分支的标准应对,不是某介质专属规则 |
61
+ | 写权用经验判定 | `access.probe=empirical` | 不查权限表、不解析显示名,只消费三态;`read-only` → 只召回不沉淀且禁说「我记住了」 |
62
+ | 容器级探测漏落点 | `access.scope=leaf` | `resolve` 只走 seed 一跳;容器级失败 = `UNAVAILABLE` 而非未配置 |
63
+
64
+ ## 四、适配器取值表
65
+
66
+ 🔴 **一个部署会同时用多个适配器**:narrative 类 section 走 `dingtalk-doc`,registry 类 section 走 `aitable`(见 `model.md` §三 kind、`adapters.md`)。端口的解耦价值正在这里——同一套模型层操作,registry 换介质就把一半病理关掉。
67
+
68
+ | 位 | `dingtalk-doc`(narrative·生产) | `aitable`(registry·生产,🔬实测) | `local-md`(开发/评测) |
69
+ |---|---|---|---|
70
+ | `read.completeness` | `verified-partial` | **`exact`** | `exact` |
71
+ | `maxFactBytes / maxViewBytes` | 4KB / 24KB | 单元格上限(故不放长正文) | ∞ / ∞ |
72
+ | `write.confirm` | `required` | `required` | `implicit` |
73
+ | `write.cas` | **`none`(raw 与 view 都是)** | `revision`(按 recordId 改行) | `revision`(临时文件 rename) |
74
+ | `dedupe.enforcement` | `client` | `server`(按 key 查重) | `client`(读 exact,故可靠) |
75
+ | `identity.collision` | `silent-rename` | `reject`(recordId 唯一) | `reject` |
76
+ | `lookup.negative` | `list-only` | **`immediate`**(`--filters` 查不到就是没有) | `immediate` |
77
+ | `access.scope` | `leaf` | `leaf`(baseId 直达) | `container` |
78
+ | `access.probe` | `empirical` | `declarative` | `declarative` |
79
+
80
+ > 🔬 **`aitable=exact/immediate` 是实测结论**(2026-07-23 核对 `dws aitable record query --help`):`--all`+`--cursor` 真分页无 341 行墙、`--filters` 按字段查、`record update --records` 按 recordId 精准改。**这就是"registry 类不该塞进文档"的凭据**:把 `00-群索引`/水位台账/tombstone 从 doc 挪到 aitable,`read.completeness=exact` 让 count 对账分支自动全灭、`lookup.negative=immediate` 让翻页判存在分支自动全灭。
81
+
82
+ > 🔬 **`dingtalk-doc` 的 `write.cas=none` 是实测结论**:`dws doc update` 只有 `--mode overwrite|append`,**没有任何 revision / expectVersion / CAS 参数**(2026-07-23 核对 `--help`)。所以编译层的整体重写同样没有并发保护,只能靠「写后回读 + 冲突时以最后一次为准」,并接受两个并发写者会互相覆盖。不要以为编译层比原始层安全。
83
+
84
+ `local-md` 落地形态:`<root>/people/<personKey>/` 为目录(**主键即目录名**,显示名进 front-matter),raw section = 一个只增 `.md`,view = 临时文件 rename,meta = `_meta/*.json`。
85
+
86
+ 🔴 **`local-md --chaos` 是契约的组成部分,不是建议**:强制 `complete=false`、随机静默改名、随机 `UNVERIFIED`、注入索引延迟与判重读截断。CI 必须让 chaos 档与真实档跑同一组用例——否则这套抽象只是把 bug 挪到了适配器边界之外,第一次执行仍然发生在生产。
87
+
88
+ ## 五、不能下放给适配器的模型层不变式
89
+
90
+ 1. `raw` 只增不删、纠错用 `supersedes`;`derived` 可重建。**View 里不得存在唯一信息。**
91
+ 2. `evidenceId` 全局唯一即去重键,必须是**源系统主键**(不得用内容哈希或时间戳)。端口只做机械去重,选错它无从发现。
92
+ 3. `audience` 写入时定、读取时只收窄。**收窄必须 fail-closed**:读该实体事实时 `complete=false`,则本轮不引用它的任何事实,宁可答「不清楚」。
93
+ 4. 交叉归属三铁律;唯一上浮方向 = 会话公开 → 人的 `public-facts`。
94
+ 5. 水位键 `(subject, channel)` 单调不回退;**先写事实、后推水位**的因果顺序**即使有 CAS 也不得反转**(CAS 只允许合并两步,不允许先推)。
95
+ 6. 「读失败 ≠ 不存在」的翻译权在模型层。**任何适配器不得把 `UNAVAILABLE` / `complete=false` 补成空集。**
96
+ 7. section 的 `layer` 归属由模型层定义并交给端口;会话隐私档位白名单默认拒绝。
97
+ 8. 知情同意:未启用态零写调用、未 `confirmed` 不说「我记住了」、首次落盘回读念一遍并给链接。
98
+ 9. **有界降级**:同一 root 连续 N 次 `unconfirmed` ⇒ 作废 `probeWrite` 缓存、降级 `read-only`、显式报人。否则表现为「一直在记但什么都没记」。
99
+
100
+ ## 六、这个抽象做不到什么(诚实清单)
101
+
102
+ - **并发建重复无法根除。** 无 CAS 介质上,端口只能把撞车压到绑定期一次并在事后 `ambiguous` → HALT。「建档只在绑定期由单写者触发」是**流程约束,不是端口能力**——这是抽象的公开缺口。
103
+ - **`client` 去重 + `verified-partial` 读存在真实双写窗口。** 判重读本身可能不全;缓解是「`complete=false` 即不重放」,代价是欠账增长而非双写。这两位的组合应在 `resolve` 阶段被显式标记为高风险档。
104
+ - **能力位大多是为 `dingtalk-doc` 的病理而设。** `aitable` 与 `local-md` 上这些位几乎全取宽松档、对应的通用算法分支全灭。这本是"抽象没做干净"的嫌疑——但反过来看,registry 走 aitable 后一半病理消失,恰恰证明**问题不在模型、在"把结构化数据塞进叙事介质"**。准入规则(≥2 适配器行为差异、上限 8 位)仍须执行,否则第 N 个介质接入时会退化成「每个适配器一个 if」。
105
+ - **判存在的读放大只被压小、没被消除。** 索引 View 可能陈旧,真要新建时仍需付全量翻页代价,且它落在「新人首次建档」这条常见路径上。
106
+ - **授权门不在本端口。** 它是消息**源侧**的门,归 ingest 源端口的 `AUTH_REQUIRED` 一态;放进存储端口即是端口变宽的开始。
107
+ - **端口窄度需要持续维护。** 任何一次「我只要再加一个 listFiles / getUrl」,都会把它推回通用文件系统。
@@ -1,3 +1,32 @@
1
1
  # DEAP 平台说明
2
2
 
3
- 敬请期待。DEAP 平台的技能包与交付链尚未开放;`dta agent-platform use deap` 与平台命令一律 fail-closed。开放时在此说明各技能用途,并在 `src/agent-platform.ts` 注册表把状态改为 `supported`。
3
+ **状态:敬请期待。** DEAP 的技能包与交付链尚未开放,`dta agent-platform use deap` 与全部平台命令一律 fail-closed。本文说明它在架构里的位置,不代表它已可用。
4
+
5
+ ## 它可能同时扮演两个角色
6
+
7
+ 注册表里 `deap` 与 `multica-dingtalk` 并列,但它在架构中可能比一个托管平台多一层(见 [Architecture §3](../../../docs/ARCHITECTURE.md#3-每件事谁说了算模型-b)):
8
+
9
+ | | Multica 这类托管平台 | DEAP |
10
+ |---|---|---|
11
+ | 组织身份与感知 | 不提供 | **主要角色**:签发身份、划定场域、送来事件、统一展示 |
12
+ | 运行位置 | **主要角色**:Workspace、Runtime、Agent 生命周期 | **待确认**:是否同时内建一层自己的运行平台 |
13
+ | 本仓库如何对待 | 通过注册表与平台 Skill 包**连接** | 身份与感知**开放后将消费**;运行位置若开放,同样按平台方式接入 |
14
+
15
+ 对本项目而言,DEAP 目前最关键的是两层:
16
+
17
+ 1. **身份权限管控** —— 数字员工的创建、UID、场域边界、入转调离与全生命周期。这一层决定一个 Agent 能以谁的名义、在什么范围内行动。本仓库不发证、不改写、不扩权,只把签发结果冻结进 Session 并全程审计。
18
+ 2. **感知与人机交互** —— 事件订阅与结果展示。本仓库不拥有触发器,只要求送达的事件信封可信、可幂等。
19
+
20
+ 第三层——它是否也提供运行位置——**尚待确认,注册表为此保留了 `deap` 条目**。如果它开放了平台能力,将和其它平台一样通过注册表与平台 Skill 包接入,不会因为它同时是身份与感知平面就获得旁路。两个角色互不排斥,本仓库分别对待,不合并成一格。
21
+
22
+ ## 驱动能力:未知,且不能假定
23
+
24
+ 装配侧可以声明一个数字员工需要哪些常驻节律(见 core 技能 `dta-agent-compose` 的 `references/drive-and-schedules.md`),但**DEAP 是否提供 schedule 能力、是否允许 Agent 自主排程,目前都未确认**。
25
+
26
+ **平台之间驱动能力不同是常态**:不得把 Multica 的 autopilot 语义(5 段 cron、`run_only`、迟到 5 分钟跳过)转述成 DEAP 的行为,也不得因为 Agent 在 Multica 上跑通了节律就认为迁到 DEAP 后节律仍然成立。平台未开放期间,任何节律相关命令与 DEAP 上的其它平台命令一样 fail-closed;需要的节律记为**缺口**,不静默降级成“那就不定时了”。
27
+
28
+ 如果 DEAP 只扮演身份与感知平面(不提供运行位置),那么它对驱动的贡献是**人设时间点的事件**(待办、日程到点)这一类,而常驻节律仍由实际承载运行位置的那个平台提供——两者不能互相替代。
29
+
30
+ ## 开放时要做什么
31
+
32
+ 平台开放时,在本文说明各技能用途,并把 `src/agent-platform.ts` 注册表里 `deap` 的状态改为 `supported`、补齐 `skills` 与 `commands`。在那之前,任何声称 DEAP 已可部署的说法都与 CLI 的实际行为冲突。
@@ -1,38 +1,64 @@
1
1
  # Multica (DingTalk) 平台说明
2
2
 
3
- Multica 是钉钉 FDE fork 的托管 Agent 平台:把一个 dingtalk-agent 数字员工从供给、部署、绑定钉钉机器人到观测、调度全程托管,用户在钉钉里直接与机器人对话即可到达该 Agent。归属本平台后,下面三个技能包按角色装填,各司其职。
3
+ Multica 是钉钉 FDE fork 的托管 Agent 平台:把一个 dingtalk-agent 数字员工从供给、部署、绑定钉钉机器人到观测、调度全程托管,用户在钉钉里直接与机器人对话即可到达该 Agent。归属本平台后,下面两个技能包按角色装填,各司其职。
4
4
 
5
5
  ## 技能包与用途
6
6
 
7
7
  | 角色 | 技能包 | 用途 |
8
8
  |---|---|---|
9
- | deploy | `dingtalk-agent-deploy-multica` | 经 dta 稳定 CLI(`deploy`/`promote`/`observe`)把 Agent Project 受控部署、晋级、观测回流到 Multica Workspace。只编排 CLI,不直接写 Multica,不持有凭据。 |
10
- | boot | `dingtalk-agent-boot-multica` | 部署产物在 Multica Host 内每次任务的启动协议:先加载基础行为再加载岗位 Skill;含部署后的 load smoke。 |
11
- | ops | `multica-external` | 平台运维执行体(纯 HTTPS,`python3 scripts/multica_ext.py <命令>`):workspace/runtime/agent 供给、skill push/pull、钉钉机器人与账号绑定、`chat-send --wait` 免钉钉测试通道、`task-trace` 观测、autopilot 调度、`agent-check` 体检。 |
9
+ | deploy | `dta-deploy-multica` | 经 dta 稳定 CLI(`deploy`/`promote`/`observe`)把 Agent Project 受控部署、晋级、观测回流到 Multica Workspace。只编排 CLI,不直接写 Multica,不持有凭据。 |
10
+ | ops | `dta-ops-multica` | 平台运维执行体(纯 HTTPS,`python3 scripts/multica_ext.py <命令>`):workspace/runtime/agent 供给、skill push/pull、钉钉机器人与账号绑定、`chat-send --wait` 免钉钉测试通道、`task-trace` 观测、autopilot 调度、`agent-check` 体检。 |
12
11
 
13
12
  ## 完整交付链
14
13
 
15
- 供给 workspace/runtime/agent → `skill-push` + `multica agent skills add` 同步并挂载技能(基础行为必须上平台)→ 绑定钉钉机器人(见下)→ `chat-send --wait` 或 DWS 对话验收 → `task-trace` 观测轨迹。
14
+ 供给 workspace/runtime/agent → `dta deploy` 原样发布人类可读 Definition,并将 Basic + Role Skills 作为一级能力精确挂载 独立 Issue load smoke → 绑定钉钉机器人(见下)→ `chat-send --wait` 或 DWS 对话验收 → `task-trace` 观测轨迹。部署哈希和 Skill 清单只存在于 plan/Receipt,不进入 Agent System Prompt。
15
+
16
+ ## 驱动能力:这个平台能提供什么节律
17
+
18
+ 装配侧的节律声明(见 core 技能 `dta-agent-compose` 的 `references/drive-and-schedules.md`)到这里被兑现。Multica 的实现是 **autopilot**:
19
+
20
+ | 装配侧概念 | Multica 实现 | 命令 |
21
+ |---|---|---|
22
+ | 常驻节律 | autopilot + `kind: schedule` trigger | `autopilot-create --cron "<5 段>" --timezone <IANA>` |
23
+ | 不产出工单的纯唤醒 | `--mode run_only` | 同上 |
24
+ | 每拍留一条工单 | `--mode create_issue`(标题模板支持 `{{date}}`) | 同上 |
25
+ | 手动补一拍 | — | `autopilot-trigger --autopilot <uuid>` |
26
+ | 停用/恢复 | `status: paused / active` | `autopilot-set-status` |
27
+ | 回读与审计 | — | `autopilot-list`、`autopilot-get`、`autopilot-runs` |
28
+
29
+ **Agent 自主排程**:平台 API 支持,但 dta 不默认授予——Agent 侧的合同是「不自建定时器、不自改节律」。要开放必须由 operator 显式决定,并保证节律仍可在 `autopilot-list` 中被看见和改掉。
30
+
31
+ 平台语义里有三条会直接影响正确性,装配时必须按它们设计判据:
32
+
33
+ - **cron 是标准 5 段**(无秒、无 `@daily`);时区是 IANA,API 默认 UTC,`multica_ext.py` 默认 `Asia/Shanghai`——**显式传 `--timezone`,不要依赖默认**。
34
+ - **run 幂等按 `(trigger, 计划时刻)`;漏掉的 fire 会塌缩成最近一次,迟到超过 5 分钟直接跳过。** 所以定点 cron 只用来开窗,不能用来保证「某时刻必须发生」——完成判据必须逾期后持续为真。
35
+ - **多条节律不要落在同一分钟。** 外发类的留痕写在动作之后、没有锁,并发唤醒会把同一条消息发两遍给第三方。
36
+
37
+ `autopilot-create` 的 stdout 是**纯 JSON**(`scheduled: ...` 进度提示走 stderr,`json.load(stdout)` 不会炸)。真正的坑是 **create 非幂等**:重复创建就是重复的节律、重复的外发。建完一律用 `autopilot-list` / `autopilot-get` 独立回读条数、cron、时区、提示词、启用状态和 `next_run_at`,不要相信创建响应。
38
+
39
+ `multica_ext.py` **没有** `autopilot-delete`;误建的先 `autopilot-set-status --status paused` 止血,再按需 `DELETE /api/autopilots/{id}`(需带 `X-Workspace-Id` 头,否则 400)。
16
40
 
17
41
  ## 使用前的就绪要求
18
42
 
19
43
  - multica CLI 已安装:`curl -fsSL https://raw.githubusercontent.com/multica-ai/multica/main/scripts/install.sh | bash`
20
- - 已登录并选定目标:先 `dta agent-platform show` 查看解析出的 **Endpoint 及来源**(env `MULTICA_SERVER_URL` > 项目 config > 已登录 profile > 建议值)——现阶段建议值指向预发测试环境,标注为「未确认」。据此 `multica login --server-url <该 endpoint> --token mul_...`。发布前必须与用户确认 endpoint / workspace / Agent 名字,绝不据未确认的建议值直连生产。
44
+ - 已登录并选定目标:先 `dta agent-platform show` 查看解析出的 **Endpoint 及来源**(env `MULTICA_SERVER_URL` > 项目 config > 已登录 profile > 建议值)——建议值指向线上正式域名,仍标注为「未确认」:正因为它是生产环境,必须先与用户确认再据此 `multica login --server-url <该 endpoint> --token mul_...`。发布前必须与用户确认 endpoint / workspace / Agent 名字,绝不据未确认的建议值直连生产。
21
45
  - 代理环境变量可能阻断直连:失败时用 `env -u HTTPS_PROXY -u https_proxy -u ALL_PROXY -u all_proxy` 运行。
22
46
 
23
47
  ## 绑定钉钉机器人的优先级
24
48
 
25
49
  1. **dws 自动路径(首选)**:`dws dev app list` 选已有应用或经用户确认 `dws dev app robot submit` 新建 →(异步,`robot result` 轮询到 SUCCESS)→ `dws dev app credentials get --unified-app-id <id>` 取 AppKey/AppSecret → `dingtalk-bind --agent <id> --client-id <AppKey> --client-secret-stdin`。
26
50
  - 机器人 desc 只允许中英文数字和特定标点(冒号/括号会触发 67010 失败)。
27
- - 想让未绑定账号的用户也能直接对话(快速验收/对客服务),绑定时加 `--allow-unbound`(connect-to-customers 模式:以安装者身份服务未绑定发送者);否则未绑定发送者只会收到账号绑定引导。
51
+ - **不加 `--allow-unbound` 的默认行为是拦截**:任何未把钉钉账号绑定到 Multica 的发送者(包括机器人 owner 本人)@ 机器人只会收到账号绑定引导,不产生任何 Run——验收第一句话就会撞上。想让未绑定账号的用户直接对话(快速验收/对客服务),绑定时加 `--allow-unbound`(connect-to-customers 模式:以安装者身份服务未绑定发送者),或先走 `dingtalk-account-begin --binding-mode message` 完成绑定。
28
52
  2. **复用已有安装**:`dingtalk-list` 查看现有机器人安装换绑。
29
53
  3. **扫码兜底**:`dingtalk-begin` 产出扫码链接交用户完成(不要用 `--wait` 阻塞会话)。
30
54
 
31
55
  ## 验收方式
32
56
 
33
- 两条通道(对应 eval 技能的通道 A 与 B;通道 C 数字员工身份仍在开发中),按顺序用:先 `chat-send --wait` 免钉钉直聊(或 `issue-create --assignee-agent` 派任务),确认 Agent 本身能干活;再走 DWS 对话验证真实钉钉链路——`dws chat bot find --query <机器人名>` 取机器人的 openDingTalkId(字段名以当前 dws 版本返回为准)→ `dws chat message send --open-dingtalk-id <odid> --text '[DTA-DEBUG-<ID>] ...' --uuid <UUID> --yes`(真实外发:只对专用测试机器人,带唯一 marker `--uuid` 幂等键,发送前与用户确认)→ `dws chat message list --open-dingtalk-id <odid> --time <发送前时刻> --direction newer` 读回复。
57
+ 两条通道(对应 eval 技能的通道 A 与 B;通道 C 数字员工身份仍在开发中),按顺序用:先用 `dta workspace run <multica-workspace> --prompt-file <只读问题> --execute --yes --json` 创建受控 Issue 并保存脱敏 evidence;需要直接定位平台时再用 `chat-send --wait` `issue-create --assignee-agent`。Issue 存在但没有 task 是平台唤醒失败,不是 Agent 回答失败。再走 DWS 对话验证真实钉钉链路——`dws chat bot find --query <机器人名> --format json` 取机器人的 openDingTalkId `dws chat message send --open-dingtalk-id <odid> --text '[DTA-DEBUG-<ID>] ...' --uuid <UUID> --yes --format json`(真实外发:只对专用测试机器人)→ `dws chat message list-direct --open-dingtalk-id <odid> --time <发送前时刻> --forward=true --format json` 独立回读。
58
+
59
+ 机器人给出岗位实质回复且未 @ 时保持沉默,只说明链路贯通且行为像;要证明基础行为真的加载,用 `task-trace` 看本次 Run 的轨迹。IM 回归至少覆盖同会话 continuation、引用、缺失附件、真实文件元数据、文档链接与只读业务问题;文件名可见不等于正文可读,回答必须与 trace 中实际取得的层级一致。通道选路、失败归因与证据要求见 `dta-agent-eval` 技能的 `references/interactive-debug-channels.md`;被测对象尚未部署时改走同技能的 `references/local-connector-smoke.md`。
34
60
 
35
- 机器人给出岗位实质回复且未 @ 时保持沉默,只说明链路贯通且行为像;要证明基础行为真的加载,用 `task-trace` 看本次 Run 的轨迹。通道选路、失败归因与证据要求见 `dingtalk-agent-eval` 技能的 `references/interactive-debug-channels.md`;被测对象尚未部署时改走同技能的 `references/local-connector-smoke.md`。
61
+ `multica chat history/thread` 只覆盖已配置的 Multica chat-channel integration,不能假定钉钉机器人安装一定接入该通道;返回 `No chat channel integration is configured` 时,不得据此判断钉钉会话没有历史、附件或引用。真实钉钉 IM 缺字段时按 Basic Behavior 的语义合同走 DWS 原始消息回读;平台层只负责保证任务中可调用当前 DWS 身份,不把 Multica CLI 写进共享 Basic。
36
62
 
37
63
  ## 解绑
38
64
 
@@ -1,9 +1,9 @@
1
1
  ---
2
- name: dingtalk-agent-deploy-multica
2
+ name: dta-deploy-multica
3
3
  description: 当开发者要把 dingtalk-agent Agent Project 部署、更新、检查、恢复或 retire 到 Multica Workspace,或把通过指定 Eval gate 的本地版本晋级、把脱敏反馈转成待评审 Eval candidate 时使用。只编排 dta 的稳定 deploy/promote/observe CLI,不直接调用 Multica 写命令,不创建 Trigger,不热改 Agent 本体或 Skill,也不替用户登录或持有凭据。
4
4
  compatibility: Requires dingtalk-agent and an authenticated Multica CLI profile for live apply/status readback.
5
5
  metadata:
6
- version: "0.2.0"
6
+ version: "0.5.0"
7
7
  ---
8
8
 
9
9
  # 部署 dingtalk-agent 到 Multica
@@ -13,11 +13,12 @@ metadata:
13
13
  ## 工作顺序
14
14
 
15
15
  1. 先运行 `dta workspace doctor <name> --json` 和 `dta workspace inspect <name> --execute --yes --json`。登录、profile 默认 Workspace、显式 Workspace、Runtime、Agent ID 链或 Skill 唯一性不一致时停止;不要用 `--yes` 越过。
16
- 2. 运行 `dta deploy --workspace <name> --dry-run --json`,向用户展示 `planId`、本体/Skill hash、forward/rollback 写预算、将读取和写入的资源类型、删除/retire 范围以及不包含 Trigger。
16
+ 2. 运行 `dta deploy --workspace <name> --dry-run --json`,向用户展示 `planId`、本体/Skill hash、forward/rollback 写预算、将读取和写入的资源类型、删除/retire 范围以及不包含 Trigger。plan 对中间状态敏感:dry-run 之后、apply 之前不要插入 `workspace inspect` 等操作,否则 planId 作废,重跑 dry-run 后立即 apply。
17
17
  3. 只有用户明确确认这份 plan 后,才运行 `dta deploy --workspace <name> --plan-id <id> --yes --json`。旧 planId、当前选择的 Workspace 或显示名都不能代替稳定 ID。
18
- 4. 返回 pending、超时或本地 Receipt 写入失败时,不重复 apply。运行 `dta deploy --workspace <name> --status --operation-id <id> --execute --yes --json` 做独立回读和 reconcile
19
- 5. 只有远端 Agent/Runtime/Skill tree/assignment 回读一致,并且 Boot、Basic、Role 的随机 load smoke 轨迹通过,Workspace 才能进入 `ready`。
20
- 6. 退役先 dry-run,再用冻结 plan 执行 `--retire`;只 archive 精确 Agent,保留 SkillIssue、语义存储和 Artifact,不把 retire 当成删除。
18
+ 4. 返回 pending、超时或本地 Receipt 写入失败时,不重复 apply。load-smoke Issue 已创建但没有任何 task 时,CLI 会且只会按稳定 Agent ID 在同一 Issue 留下一条受控 `@` 评论恢复首次入队;已有 queued/running/terminal task 时绝不重复触发。随后运行 `dta deploy --workspace <name> --status --operation-id <id> --execute --yes --json` 做独立回读和 reconcile。注意不带 `--execute` 的 `--status` 返回的是 apply 时刻的**冻结回执**(首次部署 smoke 字段常是空的 pending 快照,输出会标 `receiptFrozen` 并给出刷新命令);smoke 失败若分类为 `skills-not-visible`(冷沙箱尚未物化 Skill),状态收敛为 `verifying` 而非 `failed`,用带 `--execute --yes` 的 status 重试即可。
19
+ 5. runtime 只有两条正路:显式迁移用 `dta deploy --rebind-runtime`(先 dry-run,planId 绑定迁移前后 runtime,Receipt 记录 from/to);退役用 `--retire`(不受 `agent.runtime-drift` 阻挡)。不要原地改 env 里的 runtime id 硬顶普通 apply——那会被 fail-closed 挡下。
20
+ 6. 只有远端 Agent/Runtime/Skill tree/assignment 回读一致,System Prompt 与本地人类可读 Definition 原文一致,并且 BasicRole 的随机 load smoke 轨迹通过,Workspace 才能进入 `ready`。
21
+ 7. 退役先 dry-run,再用冻结 plan 执行 `--retire`;只 archive 精确 Agent,保留 Skill、Issue、语义存储和 Artifact,不把 retire 当成删除。
21
22
 
22
23
  完整命令、状态机与失败恢复见 [multica-deployment-contract.md](references/multica-deployment-contract.md)。
23
24
 
@@ -41,7 +42,7 @@ metadata:
41
42
  ```text
42
43
  Workspace / profile / Workspace ID / Runtime ID / Agent ID
43
44
  planId / operationId / receiptId
44
- Definition / Boot / Basic / Role hashes
45
+ Definition / Basic / Role hashes
45
46
  forward / rollback 最大写预算与实际写入数
46
47
  create / update / noop / rollback / retire 动作
47
48
  远端独立 readback 与 Skill tool 轨迹
@@ -54,7 +55,8 @@ create / update / noop / rollback / retire 动作
54
55
  - 不直接执行 `multica agent|skill ...` 写命令绕过 `dta deploy`。
55
56
  - 不把 `--yes` 当成登录、改 scope、新增权限或确认新 plan。
56
57
  - 不因模型回复正确而跳过 task status、tool trace 和远端内容 hash。
57
- - 不创建机器人、Webhook、Autopilot、定时器或其他事件 Trigger
58
+ - 不创建机器人、Webhook、Autopilot、定时器或其他事件 Trigger。**deploy 也不会修改或删除已有的**——节律是独立于交付的生产对象,重新部署不重置它、不停用它,也不会因为 Skill 改了就自动改作用域提示词。改节律走 ops 技能 `dta-ops-multica` 的 `autopilot-*`,由 operator 决定。
59
+ - 不因为部署成功就宣称这个数字员工"已经在自己干活了"。`deploy` 只保证本体与 Skill 到位;**没有 schedule 的 Agent 只会被 @ 唤醒**。交付一个需要常驻节律的员工时,必须单独确认 `autopilot-list` 里真有对应节律且 `status=active`,否则如实报告这是缺口。
58
60
  - 不把 token、Secret、邮箱、Server URL、Agent instructions 或 Skill 正文写入提交的 Receipt/日志。
59
61
  - 不把线上消息、生产原始数据或 Observation 直接写回 AGENTS.md、Prompt、Skill 或 Eval suite。
60
62
  - 不把 `proposed` candidate 当成已评审、已发布或已证明有效。
@@ -0,0 +1,67 @@
1
+ # Multica 部署合同
2
+
3
+ ## 命令
4
+
5
+ ```bash
6
+ # 无远端调用;输出稳定 planId
7
+ dta deploy --workspace multica-dev --dry-run --json
8
+
9
+ # planId 与当前本体、Skill、profile/scope 和 W3 inspection 必须仍一致
10
+ dta deploy --workspace multica-dev --plan-id <planId> --yes --json
11
+
12
+ # 异步 smoke 可先返回,再由独立状态回读收敛
13
+ dta deploy --workspace multica-dev --plan-id <planId> --yes --no-wait --json
14
+ dta deploy --workspace multica-dev --status --operation-id <operationId> --execute --yes --json
15
+
16
+ # 只列本地脱敏 operation/Receipt 索引
17
+ dta deploy --workspace multica-dev --list --json
18
+
19
+ # 部署后用单个只读问题创建 Issue;默认只输出零远端调用 plan
20
+ dta workspace run multica-dev --prompt-file readonly-question.md --json
21
+ dta workspace run multica-dev --prompt-file readonly-question.md --execute --yes --json
22
+
23
+ # 只 archive 精确 Agent;不删除 Skill、Storage、Issue 或 Trigger
24
+ dta deploy --workspace multica-dev --retire --dry-run --json
25
+ dta deploy --workspace multica-dev --retire --plan-id <planId> --yes --json
26
+
27
+ # 显式把 Agent 迁到 env 当前指向的 runtime(planId 绑定迁移前后 runtime,Receipt 记 from/to)
28
+ dta deploy --workspace multica-dev --dry-run --rebind-runtime --json
29
+ dta deploy --workspace multica-dev --rebind-runtime --plan-id <planId> --yes --json
30
+ ```
31
+
32
+ ## 状态机
33
+
34
+ ```text
35
+ planned → applying → verifying → ready
36
+ ↘ reconciling → verifying / failed
37
+ ready → retired
38
+ ```
39
+
40
+ - 每次 apply 前重新读取 W3 的 profile/auth/Workspace/Runtime/Agent/Skill ID 链;与 plan 不一致时在任何写入前失败。scope 类失败带结构化 `failureDetails`(expected/actual/hint);provider CLI 自身失败时其 stderr 原文透传到终端,证据文件仍只存 hash。
41
+ - 目标 runtime 的 provider 必须在受支持词汇表内(当前 `opencode`);hermes 等不受支持 provider 在任何远端写入前 fail closed(`scope.runtime-provider-unsupported`)——那类 runtime 上 workspace skill 一律加载不到,部署"成功"也是死局。
42
+ - Agent 的 runtime 归属漂移(`agent.runtime-drift`)只挡普通 apply:显式迁移用 `--rebind-runtime`,退役用 `--retire`,都不被漂移锁死。
43
+ - dta 调用 multica CLI 前会从子进程环境剔除保留变量 `MULTICA_AGENT_ID`(切 Agent 身份鉴权、误报未登录)、`MULTICA_SERVER_URL` 与 `MULTICA_TOKEN`(把 endpoint/身份从具名 profile 脚下换走——shell 里残留的预发 URL 绝不能重定向生产部署);剔除记录在 inspection 的 `envSanitized`。
44
+ - plan 同时冻结 forward/rollback 最大写预算;最多 32 个受管 Skill、每个最多 128 个 supporting files,CLI 在每个 Provider mutation 前检查预算。
45
+ - Provider 调用超时属于结果未知:先保存 `reconciling` operation,再只读 status;不盲目重放 create/update。
46
+ - load-smoke Issue 创建成功但 `issue runs` 为空时,说明 Issue 已持久化而首次入队未落地;apply 只允许按计划中稳定 Agent ID 在同一 Issue 留下一条受控 `mention://agent/<id>` 评论恢复一次。只要已有 task(包括 queued/running/terminal),就不得触发第二次运行。
47
+ - Agent create 未返回 ID 时,只读取最多十个同名候选的稳定 ID,并且只接受唯一完整 instructions/runtime 指纹匹配;显示名本身永远不是绑定依据。
48
+ - 已确认失败可对本 operation 已创建或已更新的受管资源做 best-effort rollback;删除只允许覆盖本 operation 新建的 Skill ID。
49
+ - 宿主 deployment Receipt 保存远端资源 ID、hash、调用顺序和结果,不保存原始 stdout/stderr、Agent instructions、Skill content、Token、邮箱或 Server URL。
50
+
51
+ ## Ready 门禁
52
+
53
+ 1. Agent 的稳定 ID、Runtime ID、Definition/instructions hash 一致;
54
+ 2. Agent instructions 与本地 Definition 原文一致,不混入部署 hash 或 Skill 清单;
55
+ 3. Basic、Role Skill 的完整文件树 hash 一致;
56
+ 4. assignment 精确等于部署计划,不包含隐式平台启动 Skill;
57
+ 5. smoke task completed;
58
+ 6. 轨迹中 Basic、每个 Role 都有装载证据——按目标 runtime 的 trace 词汇解读:Host `skill` 工具调用(工具本身就是装载机制),或云 runtime `read_file` **结果**中的 `**Skill loaded**` banner。装载「尝试」(`Loading skill 'X'` 的 tool_use,失败时同样会出现)与模型自述文本都不算证据,banner 也只认可信工具(read_file/skill)的 tool_result——terminal/bash 的输出是模型指定的,echo 伪造不算数;
59
+ 7. 精确 JSON 回执可回读——优先从 Issue comment 本身取证(平台回读,与工具词汇无关),其次是回复文本 / 受限收口文件;
60
+ 8. 轨迹不含 dws(工具名或 shell 命令);
61
+ 9. operation 与 Receipt hash 完整,Workspace state 绑定当前 desired/deployment hash。
62
+
63
+ 任一门禁失败都保持 `verifying` 或进入 `failed`,不能用本地 OpenCode 通过、旧 smoke、目录存在或模型自述覆盖。
64
+
65
+ **ready 证明什么、不证明什么**:ready 证明每个必需 Skill 有装载证据、精确回执已落盘、无 dws 调用;**不再**证明「轨迹里每个工具都在白名单内」——无害的额外工具/命令不判死(旧的负向白名单在健康云 runtime 上造成过结构性假阴性),词汇表不认识的工具名记录在 `smoke.unrecognizedTools` 供诊断。smoke 失败若分类为 `skills-not-visible`(任务终态但轨迹显示 Skill not found,冷沙箱推送物化竞态),状态收敛为 `verifying`:apply 在预算内做有限次受控 mention 重试,`--status --execute --yes` 也可补一次(该情形如实上报 `remoteWrite`)。重试以「最新 run 的 taskId 变化」为新 run 出现的判据——mention 触发的 run 在平台上异步创建,等不到新 run 只会停手,不会重复 mention;重试评论本身的写入抖动只终止重试并保持 `verifying`,绝不触发对已验证部署的回滚。
66
+
67
+ `workspace run` 在创建 Issue 前重新核对精确 Workspace、Runtime、Agent 与 assignment。报告记录 Issue/task 状态、实际 Skill trace、回答、轮询次数和脱敏调用;同一状态的重复轮询只保留一条证据。超时后仍没有关联 task 时返回 `task.not-created`,并把 Issue 状态写进 `taskDiagnostic`,不能把 Issue 创建成功算成 Agent 已运行。
@@ -1,8 +1,8 @@
1
1
  ---
2
- name: multica-external
2
+ name: dta-ops-multica
3
3
  description: External Python wrapper defining the full developer chain for delivering an agent on the Multica (DingTalk-FDE fork) platform — workspace bootstrap, runtime/agent provisioning, skill assembly (push/pull), DingTalk robot & account binding, execution-trace observability, cron scheduling (autopilot), and health checks. Use when provisioning, operating, debugging, or observing a Multica-managed agent from the command line with only an endpoint and a token.
4
4
  metadata:
5
- version: "0.1.0"
5
+ version: "0.4.1"
6
6
  ---
7
7
 
8
8
  # Multica FDE External Skill
@@ -39,7 +39,7 @@ The token is a `mul_` personal access token or login JWT; mint PATs with
39
39
  `POST /api/tokens`.
40
40
 
41
41
  ```bash
42
- PY=".agents/skills/multica-external/scripts/multica_ext.py"
42
+ PY=".agents/skills/dta-ops-multica/scripts/multica_ext.py"
43
43
  python3 $PY --help
44
44
  python3 $PY whoami --profile pre-fde
45
45
  ```
@@ -60,8 +60,13 @@ python3 $PY agent-create --workspace fde-team --name "FDE 教练" \
60
60
  merge, never clobber; repo registration needs owner/admin. Slugs:
61
61
  `^[a-z0-9]+(-[a-z0-9]+)*$`, reserved list enforced. An agent needs a runtime:
62
62
  FC-E2B cloud (`runtime-create-fc`, requires server-side FC-E2B enablement) or
63
- a local daemon. `runtime-templates` lists templates; when `--template-id` is
64
- omitted and exactly one exists it is auto-picked.
63
+ a local daemon. `runtime-create-fc` injects `--provider opencode` by default —
64
+ the SERVER default is hermes, where workspace skills structurally never load
65
+ (agents answer "Skill not found" with zero warning); only pass
66
+ `--provider hermes` if that is explicitly what you want. `runtime-templates`
67
+ lists templates; when `--template-id` is omitted the sole READY template is
68
+ auto-picked (waiting templates are excluded; several ready ones → the error
69
+ lists candidates sorted by `created_at`, pick the newest).
65
70
 
66
71
  ### 2 — Define & assemble: where the agent comes from, what skills it has
67
72
 
@@ -94,10 +99,19 @@ python3 $PY dingtalk-bind --agent <uuid> --client-id <AppKey> --client-secret-st
94
99
  python3 $PY dingtalk-begin --agent <uuid> --wait
95
100
  python3 $PY dingtalk-list ; python3 $PY dingtalk-revoke --installation <uuid>
96
101
  # Personal-account binding family (dingtalk_account):
97
- python3 $PY dingtalk-account-begin --agent <uuid> # prints QR URL
98
- python3 $PY dingtalk-account-list ; python3 $PY dingtalk-account-unbind --installation <uuid>
102
+ python3 $PY dingtalk-account-begin --agent <uuid> --binding-mode message # prints QR URL
103
+ python3 $PY dingtalk-account-list
104
+ python3 $PY dingtalk-account-unbind --installation <uuid> --binding-mode message
99
105
  ```
100
106
 
107
+ `binding_mode` is server-enforced (400 `invalid_binding_mode` without it) and
108
+ its wire contract is asymmetric: `begin` takes it in the JSON body, `unbind`
109
+ in the query string. `message` maps the sender's identity for conversation
110
+ routing only; `identity` grants the agent a persistent delegated DingTalk
111
+ identity — never choose it silently on the user's behalf. Without
112
+ `--allow-unbound` on the robot install, senders not account-bound to Multica
113
+ (the robot owner included) only receive a binding prompt.
114
+
101
115
  Bind and unbind always come in pairs: robot `dingtalk-bind`/`dingtalk-begin`
102
116
  ↔ `dingtalk-revoke`; account `dingtalk-account-begin` ↔
103
117
  `dingtalk-account-unbind`. `dingtalk-account-begin` returns the binding link
@@ -134,6 +148,54 @@ but a poll-only client fully reconstructs any past run from `task-trace`.
134
148
 
135
149
  ### 5 — Schedule: cron automations (autopilot)
136
150
 
151
+ **Reconcile from the declared source of truth, don't hand-author.** An Agent
152
+ Project declares its rhythms in `dingtalk-agent.json#schedules` (version-controlled;
153
+ see the compose skill's `drive-and-schedules.md`). `dta schedule plan --workspace
154
+ <name> --json` maps them to this provider's autopilot ops. Your job is to reconcile
155
+ that desired state against live:
156
+
157
+ ```bash
158
+ dta schedule plan --workspace <name> --json # desired state, provider-mapped
159
+ # EXIT 2 = do NOT reconcile. The manifest has schedules that collide at the same
160
+ # instant, that can't be proven not to (cross-timezone / cron extension syntax), or
161
+ # that lack a data-derived completion. Deploying anyway creates one autopilot per
162
+ # entry → the same message goes out twice. Fix the manifest, don't override.
163
+ python3 $PY autopilot-list # live; match declared↔live by title
164
+ # create only missing, update drifted, delete extra — autopilot-create is NOT
165
+ # idempotent, so ALWAYS list first; blind-creating duplicates → duplicate外发.
166
+ ```
167
+
168
+ **Completion predicate readback (fail-closed).** When the apply command's
169
+ description carries a `[dta-completion-predicate] <id>` marker (emitted by
170
+ `dta schedule plan` from the manifest's `predicates` registry), `autopilot-create`
171
+ independently GETs the created autopilot and compares the stored marker against
172
+ the declared one — the create response may merely echo the request, so only the
173
+ readback counts. Missing or rewritten marker → non-zero exit with
174
+ `predicateRef: {declared, remote, verified:false, reason}`; do not treat that
175
+ deploy as done (pause the autopilot if in doubt). `autopilot-list`/`autopilot-get`
176
+ annotate each object with its parsed `predicateRef` so reconcile sees drift. At
177
+ runtime, resolve the predicate from the SAME manifest the declaration lives in —
178
+ never from a second hand-written copy:
179
+
180
+ ```bash
181
+ python3 $PY predicate-resolve --manifest dingtalk-agent.json \
182
+ --description "<the run's autopilot description>" # parses the marker, resolves the registry
183
+ # or --id <predicate-id> to skip marker parsing. Unregistered id / conflicting
184
+ # markers exit non-zero: if the predicate can't be resolved, this run cannot
185
+ # claim it knows how completion is judged.
186
+ ```
187
+
188
+ ⚠️ Runtime reachability is NOT wired yet (issue #46): `skill-push` uploads only
189
+ the skill directory — the project manifest does NOT travel with it, the target
190
+ runtime is not guaranteed to have `multica_ext.py`, and nothing invokes
191
+ `predicate-resolve` per schedule run. Until a managed registry artifact (bound
192
+ to the deploy hash) lands, `predicate-resolve` is an OPERATOR-side tool run
193
+ against the local project checkout. Separately, platform-side structured
194
+ predicateRef fields and per-run receipts do not exist — that upstream gap is
195
+ ledgered as MUL-011 in `docs/UPSTREAM-REQUIREMENTS.md`.
196
+
197
+ Manual/ad-hoc autopilots use the same commands directly:
198
+
137
199
  ```bash
138
200
  python3 $PY autopilot-create --title "每日巡检" --agent <uuid> \
139
201
  --description "<the prompt for each run>" --cron "0 9 * * 1-5" --timezone Asia/Shanghai
@@ -160,8 +222,16 @@ python3 $PY agent-check --agent <uuid>
160
222
  Composite JSON report: agent status/archived, runtime health (online /
161
223
  unstable = offline but seen <5 min ago / offline / missing), active tasks +
162
224
  last outcome (with `failure_reason`), recent failure count, DingTalk robot +
163
- account binding state, plus a `checks[]` verdict list; `healthy` = no `fail`
164
- (warns don't fail the check).
225
+ account binding state, plus a `checks[]` verdict list. **BREAKING (0.3.0)**:
226
+ the single `healthy` boolean is removed — it reported green while users could
227
+ not reach the agent at all (no robot / account unbound are only warns).
228
+ Consume the split verdict instead: `infrastructureHealthy` (agent/runtime/
229
+ tasks have no fail) and `userReady` (infrastructure healthy AND robot bound
230
+ AND `dws_identity` bound), with `reasons[]` naming every gap and `nextAction`
231
+ carrying the single next command. `userReadyCaveat` records the one upstream
232
+ blind spot: installations don't read back effective `allow_unbound`, so a
233
+ robot bound with that flag serves unbound senders even when
234
+ `userReady=false`.
165
235
 
166
236
  ### 7 — Basics: issues, comments, chat, members, tokens
167
237
 
@@ -200,7 +270,7 @@ smoke-test a freshly provisioned agent before binding any channel.
200
270
  has **no** download command; `skill-pull` (this script) is the downlink:
201
271
  it writes `SKILL.md` + files into a local directory, byte-identical to
202
272
  what was pushed. An external orchestrator can therefore
203
- `skill-pull --skill multica-external --dest <dir>` and execute the
273
+ `skill-pull --skill dta-ops-multica --dest <dir>` and execute the
204
274
  scripts it receives.
205
275
 
206
276
  ## Command → API map
@@ -263,7 +333,7 @@ MULTICA_SERVER_URL=... MULTICA_TOKEN=... MULTICA_WORKSPACE_ID=... \
263
333
  Both paths produce a byte-identical copy of what `skill-push` uploaded, and
264
334
  bootstrap.sh ships inside the bundle, so a pulled copy can bootstrap the
265
335
  next machine. After bootstrap, stay current with
266
- `python3 scripts/multica_ext.py skill-pull --skill multica-external --dest . --force`.
336
+ `python3 scripts/multica_ext.py skill-pull --skill dta-ops-multica --dest . --force`.
267
337
 
268
338
  Equivalent one-liner where bootstrap.sh is unavailable:
269
339
 
@@ -1,5 +1,5 @@
1
1
  #!/bin/sh
2
- # Bootstrap: fetch the multica-external skill from a Multica workspace
2
+ # Bootstrap: fetch the dta-ops-multica skill from a Multica workspace
3
3
  # onto this machine. Solves the chicken-and-egg problem (skill-pull lives
4
4
  # inside the skill) using only tools already present: the multica CLI, or
5
5
  # curl with endpoint+token. python3 is required either way (the skill's
@@ -15,7 +15,7 @@
15
15
  # intranet/pre endpoints (the Go CLI needs proxies unset by the caller).
16
16
  set -eu
17
17
 
18
- SKILL_NAME="multica-external"
18
+ SKILL_NAME="dta-ops-multica"
19
19
  DEST=""
20
20
  PROFILE=""
21
21
  WS_ID=""