@agentunion/fastaun 0.5.24 → 0.5.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/_packed_docs/CHANGELOG.md +29 -0
  3. package/_packed_docs/TS/346/263/250/345/206/214/350/207/252/345/212/250/347/273/221/345/256/232Owner/347/232/204E2E/351/252/214/350/257/201/344/273/243/347/240/201.md +196 -0
  4. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +2 -2
  5. package/_packed_docs/sdk/0.5.24-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +401 -0
  6. package/_packed_docs/sdk/0.5.24-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +354 -0
  7. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +4 -0
  8. package/_packed_docs/sdk/INDEX.md +9 -0
  9. package/dist/agent-md.d.ts +1 -0
  10. package/dist/agent-md.js +38 -10
  11. package/dist/agent-md.js.map +1 -1
  12. package/dist/auth.d.ts +1 -0
  13. package/dist/auth.js +44 -10
  14. package/dist/auth.js.map +1 -1
  15. package/dist/client/delivery.d.ts +3 -0
  16. package/dist/client/delivery.js +197 -47
  17. package/dist/client/delivery.js.map +1 -1
  18. package/dist/client/rpc-pipeline.js +10 -0
  19. package/dist/client/rpc-pipeline.js.map +1 -1
  20. package/dist/client/v2-e2ee.js +17 -5
  21. package/dist/client/v2-e2ee.js.map +1 -1
  22. package/dist/client.d.ts +1 -0
  23. package/dist/client.js +43 -25
  24. package/dist/client.js.map +1 -1
  25. package/dist/group-fs.js +5 -0
  26. package/dist/group-fs.js.map +1 -1
  27. package/dist/ownership-v21.d.ts +2 -0
  28. package/dist/ownership-v21.js +61 -30
  29. package/dist/ownership-v21.js.map +1 -1
  30. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  31. package/dist/version.d.ts +1 -1
  32. package/dist/version.js +1 -1
  33. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,35 @@
6
6
 
7
7
  ---
8
8
 
9
+ ## 0.5.25 — 未发布
10
+
11
+ > 基线:已提交的 `0.5.24`(`47c59687`);本节整理截至 2026-10-25 工作区的 Node.js SDK 增量。
12
+
13
+ ### 修复
14
+
15
+ #### 消息拉取与实时交付
16
+
17
+ - Pull 成功但水位无进展、且没有待 ACK 或恢复工作时暂停后台重试,等待新 Push/hint 或显式拉取;保留在途更高 head 和未知上界通知,避免连续空 Pull。
18
+ - 已被耐久游标和服务端 ACK 覆盖的持久 Push 不再触发重复补拉;已覆盖的群 unread hint 只补未完成的 ACK。达到已知服务端 head 后,即使返回满页也停止续拉;仅同时缺少 `has_more` 和 head 的旧响应保留满页续拉兼容。
19
+ - 完整 P2P volatile Push 直接解密、交付并 ACK,减少不必要的 Pull 和持久游标初始化;继续检查发送者、接收者、设备、slot 和身份代际。
20
+ - 本人 `group.unblock` 成功后恢复目标群消息与事件拉取,隔离解除前迟到的 blocked 响应,避免旧失败重新停止已恢复的群。
21
+ - 仅从没有普通消息消费者变为首个消费者时刷新 peer 目录;追加消息监听仍唤醒已有拉取需求,不重复查询目录。
22
+
23
+ #### Ownership 与 agent.md
24
+
25
+ - Ownership 先读取权威 `storage.head_object`,SHA-256 与 revision 未变时复用已校验正文;相同文档及证书材料复用静态验签结果,仍核对当前证书、用途、有效期和吊销状态。
26
+ - 合并相同 Ownership 证书取证并短期复用有效证据,缓存按 Gateway、AID、指纹、用途、信任根和 epoch 隔离,并受 OCSP 与当前状态观察窗口约束。
27
+ - 同一观察版本的 `agent.md` 自动下载失败后退避 60 秒并合并请求;新观察和显式下载可立即重试,旧版本、旧目录或旧身份的迟到结果不覆盖当前缓存与退避状态。
28
+ - Gateway 候选按规范化 issuer domain 分别缓存,外域发现不覆盖本域连接地址;切换身份或关闭客户端时清理缓存,外域 Name Service 回退不误用本域配置。
29
+
30
+ #### GroupFS 错误兼容
31
+
32
+ - GroupFS 下载完整性校验失败时,将缓存层 `EHASH` 映射为既有 `ECONFLICT`,保持群文件 API 的错误契约;Storage API 继续返回 `EHASH`。
33
+
34
+ ### 版本统一
35
+
36
+ - 包配置、锁文件、运行时 `VERSION` 和 V2 envelope 的 `sdk_version` 统一为 `0.5.25`。
37
+
9
38
  ## 0.5.24 — 未发布
