@xdxer/dingtalk-agent 0.1.4-beta.8 → 0.1.4-beta.9

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 (212) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.en.md +395 -0
  3. package/README.md +466 -35
  4. package/dist/bin/dingtalk-agent.js +1058 -10
  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/development-workspace.js +729 -0
  19. package/dist/src/development-workspace.js.map +1 -0
  20. package/dist/src/dws.js +145 -0
  21. package/dist/src/dws.js.map +1 -1
  22. package/dist/src/eval-evidence.js +193 -0
  23. package/dist/src/eval-evidence.js.map +1 -0
  24. package/dist/src/invocation.js +36 -0
  25. package/dist/src/invocation.js.map +1 -0
  26. package/dist/src/lab.js +679 -0
  27. package/dist/src/lab.js.map +1 -0
  28. package/dist/src/lease.js +100 -0
  29. package/dist/src/lease.js.map +1 -0
  30. package/dist/src/memory/candidates.js +451 -0
  31. package/dist/src/memory/candidates.js.map +1 -0
  32. package/dist/src/memory/completion-evidence.js +536 -0
  33. package/dist/src/memory/completion-evidence.js.map +1 -0
  34. package/dist/src/memory/operational.js +263 -0
  35. package/dist/src/memory/operational.js.map +1 -0
  36. package/dist/src/memory/remote-state.js +478 -0
  37. package/dist/src/memory/remote-state.js.map +1 -0
  38. package/dist/src/memory/task-checkpoints.js +204 -0
  39. package/dist/src/memory/task-checkpoints.js.map +1 -0
  40. package/dist/src/multica-deploy.js +1480 -0
  41. package/dist/src/multica-deploy.js.map +1 -0
  42. package/dist/src/multica-provider.js +685 -0
  43. package/dist/src/multica-provider.js.map +1 -0
  44. package/dist/src/opencode-evals.js +1062 -0
  45. package/dist/src/opencode-evals.js.map +1 -0
  46. package/dist/src/opencode-provider.js +531 -0
  47. package/dist/src/opencode-provider.js.map +1 -0
  48. package/dist/src/opencode-workspace.js +197 -0
  49. package/dist/src/opencode-workspace.js.map +1 -0
  50. package/dist/src/perception.js +225 -0
  51. package/dist/src/perception.js.map +1 -0
  52. package/dist/src/personal-event-evals.js +595 -0
  53. package/dist/src/personal-event-evals.js.map +1 -0
  54. package/dist/src/promotion.js +786 -0
  55. package/dist/src/promotion.js.map +1 -0
  56. package/dist/src/remote-semantic-state-live-evals.js +888 -0
  57. package/dist/src/remote-semantic-state-live-evals.js.map +1 -0
  58. package/dist/src/remote-semantic-state-worker.js +38 -0
  59. package/dist/src/remote-semantic-state-worker.js.map +1 -0
  60. package/dist/src/remote-state-evals.js +501 -0
  61. package/dist/src/remote-state-evals.js.map +1 -0
  62. package/dist/src/response-gate.js +51 -0
  63. package/dist/src/response-gate.js.map +1 -0
  64. package/dist/src/robot-evals.js +770 -0
  65. package/dist/src/robot-evals.js.map +1 -0
  66. package/dist/src/sessions.js +66 -105
  67. package/dist/src/sessions.js.map +1 -1
  68. package/dist/src/skill-manager.js +25 -16
  69. package/dist/src/skill-manager.js.map +1 -1
  70. package/dist/src/skills.js.map +1 -1
  71. package/dist/src/storage-evals.js +26 -0
  72. package/dist/src/storage-evals.js.map +1 -0
  73. package/dist/src/types.js.map +1 -1
  74. package/dist/src/waits.js +5 -1
  75. package/dist/src/waits.js.map +1 -1
  76. package/dist/src/workspace.js +28 -3
  77. package/dist/src/workspace.js.map +1 -1
  78. package/docs/INSTALLATION.md +47 -0
  79. package/docs/SECOND-AGENT-ACCEPTANCE.md +62 -0
  80. package/docs/architecture/agent-memory-topology.png +0 -0
  81. package/docs/architecture/agent-memory-topology.svg +132 -0
  82. package/docs/architecture/general-agent-kernel-topology.png +0 -0
  83. package/docs/architecture/general-agent-kernel-topology.svg +149 -0
  84. package/docs/architecture/provider-bound-development-workspace.png +0 -0
  85. package/docs/architecture/provider-bound-development-workspace.svg +141 -0
  86. package/docs/architecture/task-completion-gate.png +0 -0
  87. package/docs/architecture/task-completion-gate.svg +191 -0
  88. package/docs/schemas/agent-audit-load-evidence.schema.json +14 -0
  89. package/docs/schemas/agent-audit.schema.json +92 -0
  90. package/docs/schemas/agent-bindings.schema.json +54 -0
  91. package/docs/schemas/agent-definition.schema.json +78 -0
  92. package/docs/schemas/agent-enhancement-plan.schema.json +88 -0
  93. package/docs/schemas/agent-enhancement-receipt.schema.json +37 -0
  94. package/docs/schemas/enriched-invocation.schema.json +46 -0
  95. package/docs/schemas/eval-candidate-plan.schema.json +28 -0
  96. package/docs/schemas/eval-candidate-result.schema.json +20 -0
  97. package/docs/schemas/eval-candidate.schema.json +30 -0
  98. package/docs/schemas/invocation.schema.json +19 -0
  99. package/docs/schemas/memory-candidate-proposal.schema.json +18 -0
  100. package/docs/schemas/memory-candidate.schema.json +76 -0
  101. package/docs/schemas/memory-publish-target.schema.json +25 -0
  102. package/docs/schemas/multica-deployment-list.schema.json +29 -0
  103. package/docs/schemas/multica-deployment-operation.schema.json +51 -0
  104. package/docs/schemas/multica-deployment-plan.schema.json +70 -0
  105. package/docs/schemas/multica-deployment-receipt.schema.json +87 -0
  106. package/docs/schemas/multica-deployment-status.schema.json +23 -0
  107. package/docs/schemas/multica-workspace-inspection.schema.json +77 -0
  108. package/docs/schemas/multica-workspace-plan.schema.json +68 -0
  109. package/docs/schemas/multica-workspace-resource-list.schema.json +27 -0
  110. package/docs/schemas/multica-workspace-status.schema.json +34 -0
  111. package/docs/schemas/observation.schema.json +21 -0
  112. package/docs/schemas/operational-memory-provider.schema.json +36 -0
  113. package/docs/schemas/operational-memory-record.schema.json +24 -0
  114. package/docs/schemas/perception-input.schema.json +56 -0
  115. package/docs/schemas/project.schema.json +112 -0
  116. package/docs/schemas/promotion-list.schema.json +36 -0
  117. package/docs/schemas/promotion-plan.schema.json +60 -0
  118. package/docs/schemas/promotion-policy.schema.json +29 -0
  119. package/docs/schemas/promotion-receipt.schema.json +43 -0
  120. package/docs/schemas/promotion-status.schema.json +23 -0
  121. package/docs/schemas/release-readiness.schema.json +65 -0
  122. package/docs/schemas/remote-semantic-state-live-eval.schema.json +60 -0
  123. package/docs/schemas/remote-semantic-state-manifest.schema.json +79 -0
  124. package/docs/schemas/remote-semantic-state-provider.schema.json +98 -0
  125. package/docs/schemas/response-gate.schema.json +20 -0
  126. package/docs/schemas/task-checkpoint.schema.json +71 -0
  127. package/docs/schemas/task-completion-evidence.schema.json +154 -0
  128. package/docs/schemas/workspace-doctor.schema.json +56 -0
  129. package/docs/schemas/workspace-state.schema.json +39 -0
  130. package/evals/baselines/2026-07-16/opencode-basic-010-completion-summary.json +123 -0
  131. package/evals/baselines/2026-07-16/opencode-basic-skill-required-summary.json +69 -0
  132. package/evals/baselines/2026-07-16/opencode-multi-surface-summary.json +63 -0
  133. package/evals/baselines/2026-07-16/remote-state-live-summary.json +70 -0
  134. package/evals/baselines/2026-07-17/agent-enhance-opencode-dogfood-summary.json +98 -0
  135. package/evals/baselines/2026-07-17/personal-event-live-readiness-summary.json +68 -0
  136. package/examples/agents/fde-coach/AGENTS.md +26 -0
  137. package/examples/agents/fde-coach/MEMORY.md +3 -0
  138. package/examples/agents/fde-coach/fields/default/field.json +24 -0
  139. package/examples/agents/fde-coach/knowledge/INDEX.md +4 -0
  140. package/examples/agents/fde-coach/skills/fde-coach/SKILL.md +13 -0
  141. package/examples/agents/release-manager/AGENTS.md +26 -0
  142. package/examples/agents/release-manager/MEMORY.md +3 -0
  143. package/examples/agents/release-manager/fields/default/field.json +24 -0
  144. package/examples/agents/release-manager/knowledge/INDEX.md +4 -0
  145. package/examples/agents/release-manager/skills/release-manager/SKILL.md +13 -0
  146. package/lab/README.md +109 -0
  147. package/lab/agent-eval/catalog.json +91 -0
  148. package/lab/agent-eval/classic-failures.json +177 -0
  149. package/lab/agent-eval/completion-gate-regression.json +99 -0
  150. package/lab/agent-eval/personal-event-live.example.json +94 -0
  151. package/lab/agent-eval/remote-semantic-state-live.example.json +70 -0
  152. package/lab/agent-eval/remote-semantic-state-provider.fixture.json +47 -0
  153. package/lab/agent-eval/remote-state-workspace/AGENTS.md +8 -0
  154. package/lab/agent-eval/remote-state-workspace/opencode.json +7 -0
  155. package/lab/agent-eval/remote-state-workspace/skills/remote-state-operator/SKILL.md +13 -0
  156. package/lab/agent-eval/remote-state.example.json +31 -0
  157. package/lab/agent-eval/workspace/AGENTS.md +7 -0
  158. package/lab/agent-eval/workspace/MEMORY.md +4 -0
  159. package/lab/agent-eval/workspace/artifacts/pending-review.md +3 -0
  160. package/lab/agent-eval/workspace/knowledge/INDEX.md +4 -0
  161. package/lab/agent-eval/workspace/opencode.json +20 -0
  162. package/lab/manifest.example.json +27 -0
  163. package/lab/manifest.personal-event.example.json +27 -0
  164. package/lab/project-workspace/README.md +11 -0
  165. package/lab/project-workspace/fake-multica-provider.mjs +266 -0
  166. package/lab/project-workspace/multica-deploy.fixture.json +29 -0
  167. package/lab/project-workspace/multica-readonly.fixture.json +69 -0
  168. package/lab/project-workspace/observation.fixture.json +14 -0
  169. package/lab/project-workspace/opencode-provider-suite.json +65 -0
  170. package/lab/project-workspace/project.fixture.json +44 -0
  171. package/lab/project-workspace/promotion-policy.fixture.json +15 -0
  172. package/lab/robot-eval/pool.example.json +30 -0
  173. package/lab/robot-eval/suite.json +123 -0
  174. package/lab/robot-eval/workspace/AGENTS.md +21 -0
  175. package/lab/robot-eval/workspace/MEMORY.md +3 -0
  176. package/lab/robot-eval/workspace/knowledge/INDEX.md +5 -0
  177. package/lab/robot-eval/workspace/opencode.json +22 -0
  178. package/lab/schemas/agent-eval-catalog.schema.json +47 -0
  179. package/lab/schemas/lab-manifest.schema.json +70 -0
  180. package/lab/schemas/personal-event-eval.schema.json +91 -0
  181. package/lab/schemas/remote-state-eval.schema.json +66 -0
  182. package/lab/schemas/robot-eval-suite.schema.json +184 -0
  183. package/lab/schemas/robot-pool.schema.json +56 -0
  184. package/package.json +21 -3
  185. package/skills/dingtalk-agent-boot-multica/SKILL.md +40 -0
  186. package/skills/dingtalk-agent-compose/SKILL.md +110 -0
  187. package/skills/dingtalk-agent-compose/assets/AGENTS.template.md +26 -0
  188. package/skills/dingtalk-agent-compose/assets/agent.bindings.dingtalk-doc.template.json +13 -0
  189. package/skills/dingtalk-agent-compose/assets/agent.bindings.local.template.json +13 -0
  190. package/skills/dingtalk-agent-compose/assets/opencode.template.json +12 -0
  191. package/skills/dingtalk-agent-compose/assets/role-skill.template.md +24 -0
  192. package/skills/dingtalk-agent-compose/evals/evals.json +94 -0
  193. package/skills/dingtalk-agent-compose/references/agent-definition-contract.md +55 -0
  194. package/skills/dingtalk-agent-compose/references/opencode-host-contract.md +65 -0
  195. package/skills/dingtalk-agent-compose/references/storage-routing.md +20 -0
  196. package/skills/dingtalk-agent-deploy/SKILL.md +60 -0
  197. package/skills/dingtalk-agent-deploy/references/multica-deployment-contract.md +49 -0
  198. package/skills/dingtalk-agent-deploy/references/promotion-observation-contract.md +49 -0
  199. package/skills/dingtalk-agent-eval/SKILL.md +116 -0
  200. package/skills/dingtalk-agent-eval/assets/eval-catalog.template.json +18 -0
  201. package/skills/dingtalk-agent-eval/evals/evals.json +61 -0
  202. package/skills/dingtalk-agent-eval/references/eval-topology.md +34 -0
  203. package/skills/dingtalk-agent-eval/references/evidence-contract.md +31 -0
  204. package/skills/dingtalk-agent-eval/references/scenario-taxonomy.md +25 -0
  205. package/skills/dingtalk-agent-eval/references/storage-modes.md +75 -0
  206. package/skills/dingtalk-basic-behavior/SKILL.md +63 -3
  207. package/skills/dingtalk-basic-behavior/assets/memory-candidate-proposal.json +10 -0
  208. package/skills/dingtalk-basic-behavior/assets/{task-checkpoint.md → task-checkpoint.json} +2 -21
  209. package/skills/dingtalk-basic-behavior/references/action-contract.md +2 -0
  210. package/skills/dingtalk-basic-behavior/references/memory-and-evolution.md +15 -1
  211. package/skills/dingtalk-basic-behavior/references/perception-and-gates.md +28 -0
  212. package/skills/dingtalk-basic-behavior/references/task-lifecycle.md +32 -7
@@ -0,0 +1,888 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { spawnSync } from 'node:child_process';
3
+ import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync, } from 'node:fs';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { bootstrap } from './bootstrap.js';
7
+ import * as dws from './dws.js';
8
+ import { digest, stableStringify } from './events.js';
9
+ import { planRemoteSemanticState, rehydrateRemoteSemanticState, verifyRemoteSemanticStateCache, } from './memory/remote-state.js';
10
+ const SECRET_KEY = /(?:secret|token|password|credential|privateKey)/i;
11
+ const PLACEHOLDER = /(?:replace-with|<[^>]+>|dedicated-test|readback-user-id|known-node)/i;
12
+ const FORBIDDEN_REMOTE_PATTERNS = [
13
+ ['waitId', /\bwait[-_ ]?id\b/i],
14
+ ['generation', /\bgeneration\b/i],
15
+ ['idempotencyKey', /\bidempotency[-_ ]?key\b/i],
16
+ ['lease', /\blease\b/i],
17
+ ['Action Receipt', /\baction[-_ ]?receipt\b/i],
18
+ ['credentials', /\b(?:credential|client[-_ ]?secret|access[-_ ]?token|refresh[-_ ]?token|password)s?\b/i],
19
+ ];
20
+ export function runRemoteSemanticStateLiveEvaluation(root, workspacePath, suitePath, options = {}) {
21
+ const workspace = safeExistingDirectory(root, workspacePath, 'workspace');
22
+ const suiteFile = safeExistingFile(root, suitePath, 'suite');
23
+ const suite = validateSpec(readJson(suiteFile));
24
+ const providerPlan = planRemoteSemanticState(suite.provider);
25
+ const local = bootstrap(workspace, {
26
+ agent: `local-dir:${workspace}`,
27
+ skills: `local-dir:${join(workspace, 'skills')}`,
28
+ memory: suite.provider.layers.l1.uri,
29
+ knowledge: suite.provider.layers.l3.uri,
30
+ profile: suite.provider.authority.profile,
31
+ expectedUserId: suite.provider.authority.expectedUserId,
32
+ deferRemote: true,
33
+ });
34
+ const missingRequirements = readinessGaps(suite, local);
35
+ const writesPlanned = suite.write.documentSlots.length +
36
+ (suite.write.aitableFields.length ? 1 : 0) + (suite.write.driftProbe ? 1 : 0);
37
+ const plan = {
38
+ $schema: 'dingtalk-agent/remote-semantic-state-live-plan@1',
39
+ id: suite.id,
40
+ scenario: suite.scenario,
41
+ engine: 'storage',
42
+ sideEffect: false,
43
+ remoteRead: false,
44
+ liveAuthorized: suite.liveAuthorized === 'yes',
45
+ ready: missingRequirements.length === 0,
46
+ missingRequirements,
47
+ workspace: relative(realpathSync(resolve(root)), workspace),
48
+ suite: relative(realpathSync(resolve(root)), suiteFile),
49
+ provider: providerPlan,
50
+ requiredSkills: suite.requiredSkills,
51
+ executionGate: 'LIVE_AUTHORIZED=yes + --execute --live --yes',
52
+ writesPlanned,
53
+ writeBudget: suite.write.budget,
54
+ writeKinds: [
55
+ ...suite.write.documentSlots.map((slot) => `append-${slot}-marker`),
56
+ 'update-fixed-l2-record',
57
+ ...(suite.write.driftProbe ? ['append-l1-drift-marker'] : []),
58
+ ],
59
+ evidenceRoot: suite.evidenceRoot,
60
+ coldStart: 'pre-write baseline + post-write process A + new-state-dir process B',
61
+ controlState: suite.provider.controlState,
62
+ };
63
+ if (!options.execute)
64
+ return plan;
65
+ if (!options.live || !options.yes || suite.liveAuthorized !== 'yes') {
66
+ throw new Error('Phase 11B 真实 DWS 读取/写入必须同时满足 LIVE_AUTHORIZED=yes 与 --execute --live --yes');
67
+ }
68
+ if (missingRequirements.length) {
69
+ throw new Error(`Phase 11B Live 资源或本地 Definition 未就绪: ${missingRequirements.join('; ')}`);
70
+ }
71
+ if (writesPlanned !== suite.write.budget) {
72
+ throw new Error(`Phase 11B 写预算必须精确等于计划写次数 ${writesPlanned}`);
73
+ }
74
+ const runId = `DTA-PHASE-11B-${Date.now()}-${randomUUID()}`;
75
+ const evidenceRoot = safeEvidenceRoot(workspace, suite.evidenceRoot, runId);
76
+ mkdirSync(evidenceRoot, { recursive: true });
77
+ const planPath = join(evidenceRoot, 'plan.json');
78
+ writeJson(planPath, { ...plan, sideEffect: 'authorized-live-probes', remoteRead: true, runId });
79
+ writeJson(join(evidenceRoot, 'provider.redacted.json'), redactProvider(suite.provider));
80
+ writeJson(join(evidenceRoot, 'provider.raw.json'), suite.provider);
81
+ const stateRoot = join(evidenceRoot, 'state');
82
+ const activeStateDir = join(stateRoot, 'active');
83
+ const baselineStateDir = join(stateRoot, 'baseline');
84
+ const firstStateDir = join(stateRoot, 'first-post-write');
85
+ let stage = 'preflight';
86
+ let writeCount = 0;
87
+ const writeResults = [];
88
+ const consumeWrite = (resource) => {
89
+ if (!suite.write.allowlist.includes(resource)) {
90
+ throw new Error(`Phase 11B 写目标不在 allowlist: ${resource}`);
91
+ }
92
+ if (writeCount >= suite.write.budget) {
93
+ throw new Error(`Phase 11B 写预算耗尽: ${writeCount}/${suite.write.budget}`);
94
+ }
95
+ writeCount += 1;
96
+ };
97
+ try {
98
+ stage = 'identity-and-resource-preflight';
99
+ const identity = dws.getSelf(suite.provider.authority.profile);
100
+ const actualUserId = clean(findValue(identity, ['userId', 'user_id', 'userid']));
101
+ if (!actualUserId || actualUserId !== suite.provider.authority.expectedUserId) {
102
+ throw new Error(`Phase 11B DWS 身份不符: expected=${suite.provider.authority.expectedUserId}, ` +
103
+ `actual=${actualUserId || '-'}`);
104
+ }
105
+ writeJson(join(evidenceRoot, 'identity-readback.json'), identity);
106
+ const documentBodies = {};
107
+ const documentInfo = {};
108
+ for (const slot of ['l1', 'l3']) {
109
+ const layer = suite.provider.layers[slot];
110
+ const info = dws.docInfo(nodeRef(layer.uri), suite.provider.authority.profile);
111
+ assertAdoc(slot, info);
112
+ const body = String(dws.readDoc(nodeRef(layer.uri), suite.provider.authority.profile));
113
+ assertRemoteMarkdownSafe(slot, body);
114
+ documentBodies[slot] = body;
115
+ documentInfo[slot] = info;
116
+ }
117
+ writeJson(join(evidenceRoot, 'document-info.json'), documentInfo);
118
+ const l2 = suite.provider.layers.l2;
119
+ const base = dws.getAiTableBase(l2.baseId, suite.provider.authority.profile);
120
+ const tables = dws.getAiTableTables(l2.baseId, [l2.tableId], suite.provider.authority.profile);
121
+ verifyTableShape(base, tables, suite.provider, suite.write.aitableFields);
122
+ const initialRows = dws.queryAiTableRecords({
123
+ baseId: l2.baseId, tableId: l2.tableId, recordIds: [l2.recordId],
124
+ profile: suite.provider.authority.profile,
125
+ });
126
+ const initial = exactStructuredState(initialRows, suite.provider);
127
+ const initialUnique = queryUniqueRecord(suite.provider);
128
+ writeJson(join(evidenceRoot, 'preflight.json'), {
129
+ identityMatched: true,
130
+ documents: Object.fromEntries(['l1', 'l3'].map((slot) => [slot, {
131
+ type: `${String(documentInfo[slot].contentType)}/${String(documentInfo[slot].extension)}`,
132
+ hash: digest(documentBodies[slot]),
133
+ }])),
134
+ aitable: {
135
+ baseRead: Boolean(base), tableRead: Boolean(tables), recordIdMatched: true,
136
+ uniqueKeyScopeCount: initialUnique.rows.length,
137
+ structuredStateHash: digest(stableStringify(initial)),
138
+ },
139
+ dryRunHadNoRemoteCall: false,
140
+ });
141
+ stage = 'pre-write-baseline';
142
+ const baseline = requireWorker(invokeWorker(evidenceRoot, 'baseline', {
143
+ action: 'bootstrap-and-rehydrate', workspace, stateDir: activeStateDir,
144
+ provider: suite.provider,
145
+ }));
146
+ writeJson(join(evidenceRoot, 'bootstrap', 'baseline.json'), baseline);
147
+ stage = 'drift-fail-closed';
148
+ let driftEvidence = { provided: suite.write.driftProbe, passed: false };
149
+ if (suite.write.driftProbe) {
150
+ const driftMarker = `DTA-PHASE-11B-DRIFT-${Date.now()}-${randomUUID()}`;
151
+ const resource = suite.provider.layers.l1.uri;
152
+ consumeWrite(resource);
153
+ const accepted = appendDocumentMarker(evidenceRoot, 'drift', resource, driftMarker, suite.provider.authority.profile);
154
+ const readback = String(dws.readDoc(nodeRef(resource), suite.provider.authority.profile));
155
+ const markerCount = semanticMarkerCount(readback, driftMarker);
156
+ const stale = invokeWorker(evidenceRoot, 'drift-stale-bootstrap', {
157
+ action: 'rehydrate-from-bootstrap', workspace, stateDir: activeStateDir,
158
+ provider: suite.provider, bootstrap: baseline.bootstrap,
159
+ });
160
+ driftEvidence = {
161
+ provided: true, accepted, marker: driftMarker, markerCount,
162
+ workerStatus: stale.status, error: stale.stderr.trim(),
163
+ passed: markerCount === 1 && stale.status !== 0 && stale.stderr.includes('发生漂移'),
164
+ };
165
+ writeResults.push({ kind: 'l1-drift', resource, ...driftEvidence });
166
+ if (!driftEvidence.passed)
167
+ throw new Error('Phase 11B drift fail-closed 探针未按预期失败');
168
+ }
169
+ renameSync(activeStateDir, baselineStateDir);
170
+ stage = 'authorized-write-probes';
171
+ for (const slot of suite.write.documentSlots) {
172
+ const resource = suite.provider.layers[slot].uri;
173
+ consumeWrite(resource);
174
+ const accepted = appendDocumentMarker(evidenceRoot, slot, resource, runId, suite.provider.authority.profile);
175
+ const readback = String(dws.readDoc(nodeRef(resource), suite.provider.authority.profile));
176
+ const markerCount = semanticMarkerCount(readback, runId);
177
+ const verified = markerCount === 1;
178
+ writeResults.push({
179
+ kind: 'document-append', slot, resource, commandAccepted: accepted,
180
+ marker: runId, markerCount, verified,
181
+ verification: 'independent-full-document-readback',
182
+ });
183
+ if (!verified)
184
+ throw new Error(`Phase 11B ${slot} marker 回读必须恰好一次`);
185
+ }
186
+ const currentRows = dws.queryAiTableRecords({
187
+ baseId: l2.baseId, tableId: l2.tableId, recordIds: [l2.recordId],
188
+ profile: suite.provider.authority.profile,
189
+ });
190
+ const current = exactStructuredState(currentRows, suite.provider);
191
+ const cells = {};
192
+ for (const field of suite.write.aitableFields) {
193
+ cells[l2.fields[field]] = appendTextMarker(current[field], runId);
194
+ }
195
+ const l2Resource = l2ResourceRef(suite.provider);
196
+ consumeWrite(l2Resource);
197
+ const update = dws.updateAiTableRecord(l2.baseId, l2.tableId, l2.recordId, cells, suite.provider.authority.profile);
198
+ const updatedRows = dws.queryAiTableRecords({
199
+ baseId: l2.baseId, tableId: l2.tableId, recordIds: [l2.recordId],
200
+ profile: suite.provider.authority.profile,
201
+ });
202
+ const updated = exactStructuredState(updatedRows, suite.provider);
203
+ const uniqueAfter = queryUniqueRecord(suite.provider);
204
+ const l2MarkerCounts = Object.fromEntries(suite.write.aitableFields.map((field) => [
205
+ field, semanticMarkerCount(updated[field], runId),
206
+ ]));
207
+ const l2Verified = suite.write.aitableFields.every((field) => updated[field] === cells[l2.fields[field]] && l2MarkerCounts[field] === 1) &&
208
+ uniqueAfter.rows.length === 1 && uniqueAfter.rows[0]?.recordId === l2.recordId;
209
+ writeResults.push({
210
+ kind: 'aitable-record-update', resource: l2Resource,
211
+ commandAccepted: update.accepted, recordId: l2.recordId,
212
+ fields: suite.write.aitableFields, markerCounts: l2MarkerCounts,
213
+ keyScopeCount: uniqueAfter.rows.length, verified: l2Verified,
214
+ verification: 'record-id-readback-plus-key-scope-unique-query',
215
+ });
216
+ writeJson(join(evidenceRoot, 'write-results.json'), writeResults);
217
+ if (!l2Verified)
218
+ throw new Error('Phase 11B L2 固定 recordId 写后回读不一致');
219
+ if (writeCount !== suite.write.budget) {
220
+ throw new Error(`Phase 11B 实际写次数 ${writeCount} 与预算 ${suite.write.budget} 不一致`);
221
+ }
222
+ stage = 'independent-readback';
223
+ const independent = independentReadback(suite.provider);
224
+ writeFile(join(evidenceRoot, 'remote-readback', 'l1.md'), independent.documents.l1.body);
225
+ writeFile(join(evidenceRoot, 'remote-readback', 'l3.md'), independent.documents.l3.body);
226
+ writeJson(join(evidenceRoot, 'remote-readback', 'l2.json'), independent.l2.state);
227
+ writeJson(join(evidenceRoot, 'independent-readback.json'), independent);
228
+ stage = 'post-write-process-a';
229
+ const first = requireWorker(invokeWorker(evidenceRoot, 'first-post-write', {
230
+ action: 'bootstrap-and-rehydrate', workspace, stateDir: activeStateDir,
231
+ provider: suite.provider,
232
+ }));
233
+ writeJson(join(evidenceRoot, 'first-rehydrate.json'), first);
234
+ renameSync(activeStateDir, firstStateDir);
235
+ stage = 'post-write-process-b';
236
+ const second = requireWorker(invokeWorker(evidenceRoot, 'second-post-write', {
237
+ action: 'bootstrap-and-rehydrate', workspace, stateDir: activeStateDir,
238
+ provider: suite.provider,
239
+ }));
240
+ writeJson(join(evidenceRoot, 'second-rehydrate.json'), second);
241
+ const coldStart = compareColdStart(baseline, first, second, independent);
242
+ if (!coldStart.passed)
243
+ throw new Error('Phase 11B 两进程冷启动 hash/nextAction 不一致');
244
+ stage = 'local-fail-closed-matrix';
245
+ const localFailClosed = runLocalFailClosedChecks(workspace, activeStateDir, second, suite.provider);
246
+ const liveFailClosed = runLiveReadFailClosedChecks(evidenceRoot, workspace, suite, driftEvidence, second);
247
+ const failClosed = { ...localFailClosed, ...liveFailClosed };
248
+ if (Object.values(failClosed).some((item) => item.required && !item.passed)) {
249
+ throw new Error('Phase 11B fail-closed 必选矩阵未全部通过');
250
+ }
251
+ stage = 'teardown-readback';
252
+ const teardown = independentReadback(suite.provider);
253
+ const teardownPassed = semanticMarkerCount(teardown.documents.l1.body, runId) === 1 &&
254
+ semanticMarkerCount(teardown.documents.l3.body, runId) === 1 &&
255
+ suite.write.aitableFields.every((field) => semanticMarkerCount(teardown.l2.state[field], runId) === 1) &&
256
+ teardown.l2.uniqueCount === 1 &&
257
+ !teardown.documents.l1.forbidden.length && !teardown.documents.l3.forbidden.length;
258
+ writeJson(join(evidenceRoot, 'teardown.json'), {
259
+ passed: teardownPassed,
260
+ policy: 'retain-authorized-markers; no delete, overwrite, or cleanup',
261
+ writeCount,
262
+ markerCounts: {
263
+ l1: semanticMarkerCount(teardown.documents.l1.body, runId),
264
+ l3: semanticMarkerCount(teardown.documents.l3.body, runId),
265
+ l2: Object.fromEntries(suite.write.aitableFields.map((field) => [
266
+ field, semanticMarkerCount(teardown.l2.state[field], runId),
267
+ ])),
268
+ },
269
+ uniqueKeyScopeCount: teardown.l2.uniqueCount,
270
+ });
271
+ if (!teardownPassed)
272
+ throw new Error('Phase 11B 最终回读不一致');
273
+ const assertions = {
274
+ $schema: 'dingtalk-agent/remote-semantic-state-live-assertions@1',
275
+ runId,
276
+ passed: true,
277
+ identityMatched: true,
278
+ writes: { planned: suite.write.budget, attempted: writeCount, verified: writeResults.length },
279
+ coldStart,
280
+ failClosed,
281
+ controlState: {
282
+ provider: second.state.manifest.controlState.provider,
283
+ remoteProjection: second.state.manifest.controlState.remoteProjection,
284
+ remoteMarkdownSafe: true,
285
+ },
286
+ evidenceRoot,
287
+ limitations: [
288
+ suite.nonAdocNegativeTestUri
289
+ ? 'non-adoc dedicated resource was checked'
290
+ : 'no dedicated non-adoc Live resource; deterministic contract 49 remains the negative type proof',
291
+ 'no duplicate-row resource was created; uniqueness was enforced against the authorized key/scope',
292
+ 'no control-state or credential marker was written remotely; absence was scanned and contract 49 retains injection coverage',
293
+ ],
294
+ };
295
+ writeJson(join(evidenceRoot, 'assertions.json'), assertions);
296
+ return {
297
+ $schema: 'dingtalk-agent/remote-semantic-state-live-report@1',
298
+ passed: true,
299
+ runId,
300
+ evidenceRoot,
301
+ plan: { ...plan, sideEffect: 'authorized-live-probes', remoteRead: true },
302
+ writes: assertions.writes,
303
+ coldStart,
304
+ failClosed,
305
+ controlState: assertions.controlState,
306
+ nextAction: second.state.nextAction,
307
+ teardown: { passed: true, retainedMarkers: true },
308
+ limitations: assertions.limitations,
309
+ };
310
+ }
311
+ catch (error) {
312
+ writeJson(join(evidenceRoot, 'failure.json'), {
313
+ $schema: 'dingtalk-agent/remote-semantic-state-live-failure@1',
314
+ runId, stage, error: error.message, writeCount, writeResults,
315
+ reconciliationRequired: writeCount > 0,
316
+ instruction: '先按固定资源独立回读;禁止盲目重试或扩大 allowlist',
317
+ });
318
+ throw error;
319
+ }
320
+ }
321
+ function readinessGaps(suite, local) {
322
+ const gaps = [];
323
+ if (suite.liveAuthorized !== 'yes')
324
+ gaps.push('LIVE_AUTHORIZED 必须为 yes');
325
+ if (containsPlaceholder(suite.provider) || containsPlaceholder(suite.write.allowlist)) {
326
+ gaps.push('Provider/allowlist 仍含占位符');
327
+ }
328
+ if (local.scopeId !== suite.provider.scopeId) {
329
+ gaps.push(`Provider scopeId 必须绑定当前 Agent scope ${local.scopeId}`);
330
+ }
331
+ if (!local.mounts.some((item) => item.slot === 'body' && item.provider === 'local-dir')) {
332
+ gaps.push('本地 Agent Definition 缺少 AGENTS.md body');
333
+ }
334
+ const skills = new Set(local.definition.skills.map((item) => item.name));
335
+ if (!skills.has('dingtalk-basic-behavior')) {
336
+ gaps.push('Definition 必须包含 dingtalk-basic-behavior');
337
+ }
338
+ const missingSkills = suite.requiredSkills.filter((item) => !skills.has(item));
339
+ if (missingSkills.length)
340
+ gaps.push(`缺少 Role Skills: ${missingSkills.join(', ')}`);
341
+ const expectedAllowlist = [
342
+ suite.provider.layers.l1.uri, suite.provider.layers.l3.uri, l2ResourceRef(suite.provider),
343
+ ].sort();
344
+ if (stableStringify([...suite.write.allowlist].sort()) !== stableStringify(expectedAllowlist)) {
345
+ gaps.push('write allowlist 必须精确等于 L1/L3/L2 三个专用资源');
346
+ }
347
+ const writes = suite.write.documentSlots.length + (suite.write.aitableFields.length ? 1 : 0) +
348
+ (suite.write.driftProbe ? 1 : 0);
349
+ if (suite.write.budget !== writes)
350
+ gaps.push(`write budget 必须精确为 ${writes}`);
351
+ return gaps;
352
+ }
353
+ function validateSpec(value) {
354
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
355
+ throw new Error('Phase 11B suite 必须是 JSON object');
356
+ }
357
+ const input = value;
358
+ assertKeys(input, [
359
+ '$schema', 'id', 'scenario', 'liveAuthorized', 'provider', 'requiredSkills',
360
+ 'evidenceRoot', 'write', 'nonAdocNegativeTestUri',
361
+ ], 'suite');
362
+ if (input.$schema !== 'dingtalk-agent/remote-semantic-state-live-eval@1' ||
363
+ !/^[a-z0-9][a-z0-9._-]*$/.test(clean(input.id)) ||
364
+ input.scenario !== 'memory-and-storage' ||
365
+ !['yes', 'no'].includes(input.liveAuthorized)) {
366
+ throw new Error('Phase 11B suite schema/id/scenario/liveAuthorized 非法');
367
+ }
368
+ planRemoteSemanticState(input.provider);
369
+ assertNoSecretKeys(input);
370
+ const requiredSkills = stringList(input.requiredSkills, 'requiredSkills', true);
371
+ if (!requiredSkills.includes('dingtalk-basic-behavior') ||
372
+ !requiredSkills.some((item) => item !== 'dingtalk-basic-behavior')) {
373
+ throw new Error('Phase 11B requiredSkills 必须包含 Basic 和至少一个 Role Skill');
374
+ }
375
+ if (!clean(input.evidenceRoot) || resolve(input.evidenceRoot) === resolve('.')) {
376
+ throw new Error('Phase 11B evidenceRoot 非法');
377
+ }
378
+ assertKeys(input.write, [
379
+ 'allowlist', 'budget', 'documentSlots', 'aitableFields', 'driftProbe', 'retainMarkers',
380
+ ], 'write');
381
+ const allowlist = stringList(input.write?.allowlist, 'write.allowlist', true);
382
+ const documentSlots = stringList(input.write?.documentSlots, 'write.documentSlots', true);
383
+ if (documentSlots.length !== 2 || !documentSlots.includes('l1') || !documentSlots.includes('l3')) {
384
+ throw new Error('Phase 11B documentSlots 必须精确包含 l1 和 l3');
385
+ }
386
+ const aitableFields = stringList(input.write?.aitableFields, 'write.aitableFields', true);
387
+ if (aitableFields.some((item) => !['summary', 'nextAction'].includes(item)) ||
388
+ new Set(aitableFields).size !== aitableFields.length) {
389
+ throw new Error('Phase 11B aitableFields 只允许 summary/nextAction 且不能重复');
390
+ }
391
+ if (!Number.isInteger(input.write?.budget) || input.write.budget < 1 ||
392
+ input.write.budget > 10 || input.write?.driftProbe !== true ||
393
+ input.write?.retainMarkers !== true) {
394
+ throw new Error('Phase 11B write 必须固定正整数预算、启用 driftProbe 并保留 marker');
395
+ }
396
+ if (input.nonAdocNegativeTestUri !== undefined && !clean(input.nonAdocNegativeTestUri)) {
397
+ throw new Error('Phase 11B nonAdocNegativeTestUri 不能为空');
398
+ }
399
+ return JSON.parse(JSON.stringify({
400
+ ...input, requiredSkills, write: {
401
+ ...input.write, allowlist, documentSlots, aitableFields,
402
+ },
403
+ }));
404
+ }
405
+ function verifyTableShape(base, tableResult, provider, writable) {
406
+ const l2 = provider.layers.l2;
407
+ const baseTables = findArray(base, 'tables');
408
+ if (!baseTables.length || !baseTables.some((item) => clean(item?.tableId) === l2.tableId)) {
409
+ throw new Error('Phase 11B Base 回读不包含声明的 tableId');
410
+ }
411
+ const tables = findArray(tableResult, 'tables');
412
+ const table = tables.find((item) => clean(item?.tableId) === l2.tableId);
413
+ if (!table)
414
+ throw new Error('Phase 11B table get 未精确返回声明的 tableId');
415
+ const fields = Array.isArray(table.fields) ? table.fields : [];
416
+ const ids = new Set(fields.map((item) => clean(item?.fieldId)));
417
+ const missing = Object.entries(l2.fields).filter(([, id]) => !ids.has(id)).map(([name]) => name);
418
+ if (missing.length)
419
+ throw new Error(`Phase 11B table 缺少 fieldId: ${missing.join(', ')}`);
420
+ for (const name of writable) {
421
+ const field = fields.find((item) => clean(item?.fieldId) === l2.fields[name]);
422
+ const type = clean(field?.type).toLowerCase();
423
+ if (['formula', 'creator', 'lastmodifier', 'createdtime', 'lastmodifiedtime'].includes(type)) {
424
+ throw new Error(`Phase 11B ${name} 不能绑定只读 field type=${type}`);
425
+ }
426
+ }
427
+ }
428
+ function queryUniqueRecord(provider) {
429
+ const l2 = provider.layers.l2;
430
+ const rows = dws.queryAiTableRecords({
431
+ baseId: l2.baseId,
432
+ tableId: l2.tableId,
433
+ filters: {
434
+ operator: 'and',
435
+ operands: [
436
+ { operator: 'eq', operands: [l2.fields.key, l2.expected.key] },
437
+ { operator: 'eq', operands: [l2.fields.scopeId, l2.expected.scopeId] },
438
+ ],
439
+ },
440
+ profile: provider.authority.profile,
441
+ });
442
+ if (rows.length !== 1 || clean(rows[0]?.recordId) !== l2.recordId) {
443
+ throw new Error(`Phase 11B L2 key+scope 必须唯一命中声明 recordId;实际 count=${rows.length}`);
444
+ }
445
+ return { rows, state: exactStructuredState(rows, provider) };
446
+ }
447
+ function exactStructuredState(rows, provider) {
448
+ const l2 = provider.layers.l2;
449
+ if (rows.length !== 1 || clean(rows[0]?.recordId) !== l2.recordId) {
450
+ throw new Error(`Phase 11B L2 recordId=${l2.recordId} 必须精确回读一条记录`);
451
+ }
452
+ const cells = rows[0]?.cells;
453
+ if (!cells || typeof cells !== 'object' || Array.isArray(cells)) {
454
+ throw new Error('Phase 11B L2 回读缺少 cells');
455
+ }
456
+ const state = {
457
+ recordId: l2.recordId,
458
+ key: textCell(cells[l2.fields.key]),
459
+ scopeId: textCell(cells[l2.fields.scopeId]),
460
+ status: textCell(cells[l2.fields.status]),
461
+ nextAction: textCell(cells[l2.fields.nextAction]),
462
+ summary: textCell(cells[l2.fields.summary]),
463
+ sourceRefs: textCell(cells[l2.fields.sourceRefs]),
464
+ };
465
+ if (state.key !== l2.expected.key || state.scopeId !== l2.expected.scopeId) {
466
+ throw new Error('Phase 11B L2 recordId/key/scopeId 绑定不一致');
467
+ }
468
+ for (const key of ['status', 'nextAction', 'summary', 'sourceRefs']) {
469
+ if (!state[key])
470
+ throw new Error(`Phase 11B L2 缺少 ${key}`);
471
+ }
472
+ return state;
473
+ }
474
+ function independentReadback(provider) {
475
+ const documents = {};
476
+ for (const slot of ['l1', 'l3']) {
477
+ const layer = provider.layers[slot];
478
+ const info = dws.docInfo(nodeRef(layer.uri), provider.authority.profile);
479
+ assertAdoc(slot, info);
480
+ const body = String(dws.readDoc(nodeRef(layer.uri), provider.authority.profile));
481
+ const forbidden = forbiddenRemoteFields(body);
482
+ if (forbidden.length) {
483
+ throw new Error(`Phase 11B ${slot} 远端 Markdown 含控制状态/凭据: ${forbidden.join(', ')}`);
484
+ }
485
+ documents[slot] = { info, body, hash: digest(body), forbidden };
486
+ }
487
+ const unique = queryUniqueRecord(provider);
488
+ return {
489
+ documents,
490
+ l2: {
491
+ state: unique.state,
492
+ hash: digest(stableStringify(unique.state)),
493
+ uniqueCount: unique.rows.length,
494
+ },
495
+ };
496
+ }
497
+ function compareColdStart(baseline, first, second, readback) {
498
+ const fields = [
499
+ 'definitionHash', 'providerHash', 'memoryHash', 'structuredStateHash',
500
+ 'knowledgeHash', 'stateHash', 'nextAction',
501
+ ];
502
+ const comparisons = Object.fromEntries(fields.map((field) => [field, {
503
+ first: first.state[field], second: second.state[field],
504
+ matched: first.state[field] === second.state[field],
505
+ }]));
506
+ const remoteMatched = first.state.memoryHash === readback.documents.l1.hash &&
507
+ first.state.knowledgeHash === readback.documents.l3.hash &&
508
+ first.state.structuredStateHash === readback.l2.hash;
509
+ return {
510
+ passed: baseline.state.definitionHash === first.state.definitionHash &&
511
+ baseline.state.providerHash === first.state.providerHash &&
512
+ first.pid !== second.pid && fields.every((field) => comparisons[field].matched) && remoteMatched,
513
+ baselinePid: baseline.pid,
514
+ firstPid: first.pid,
515
+ secondPid: second.pid,
516
+ distinctProcesses: first.pid !== second.pid,
517
+ freshStateDir: true,
518
+ baselineDefinitionStable: baseline.state.definitionHash === first.state.definitionHash,
519
+ baselineProviderStable: baseline.state.providerHash === first.state.providerHash,
520
+ comparisons,
521
+ independentRemoteHashesMatched: remoteMatched,
522
+ };
523
+ }
524
+ function runLocalFailClosedChecks(workspace, activeStateDir, second, provider) {
525
+ const manifestPath = second.state.manifestPath;
526
+ const originalManifest = readFileSync(manifestPath, 'utf8');
527
+ const manifest = JSON.parse(originalManifest);
528
+ manifest.layers.l1.cachePath = manifest.layers.l3.cachePath;
529
+ writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n');
530
+ let slotError = '';
531
+ try {
532
+ verifyRemoteSemanticStateCache(workspace, second.bootstrap, provider);
533
+ }
534
+ catch (error) {
535
+ slotError = error.message;
536
+ }
537
+ writeFileSync(manifestPath, originalManifest);
538
+ const memoryMount = second.bootstrap.mounts.find((item) => item.slot === 'memory');
539
+ if (!memoryMount)
540
+ throw new Error('Phase 11B 第二进程缺少 memory mount');
541
+ const originalCache = readFileSync(memoryMount.path, 'utf8');
542
+ writeFileSync(memoryMount.path, `${originalCache}\nDTA_PHASE_11B_LOCAL_TAMPER\n`);
543
+ let cacheError = '';
544
+ try {
545
+ verifyRemoteSemanticStateCache(workspace, second.bootstrap, provider);
546
+ }
547
+ catch (error) {
548
+ cacheError = error.message;
549
+ }
550
+ writeFileSync(memoryMount.path, originalCache);
551
+ const restored = verifyRemoteSemanticStateCache(workspace, second.bootstrap, provider);
552
+ return {
553
+ slotSwap: { required: true, passed: slotError.includes('slot/source/cachePath 串线'), error: slotError },
554
+ cacheTamper: { required: true, passed: cacheError.includes('mount/cache hash 不一致'), error: cacheError },
555
+ restoredCache: {
556
+ required: true, passed: restored.stateHash === second.state.stateHash,
557
+ activeStateDir,
558
+ },
559
+ };
560
+ }
561
+ function runLiveReadFailClosedChecks(evidenceRoot, workspace, suite, drift, second) {
562
+ const provider = suite.provider;
563
+ const mutate = (patch) => {
564
+ const value = JSON.parse(JSON.stringify(provider));
565
+ patch(value);
566
+ return value;
567
+ };
568
+ const identity = invokeWorker(evidenceRoot, 'negative-identity', {
569
+ action: 'bootstrap-and-rehydrate', workspace,
570
+ stateDir: join(evidenceRoot, 'state', 'negative-identity'),
571
+ provider: mutate((value) => { value.authority.expectedUserId += '-mismatch'; }),
572
+ });
573
+ const recordProvider = mutate((value) => { value.layers.l2.recordId += '-mismatch'; });
574
+ let recordError = '';
575
+ try {
576
+ verifyRemoteSemanticStateCache(workspace, second.bootstrap, recordProvider);
577
+ }
578
+ catch (error) {
579
+ recordError = error.message;
580
+ }
581
+ const key = invokeWorker(evidenceRoot, 'negative-key', {
582
+ action: 'bootstrap-and-rehydrate', workspace,
583
+ stateDir: join(evidenceRoot, 'state', 'negative-key'),
584
+ provider: mutate((value) => { value.layers.l2.expected.key += '-mismatch'; }),
585
+ });
586
+ const scope = invokeWorker(evidenceRoot, 'negative-scope', {
587
+ action: 'bootstrap-and-rehydrate', workspace,
588
+ stateDir: join(evidenceRoot, 'state', 'negative-scope'),
589
+ provider: mutate((value) => {
590
+ value.scopeId += '-mismatch';
591
+ value.layers.l2.expected.scopeId = value.scopeId;
592
+ }),
593
+ });
594
+ let nonAdoc;
595
+ if (suite.nonAdocNegativeTestUri) {
596
+ const info = dws.docInfo(nodeRef(suite.nonAdocNegativeTestUri), provider.authority.profile);
597
+ const rejected = String(info.contentType || '').toUpperCase() !== 'ALIDOC' ||
598
+ String(info.extension || '').toLowerCase() !== 'adoc';
599
+ nonAdoc = { required: true, provided: true, passed: rejected, info };
600
+ }
601
+ else {
602
+ nonAdoc = {
603
+ required: false, provided: false, passed: true,
604
+ proof: 'Live resource not provided; deterministic contract 49 retains non-adoc rejection',
605
+ };
606
+ }
607
+ return {
608
+ identityMismatch: {
609
+ required: true, passed: identity.status !== 0 && identity.stderr.includes('身份不符'),
610
+ error: identity.stderr.trim(),
611
+ },
612
+ recordIdMismatch: {
613
+ required: true,
614
+ passed: recordError.includes('manifest Provider/Definition/scope 绑定不一致'),
615
+ error: recordError,
616
+ proof: 'local manifest binding mutation; no unlisted recordId was requested from DWS',
617
+ },
618
+ keyMismatch: {
619
+ required: true, passed: key.status !== 0 && key.stderr.includes('recordId/key/scopeId 绑定不一致'),
620
+ error: key.stderr.trim(),
621
+ },
622
+ scopeMismatch: {
623
+ required: true, passed: scope.status !== 0 && scope.stderr.includes('Definition/authority/storage/scope'),
624
+ error: scope.stderr.trim(),
625
+ },
626
+ remoteDrift: { required: true, ...drift },
627
+ nonAdoc,
628
+ duplicateKeyScope: {
629
+ required: true, passed: true,
630
+ proof: 'authorized key+scope query returned exactly one declared recordId before and after write',
631
+ },
632
+ forbiddenRemoteControlState: {
633
+ required: true, passed: true, liveInjected: false,
634
+ proof: 'both authorized documents scanned clean; deterministic contract 49 retains injection rejection',
635
+ },
636
+ };
637
+ }
638
+ function invokeWorker(evidenceRoot, name, input) {
639
+ const inputPath = join(evidenceRoot, 'bootstrap', `${name}.input.json`);
640
+ writeJson(inputPath, input);
641
+ const workerPath = fileURLToPath(new URL('./remote-semantic-state-worker.js', import.meta.url));
642
+ const run = spawnSync(process.execPath, [workerPath, inputPath], {
643
+ encoding: 'utf8', timeout: 300_000, maxBuffer: 64 * 1024 * 1024,
644
+ });
645
+ writeFile(join(evidenceRoot, 'bootstrap', `${name}.stdout.json`), run.stdout || '');
646
+ writeFile(join(evidenceRoot, 'bootstrap', `${name}.stderr.log`), run.stderr || '');
647
+ let result = null;
648
+ if (run.status === 0 && run.stdout) {
649
+ try {
650
+ result = JSON.parse(run.stdout);
651
+ }
652
+ catch {
653
+ result = null;
654
+ }
655
+ }
656
+ return { status: run.status, stderr: run.stderr || '', result };
657
+ }
658
+ function requireWorker(invocation) {
659
+ if (invocation.status !== 0 || !invocation.result) {
660
+ throw new Error(`Phase 11B worker 失败: ${invocation.stderr.trim() || 'invalid output'}`);
661
+ }
662
+ return invocation.result;
663
+ }
664
+ function appendDocumentMarker(evidenceRoot, label, uri, marker, profile) {
665
+ const path = join(evidenceRoot, 'write-intent', `${label}.md`);
666
+ writeFile(path, `\n\nPhase 11B semantic state validation marker: ${marker}\n`);
667
+ return Boolean(dws.appendDoc(nodeRef(uri), path, profile));
668
+ }
669
+ function appendTextMarker(value, marker) {
670
+ return `${value.trimEnd()}\nPhase 11B semantic state validation marker: ${marker}`;
671
+ }
672
+ function assertAdoc(slot, info) {
673
+ const contentType = String(info?.contentType || '').toUpperCase();
674
+ const extension = String(info?.extension || '').toLowerCase();
675
+ if (contentType !== 'ALIDOC' || extension !== 'adoc') {
676
+ throw new Error(`Phase 11B ${slot} 只接受在线文档 adoc;` +
677
+ `实际 contentType=${contentType || '?'} extension=${extension || '?'}`);
678
+ }
679
+ }
680
+ function assertRemoteMarkdownSafe(slot, body) {
681
+ const found = forbiddenRemoteFields(body);
682
+ if (found.length) {
683
+ throw new Error(`Phase 11B ${slot} 远端 Markdown 含控制状态/凭据: ${found.join(', ')}`);
684
+ }
685
+ }
686
+ function forbiddenRemoteFields(body) {
687
+ return FORBIDDEN_REMOTE_PATTERNS.filter(([, pattern]) => pattern.test(body)).map(([name]) => name);
688
+ }
689
+ function semanticMarkerCount(body, marker) {
690
+ const normalizedBody = dws.words(body);
691
+ const normalizedMarker = dws.words(marker);
692
+ if (!normalizedMarker)
693
+ return 0;
694
+ return normalizedBody.split(normalizedMarker).length - 1;
695
+ }
696
+ function nodeRef(uri) {
697
+ return uri.startsWith('dingtalk-doc:') ? uri.slice('dingtalk-doc:'.length) : uri;
698
+ }
699
+ function l2ResourceRef(provider) {
700
+ const l2 = provider.layers.l2;
701
+ return `dingtalk-aitable:${l2.baseId}/${l2.tableId}/${l2.recordId}`;
702
+ }
703
+ function redactProvider(provider) {
704
+ const ref = (value) => ({ hash: digest(value), redacted: true });
705
+ return {
706
+ $schema: provider.$schema,
707
+ id: provider.id,
708
+ scopeIdHash: digest(provider.scopeId),
709
+ authority: { profileHash: digest(provider.authority.profile), expectedUserIdHash: digest(provider.authority.expectedUserId) },
710
+ layers: {
711
+ l1: { provider: 'dingtalk-doc', mode: provider.layers.l1.mode, uri: ref(provider.layers.l1.uri) },
712
+ l2: {
713
+ provider: 'dingtalk-aitable', mode: provider.layers.l2.mode,
714
+ baseId: ref(provider.layers.l2.baseId), tableId: ref(provider.layers.l2.tableId),
715
+ recordId: ref(provider.layers.l2.recordId),
716
+ expected: { keyHash: digest(provider.layers.l2.expected.key), scopeIdHash: digest(provider.layers.l2.expected.scopeId) },
717
+ fields: Object.fromEntries(Object.entries(provider.layers.l2.fields).map(([name, id]) => [name, ref(id)])),
718
+ },
719
+ l3: { provider: 'dingtalk-doc', mode: provider.layers.l3.mode, uri: ref(provider.layers.l3.uri) },
720
+ },
721
+ controlState: provider.controlState,
722
+ };
723
+ }
724
+ function safeEvidenceRoot(workspace, input, runId) {
725
+ if (resolve(input) === resolve('.') || input.split(/[\\/]/).includes('..')) {
726
+ throw new Error('Phase 11B evidenceRoot 非法');
727
+ }
728
+ const base = resolve(workspace, input);
729
+ assertInside(workspace, base, 'evidenceRoot');
730
+ assertNoSymlinkAncestors(workspace, base, 'evidenceRoot');
731
+ const rel = relative(workspace, base).split('\\').join('/');
732
+ if (!rel.startsWith('.dingtalk-agent/')) {
733
+ throw new Error('Phase 11B 原始证据必须位于 Workspace 的 gitignored .dingtalk-agent/ 下');
734
+ }
735
+ if (existsSync(base) && lstatSync(base).isSymbolicLink()) {
736
+ throw new Error('Phase 11B evidenceRoot 不能是 symlink');
737
+ }
738
+ const path = join(base, runId);
739
+ if (existsSync(path))
740
+ throw new Error('Phase 11B evidence run directory 已存在');
741
+ return path;
742
+ }
743
+ function safeExistingDirectory(root, input, label) {
744
+ const candidate = resolve(root, input);
745
+ if (lstatSync(candidate).isSymbolicLink())
746
+ throw new Error(`Phase 11B ${label} 不能是 symlink`);
747
+ const path = realpathSync(candidate);
748
+ assertInside(realpathSync(resolve(root)), path, label);
749
+ if (!lstatSync(path).isDirectory() || lstatSync(path).isSymbolicLink()) {
750
+ throw new Error(`Phase 11B ${label} 必须是普通目录`);
751
+ }
752
+ return path;
753
+ }
754
+ function safeExistingFile(root, input, label) {
755
+ const candidate = resolve(root, input);
756
+ if (lstatSync(candidate).isSymbolicLink())
757
+ throw new Error(`Phase 11B ${label} 不能是 symlink`);
758
+ const path = realpathSync(candidate);
759
+ assertInside(realpathSync(resolve(root)), path, label);
760
+ if (!lstatSync(path).isFile() || lstatSync(path).isSymbolicLink()) {
761
+ throw new Error(`Phase 11B ${label} 必须是普通文件`);
762
+ }
763
+ return path;
764
+ }
765
+ function assertInside(root, path, label) {
766
+ const rel = relative(resolve(root), resolve(path));
767
+ if (rel === '..' || rel.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`)) {
768
+ throw new Error(`Phase 11B ${label} 越界`);
769
+ }
770
+ }
771
+ function assertNoSymlinkAncestors(root, path, label) {
772
+ const rel = relative(resolve(root), resolve(path));
773
+ let current = resolve(root);
774
+ for (const part of rel.split(/[\\/]/).filter(Boolean)) {
775
+ current = join(current, part);
776
+ if (!existsSync(current))
777
+ break;
778
+ if (lstatSync(current).isSymbolicLink()) {
779
+ throw new Error(`Phase 11B ${label} 不能穿过 symlink`);
780
+ }
781
+ }
782
+ }
783
+ function findArray(value, key) {
784
+ if (!value || typeof value !== 'object')
785
+ return [];
786
+ if (Array.isArray(value)) {
787
+ for (const item of value) {
788
+ const found = findArray(item, key);
789
+ if (found.length)
790
+ return found;
791
+ }
792
+ return [];
793
+ }
794
+ const input = value;
795
+ if (Array.isArray(input[key]))
796
+ return input[key];
797
+ for (const item of Object.values(input)) {
798
+ const found = findArray(item, key);
799
+ if (found.length)
800
+ return found;
801
+ }
802
+ return [];
803
+ }
804
+ function findValue(value, keys) {
805
+ if (!value || typeof value !== 'object')
806
+ return undefined;
807
+ if (Array.isArray(value)) {
808
+ for (const item of value) {
809
+ const found = findValue(item, keys);
810
+ if (found !== undefined)
811
+ return found;
812
+ }
813
+ return undefined;
814
+ }
815
+ const input = value;
816
+ for (const key of keys)
817
+ if (input[key] !== undefined)
818
+ return input[key];
819
+ for (const item of Object.values(input)) {
820
+ const found = findValue(item, keys);
821
+ if (found !== undefined)
822
+ return found;
823
+ }
824
+ return undefined;
825
+ }
826
+ function textCell(value) {
827
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
828
+ const input = value;
829
+ return clean(input.name ?? input.text ?? input.value);
830
+ }
831
+ return clean(value);
832
+ }
833
+ function assertNoSecretKeys(value, path = 'suite') {
834
+ if (!value || typeof value !== 'object')
835
+ return;
836
+ if (Array.isArray(value)) {
837
+ value.forEach((item, index) => assertNoSecretKeys(item, `${path}[${index}]`));
838
+ return;
839
+ }
840
+ for (const [key, item] of Object.entries(value)) {
841
+ if (SECRET_KEY.test(key))
842
+ throw new Error(`Phase 11B suite 禁止凭据字段: ${path}.${key}`);
843
+ assertNoSecretKeys(item, `${path}.${key}`);
844
+ }
845
+ }
846
+ function containsPlaceholder(value) {
847
+ if (typeof value === 'string')
848
+ return PLACEHOLDER.test(value);
849
+ if (Array.isArray(value))
850
+ return value.some(containsPlaceholder);
851
+ if (value && typeof value === 'object')
852
+ return Object.values(value).some(containsPlaceholder);
853
+ return false;
854
+ }
855
+ function assertKeys(value, allowed, label) {
856
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
857
+ throw new Error(`Phase 11B ${label} 必须是 JSON object`);
858
+ }
859
+ const extra = Object.keys(value).filter((key) => !allowed.includes(key));
860
+ if (extra.length)
861
+ throw new Error(`Phase 11B ${label} 含未知字段: ${extra.join(', ')}`);
862
+ }
863
+ function stringList(value, label, nonEmpty = false) {
864
+ if (!Array.isArray(value) || value.some((item) => !clean(item))) {
865
+ throw new Error(`Phase 11B ${label} 必须是非空字符串数组`);
866
+ }
867
+ const out = value.map((item) => clean(item));
868
+ if (nonEmpty && !out.length)
869
+ throw new Error(`Phase 11B ${label} 不能为空`);
870
+ if (new Set(out).size !== out.length)
871
+ throw new Error(`Phase 11B ${label} 不能重复`);
872
+ return out;
873
+ }
874
+ function readJson(path) {
875
+ return JSON.parse(readFileSync(path, 'utf8'));
876
+ }
877
+ function writeJson(path, value) {
878
+ mkdirSync(dirname(path), { recursive: true });
879
+ writeFileSync(path, JSON.stringify(value, null, 2) + '\n');
880
+ }
881
+ function writeFile(path, value) {
882
+ mkdirSync(dirname(path), { recursive: true });
883
+ writeFileSync(path, value);
884
+ }
885
+ function clean(value) {
886
+ return String(value ?? '').trim();
887
+ }
888
+ //# sourceMappingURL=remote-semantic-state-live-evals.js.map