10
39
 
11
40
  > 基线:npm 已发布的 `@agentunion/fastaun@0.5.23` 编译产物;本节整理截至 2026-10-24 工作区的 SDK 增量。
@@ -6,6 +6,35 @@
6
6
 
7
7
  ---
8
8
 
9
+ ## 0.5.25 — 未发布
10
+
11
+ > 基线:已提交的 `0.5.24`(`47c59687`);本节整理截至 2026-10-25 工作区的 Node.js SDK 增量。
12
+
13
+ ### 修复
14
+
15
+ #### 消息拉取与实时交付
16
+
17
+ - Pull 成功但水位无进展、且没有待 ACK 或恢复工作时暂停后台重试,等待新 Push/hint 或显式拉取;保留在途更高 head 和未知上界通知,避免连续空 Pull。
18
+ - 已被耐久游标和服务端 ACK 覆盖的持久 Push 不再触发重复补拉;已覆盖的群 unread hint 只补未完成的 ACK。达到已知服务端 head 后,即使返回满页也停止续拉;仅同时缺少 `has_more` 和 head 的旧响应保留满页续拉兼容。
19
+ - 完整 P2P volatile Push 直接解密、交付并 ACK,减少不必要的 Pull 和持久游标初始化;继续检查发送者、接收者、设备、slot 和身份代际。
20
+ - 本人 `group.unblock` 成功后恢复目标群消息与事件拉取,隔离解除前迟到的 blocked 响应,避免旧失败重新停止已恢复的群。
21
+ - 仅从没有普通消息消费者变为首个消费者时刷新 peer 目录;追加消息监听仍唤醒已有拉取需求,不重复查询目录。
22
+
23
+ #### Ownership 与 agent.md
24
+
25
+ - Ownership 先读取权威 `storage.head_object`,SHA-256 与 revision 未变时复用已校验正文;相同文档及证书材料复用静态验签结果,仍核对当前证书、用途、有效期和吊销状态。
26
+ - 合并相同 Ownership 证书取证并短期复用有效证据,缓存按 Gateway、AID、指纹、用途、信任根和 epoch 隔离,并受 OCSP 与当前状态观察窗口约束。
27
+ - 同一观察版本的 `agent.md` 自动下载失败后退避 60 秒并合并请求;新观察和显式下载可立即重试,旧版本、旧目录或旧身份的迟到结果不覆盖当前缓存与退避状态。
28
+ - Gateway 候选按规范化 issuer domain 分别缓存,外域发现不覆盖本域连接地址;切换身份或关闭客户端时清理缓存,外域 Name Service 回退不误用本域配置。
29
+
30
+ #### GroupFS 错误兼容
31
+
32
+ - GroupFS 下载完整性校验失败时,将缓存层 `EHASH` 映射为既有 `ECONFLICT`,保持群文件 API 的错误契约;Storage API 继续返回 `EHASH`。
33
+
34
+ ### 版本统一
35
+
36
+ - 包配置、锁文件、运行时 `VERSION` 和 V2 envelope 的 `sdk_version` 统一为 `0.5.25`。
37
+
9
38
  ## 0.5.24 — 未发布
10
39
 
11
40
  > 基线:npm 已发布的 `@agentunion/fastaun@0.5.23` 编译产物;本节整理截至 2026-10-24 工作区的 SDK 增量。
@@ -0,0 +1,196 @@
1
+ # TypeScript 注册自动绑定 Owner 的 E2E 验证
2
+
3
+ 下面的脚本验证 `store.register(aid, { type: 'agent', owner })` 的完整流程:注册并保存绑定意图、连接后由 sub 自动发送 `bind.request`、owner 审批、NS 形成锁定共识,以及 Storage 保存 `agent.md` 副本。
4
+
5
+ 脚本使用 TS SDK 的编译产物,以 Node.js ESM JavaScript 运行。目标环境是 `agentid.cn` Docker;需要已运行的 `ownpull-cn-20261023-ts` 容器、当前 TS SDK 的 `dist`,以及容器中已注入的 `AUN_TEST_ENCRYPTION_SEED`。依赖预先安装,业务测试期间不下载依赖。
6
+
7
+ 每次执行会创建两个新的测试 AID,将身份材料保存在容器的独立 `/tmp/aun-e2e-*` 目录并保留。注册意图读取和最终原文验签使用 SDK 私有探针 `_tokenStore`、`_getOwnershipFiles()`,仅用于本用例的内部断言。
8
+
9
+ ## 验证代码
10
+
11
+ ```javascript
12
+ import { createHash } from 'node:crypto';
13
+ import { AIDStore, AUNClient } from './dist/index.js';
14
+
15
+ const seed = String(process.env.AUN_TEST_ENCRYPTION_SEED ?? '');
16
+ if (!seed) throw new Error('容器缺少 AUN_TEST_ENCRYPTION_SEED');
17
+ const stamp = Date.now();
18
+ const ownerAid = `e2eown${stamp}.agentid.cn`;
19
+ const subAid = `e2esub${stamp}.agentid.cn`;
20
+ const ownerPath = `/tmp/aun-e2e-owner-${stamp}`;
21
+ const subPath = `/tmp/aun-e2e-sub-${stamp}`;
22
+
23
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
24
+ const sha256 = (data) => createHash('sha256').update(data).digest('hex');
25
+
26
+ function assert(condition, message) {
27
+ if (!condition) throw new Error(message);
28
+ }
29
+
30
+ function dataOf(result, label) {
31
+ if (!result.ok || result.data === undefined) {
32
+ throw new Error(`${label}: ${result.error?.message ?? 'unknown error'}`);
33
+ }
34
+ return result.data;
35
+ }
36
+
37
+ async function withTimeout(promise, ms, label) {
38
+ let timer;
39
+ try {
40
+ return await Promise.race([
41
+ promise,
42
+ new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(`${label} timeout`)), ms); }),
43
+ ]);
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+
49
+ let ownerClient;
50
+ let subClient;
51
+ let ownerStore;
52
+ let subStore;
53
+
54
+ try {
55
+ console.log(JSON.stringify({ owner_aid: ownerAid, sub_aid: subAid, ownerPath, subPath }));
56
+ ownerStore = new AIDStore({
57
+ aunPath: ownerPath,
58
+ encryptionSeed: seed,
59
+ verifySsl: false,
60
+ });
61
+ dataOf(await ownerStore.register(ownerAid, { type: 'agent' }), 'owner register');
62
+ ownerClient = new AUNClient(dataOf(ownerStore.load(ownerAid), 'owner load').aid);
63
+
64
+ let resolveRequest;
65
+ const request = new Promise((resolve) => { resolveRequest = resolve; });
66
+
67
+ // message.received/message.batch 会使后台 P2P Pull 门禁开启;ownership.request 捕获审批任务。
68
+ for (const event of ['message.received', 'message.recalled', 'message.batch', 'ownership.request']) {
69
+ ownerClient.on(event, (payload) => {
70
+ if (event === 'ownership.request' && payload?.sub_aid === subAid && payload.owner_aid === ownerAid) {
71
+ console.log('owner 收到 ownership.request', payload.operation_id);
72
+ resolveRequest(payload);
73
+ }
74
+ });
75
+ }
76
+ await ownerClient.connect({ auto_reconnect: false, background_sync: true });
77
+
78
+ subStore = new AIDStore({
79
+ aunPath: subPath,
80
+ encryptionSeed: seed,
81
+ verifySsl: false,
82
+ });
83
+ dataOf(await subStore.register(subAid, { type: 'agent', owner: ownerAid }), 'sub register');
84
+
85
+ // registration_intent 是 SDK 本地持久状态;这里用于确认 connect 前仍是 pending。
86
+ const intent = subStore._tokenStore.loadMetadata(subAid)?.registration_intent;
87
+ assert(intent?.protocol === '2.1', 'registration intent protocol is not 2.1');
88
+ assert(intent?.owner === ownerAid && intent.pending === true, 'registration intent is not pending');
89
+ assert(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(intent.operation_id), 'registration operation_id is not UUIDv4');
90
+ console.log('注册意图已保存', intent.operation_id);
91
+
92
+ subClient = new AUNClient(dataOf(subStore.load(subAid), 'sub load').aid);
93
+ for (const event of ['message.received', 'message.recalled', 'message.batch']) {
94
+ subClient.on(event, () => {});
95
+ }
96
+ await subClient.connect({ auto_reconnect: false, background_sync: true });
97
+
98
+ const ownerRequest = await withTimeout(request, 60_000, 'ownership.request');
99
+ assert(ownerRequest.operation_id === intent.operation_id, 'bind.request operation_id differs from registration intent');
100
+ assert(ownerRequest.requester_aid === subAid, 'ownership.request requester is not the sub AID');
101
+
102
+ const accepted = await ownerClient.acceptOwnershipRequest(intent.operation_id);
103
+ assert(accepted.operation_id === intent.operation_id, 'owner acceptance operation_id differs');
104
+ assert(!['error', 'stopped', 'rejected'].includes(accepted.status), 'owner acceptance failed');
105
+ console.log('owner 已确认', accepted.operation_id);
106
+
107
+ let ownerConsensus;
108
+ let subConsensus;
109
+ for (let i = 0; i < 40; i += 1) {
110
+ try { ownerConsensus = await ownerClient.checkOwnershipConsensus(subAid); } catch { ownerConsensus = undefined; }
111
+ try { subConsensus = await subClient.checkOwnershipConsensus(subAid); } catch { subConsensus = undefined; }
112
+ console.log('共识检查', i + 1, ownerConsensus?.status, subConsensus?.status);
113
+ if (
114
+ ownerConsensus?.status === 'consensus' && ownerConsensus.lock === 'locked' && ownerConsensus.relation_version === 1
115
+ && subConsensus?.status === 'consensus' && subConsensus.lock === 'locked' && subConsensus.relation_version === 1
116
+ ) break;
117
+ await sleep(1_500);
118
+ }
119
+
120
+ assert(ownerConsensus?.status === 'consensus', 'owner did not observe consensus');
121
+ assert(subConsensus?.status === 'consensus', 'sub did not observe consensus');
122
+ assert(ownerConsensus.lock === 'locked' && subConsensus.lock === 'locked', 'ownership lock is not locked');
123
+ assert(ownerConsensus.relation_version === 1 && subConsensus.relation_version === 1, 'relation version is not 1');
124
+ assert(ownerConsensus.owner_aid === ownerAid && subConsensus.owner_aid === ownerAid, 'owner AID mismatch');
125
+ assert(ownerConsensus.sub_aid === subAid && subConsensus.sub_aid === subAid, 'sub AID mismatch');
126
+ assert(ownerConsensus.agent_md_sha256 === subConsensus.agent_md_sha256, 'agent.md observations differ');
127
+ assert(ownerConsensus.owner_md_sha256 === subConsensus.owner_md_sha256, 'owner.md observations differ');
128
+
129
+ const pending = subStore._tokenStore.loadMetadata(subAid)?.registration_intent?.pending;
130
+ assert(pending === false, 'registration intent remains pending after completion');
131
+
132
+ const files = subClient._getOwnershipFiles();
133
+ const nsAgent = await files.readAgent(subAid);
134
+ const ownerFile = await files.readOwner(ownerAid, subAid);
135
+ assert(nsAgent.exists && ownerFile.exists, 'ownership file is missing');
136
+ await files.verifyFile(nsAgent.content, 'agent', subAid);
137
+ await files.verifyFile(ownerFile.content, 'owner', subAid, ownerAid);
138
+ assert(ownerFile.sha256 === ownerConsensus.owner_md_sha256, 'Storage owner.md SHA differs from consensus');
139
+
140
+ // NS 到 Storage 的副本同步允许短暂延迟,等待副本追上已验证的共识摘要。
141
+ let storageAgentMd;
142
+ for (let i = 0; i < 40; i += 1) {
143
+ try { storageAgentMd = await subClient.storage.readBytes('/agent.md', { owner: subAid, noCache: true }); } catch { storageAgentMd = undefined; }
144
+ if (storageAgentMd && sha256(storageAgentMd) === ownerConsensus.agent_md_sha256) break;
145
+ await sleep(1_500);
146
+ }
147
+ assert(storageAgentMd, 'Storage agent.md is missing');
148
+ assert(sha256(storageAgentMd) === ownerConsensus.agent_md_sha256, 'Storage agent.md SHA differs from consensus');
149
+ assert(new TextDecoder('utf-8', { fatal: true }).decode(storageAgentMd) === nsAgent.content, 'NS/Storage agent.md bodies differ');
150
+ assert(sha256(new TextEncoder().encode(ownerFile.content)) === ownerConsensus.owner_md_sha256, 'owner.md SHA differs from consensus');
151
+
152
+ console.log(JSON.stringify({
153
+ result: 'PASS',
154
+ owner_aid: ownerAid,
155
+ sub_aid: subAid,
156
+ operation_id: intent.operation_id,
157
+ status: subConsensus.status,
158
+ lock: subConsensus.lock,
159
+ relation_version: subConsensus.relation_version,
160
+ agent_md_sha256: subConsensus.agent_md_sha256,
161
+ owner_md_sha256: subConsensus.owner_md_sha256,
162
+ storage_agent_md_matches: true,
163
+ }));
164
+ } finally {
165
+ await Promise.allSettled([
166
+ subClient?.close(),
167
+ ownerClient?.close(),
168
+ ]);
169
+ subStore?.close();
170
+ ownerStore?.close();
171
+ }
172
+ ```
173
+
174
+ ## 容器内运行
175
+
176
+ 在项目根目录 `D:\modelunion\kite` 用 PowerShell 7(UTF-8 管道编码)执行。第一条命令进行当前 SDK 的完整编译;后续命令直接提取本文的 JavaScript 代码块送入容器,不需要额外保存脚本文件。
177
+
178
+ ```powershell
179
+ docker exec -w /workspace/ts ownpull-cn-20261023-ts node --jitless node_modules/typescript/bin/tsc --pretty false
180
+ if ($LASTEXITCODE -ne 0) { throw 'TS SDK 编译失败' }
181
+ $ownerE2eDoc = Get-Content -LiteralPath 'aun-sdk-core\docs\TS注册自动绑定Owner的E2E验证代码.md' -Raw -Encoding utf8
182
+ $ownerE2eCode = [regex]::Match($ownerE2eDoc, '(?s)```javascript\r?\n(.*?)\r?\n```').Groups[1].Value
183
+ if (-not $ownerE2eCode) { throw '未找到 E2E JavaScript 代码块' }
184
+ $ownerE2eCode | docker exec -i -w /workspace/ts ownpull-cn-20261023-ts node --max-opt=0 --input-type=module
185
+ if ($LASTEXITCODE -ne 0) { throw 'E2E 验证失败' }
186
+ ```
187
+
188
+ 通过标准是进程退出码为 `0`、输出 `result: "PASS"`,并同时满足 `status: "consensus"`、`lock: "locked"`、`relation_version: 1`。`storage_agent_md_matches` 为 `true` 表示 Storage 的 `/agent.md` 正文及 SHA 与 fresh NS 观察结果一致。任一断言失败会立即退出当前用例。
189
+
190
+ 这组 Node 启动参数在当前容器会使 SDK 使用本地 transport 回退;本用例验证注册、自动绑定、审批和材料一致性,未覆盖 Worker transport。完整编译使用 `--jitless` 规避当前容器曾出现的 TypeScript 编译器内部错误;业务脚本使用 `--max-opt=0`。
191
+
192
+ 本验证不调用 `bindOwneraid()` 或其他手动绑定入口;绑定请求必须由 `connect()` 对本地 `registration_intent` 的恢复逻辑自动产生。
193
+
194
+ ## 实测结果
195
+
196
+ 2026-10-23 23:58(北京时间),直接提取本文代码在 `agentid.cn` Docker 环境运行通过,退出码为 `0`。操作 ID:`ef35d48b-243c-434e-9265-1462acf469e3`。双方返回 `consensus / locked / relation_version=1`,两文件验签和 NS/Storage 副本校验通过。
@@ -4,7 +4,7 @@
4
4
 
5
5
  普通 `agent.md` 的基础读取、验签、上传与显式 pending 恢复不以前置 Ownership 共识、owner Storage 或关系状态为条件。普通正文更新须原样保留当前原件中全部关系材料的字段存在性、类型和值,并遵守本人签名和原 ETag/CAS;材料无效或依赖不可用不能阻断这种更新。增删改关系字段继续执行当前双文件授权核验,不能借普通名片入口清除或替换 owner 锁。
6
6
 
7
- 本协议的通用关系流程适用于 `human`、`group`、`org` 以外的主体;`human` 禁止声明 owner,`group/org` 作为被管理的 subject 时使用第 1.3 节的专属生命周期,也可按普通规则作为 `owner_aid`。通用关系双方独立签名、上传自己的声明文件;本协议不定义 ownership 专用授权 RPC 或权威关系控制方法,也不提供代签名、代上传及协议层关系申请、接受、拒绝方法。SDK 使用普通加密 P2P 载荷编排,并由 sub 归属 NS 按[编排指南第 11 节](../sdk/Ownership-SDK编排指南2.1.md#11-aid-级多设备一次性决策)保存各 AID 的不可变审批决定;载荷和决定均不构成关系授权或关系提交事实,不能替代双方文件及服务端验收。可选托管由应用层实现,不改变这两个文件的签署及上传身份。
7
+ 本协议的通用关系流程适用于 `human`、`group`、`org` 以外的主体;`human` 禁止声明 owner,`group/org` 作为被管理的 subject 时使用第 1.3 节的专属生命周期,也可按普通规则作为 `owner_aid`。通用关系双方独立签名、上传自己的声明文件;本协议不定义 ownership 专用授权 RPC 或权威关系控制方法,也不提供代签名、代上传及协议层关系申请、接受、拒绝方法。SDK 可以使用普通加密 P2P 载荷进行非权威编排,也可在持有各方身份材料时直接执行本地编排;当前流程不要求独立 decisions 接口或不可变审批记录。载荷和本地调用结果均不能替代双方文件及 NS 验收,具体流程见[当前应用指南](../sdk/Ownership2.1-SDK应用集成指南.md)。可选托管由应用层实现,不改变这两个文件的签署及上传身份。
8
8
 
9
9
  兼容方向为新服务端保留旧 SDK 的既有身份认证、普通名片、通信和 legacy owner 操作入口;新 SDK 的 2.1 关系操作不兼容旧服务端。兼容旧 SDK 所需的注册、绑定、转移、查询和解绑 RPC 使用隔离的 Auth JSONL 状态及 NS `ownership_proof`,只处理 legacy 关系;它们不生成 2.1 双文件声明,不构成 2.1 授权、owner 锁、关系版本或 `consensus`,也不得覆盖已存现代声明。Ownership 2.1 当前关系始终重新核对两份当前文件;不把新 SDK 源码调用形态兼容作为本契约要求。
10
10
 
@@ -663,7 +663,7 @@ self-owner 验证通过同样返回 `consensus`,仅证明该 AID 的自主管
663
663
 
664
664
  执行端按自身业务审计要求记录授权与执行结果;关系授权时应校验两份当前完整签名文件、owner_aid 身份及证书和本次验签结果。审计记录只描述历史操作,不参与当前关系判断;普通 `agent.md` 读取和身份验签不依赖 Ownership 审计数据。
665
665
 
666
- NS 不持久化独立关系事实、释放状态或关系版本上界。关系材料保存在当前 `agent.md` 及其发布 ETag;owner 身份和声明依据 owner_aid 对应的证书及 Storage 当前 `owner.md`。SDK 多设备编排另存 operation 上下文及不可变决定,仅用于防止重复决策。无法从这两份当前材料确证关系或版本时,关系授权及关系变更 fail-closed,但不影响符合普通规则的 `agent.md` 读取、验签和使用。决策记录、同步待办和应用审计均不能作为第三个关系事实来源。
666
+ NS 不持久化独立关系事实、释放状态或关系版本上界。关系材料保存在当前 `agent.md` 及其发布 ETag;owner 身份和声明依据 owner_aid 对应的证书及 Storage 当前 `owner.md`。SDK 不持久保存 Ownership 任务、WAL 或审批决定。无法从这两份当前材料确证关系或版本时,关系授权及关系变更 fail-closed,但不影响符合普通规则的 `agent.md` 读取、验签和使用。同步待办和应用审计均不能作为第三个关系事实来源。
667
667
 
668
668
  托管为应用层显式启用的可选能力,不改变各方签署及上传身份。外部 owner 若持有可解密的 sub 私钥,仍可冒用其身份,移除代理接口不能撤销该能力。关系变更不自动撤销旧凭据及其续签、恢复身份的能力;业务交接完成须另行确认凭据隔离,与 NS 关系提交是独立事实。
669
669