@agentunion/fastaun 0.5.22 → 0.5.23

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.
@@ -10,7 +10,7 @@
10
10
  | --- | --- |
11
11
  | [0.5.21 SDK 应用集成指南](sdk/0.5.21-SDK应用集成指南.md) | TypeScript 应用接入、batch 持久幂等、peer 同步、resolve/owner 展示、关系审批、凭证续期、诊断与升级验收 |
12
12
  | [0.5.21 SDK 开发与移植指南](sdk/0.5.21-SDK开发与移植指南.md) | 四端公开契约、peer 水位、流控与提交边界、frontmatter/共识缓存、原 CAS 和旧任务、checkpoint、语言差异及验收顺序 |
13
- | [0.5.22 SDK 应用集成指南](sdk/0.5.22-SDK应用集成指南.md) | TypeScript 接入、消息订阅、两种绑定与审批恢复、resolve 展示、60 秒读缓存与刷新、撤销同步和组织完整成员包 |
13
+ | [0.5.22 SDK 应用集成指南](sdk/0.5.22-SDK应用集成指南.md) / [注册自动绑定 Owner E2E](TS注册自动绑定Owner的E2E验证代码.md) | TypeScript 接入、消息订阅、两种绑定与审批恢复、resolve 展示、60 秒读缓存与刷新、撤销同步和组织完整成员包 |
14
14
  | [0.5.22 SDK 开发与移植指南](sdk/0.5.22-SDK开发与移植指南.md) | 后台退避、群状态、两种绑定与 Stage/CAS、消息内部消费、撤销同步、组织恢复及配套服务修复、默认读缓存、Rust 移植与验收 |
15
15
  | [aun测试运行指南](aun测试运行指南.md) | `agentid.pub/agentid.cn` 双域本地单实例、MySQL 命名卷/bind 核验、独立固定材料与 Tester、Go 1.24、断网 cross-SDK、缓存专项首失败续跑、三文件线上镜像替换包、开箱默认配置、一次性初始化辅助包及单节点部署 |
16
16
  | [aun公网测试运行指南](aun公网测试运行指南.md) | 可配置 issuer domain 的 Python SDK 公网 integration/E2E runner、独立身份/运行目录、TLS 与服务端公网验收衔接 |
@@ -463,3 +463,7 @@ Group/Org 作为 sub 时不进入这些通用任务或审批;以其为 sub 误
463
463
  ### AUN 离线推送 Push Service
464
464
 
465
465
  设计文档定义独立 Go Push Service 的职责、通信平面、持久状态机、在线复核、批次投递、故障恢复、安全和三中心纳管;Python `connect()` 把 `push_notify_aid/push_token` 映射为显式 `push.register`,运行期通过 PushFacade 登记或注销,认证协议本身不扩展字段。[接入与演示指南](AUN离线推送接入与演示指南.md)从终端、Push Server 两侧给出 token、长连接、幂等 ACK、Mock smoke 和排障路径。[离线推送通知协议](protocol/15-离线推送通知协议.md)给出对外字段和可靠性边界。契约冻结文档固定确定 Session、批次级 ACK、不可变 membership、candidate 汇总、恢复、Gateway ACL 和关闭顺序。v4 计划只定义范围、依赖和验收,Task TDD 在实施时展开。[运维与发布指南](AUN离线推送服务运维与发布指南.md)补充发布门、身份材料、Mock Python SDK 应用层恢复、灰度和回滚,并明确生产仍受 token 加密硬阻断。旧 Codex 文档只作为历史背景。
466
+
467
+ ### TS 注册自动绑定 Owner 的 E2E 验证代码
468
+
469
+ [代码与运行方式](TS注册自动绑定Owner的E2E验证代码.md):TS SDK 编译产物的 Node.js ESM 用例,覆盖注册意图、connect 自动发起 bind.request、owner 审批、双端锁定共识、NS/Storage 正文及 SHA;包含 agentid.cn 实测结果和私有探针/transport 范围。
@@ -34,6 +34,7 @@ AUN SDK Core 文档在 `docs/` 下。根级索引为 `docs/INDEX.md`,SDK API
34
34
  | 0.5.21 应用开发者:TypeScript 接入、消息入库、resolve/owner、凭证组织、诊断和升级 | `docs/sdk/0.5.21-SDK应用集成指南.md` 1. L7-20;2. L21-79;3. L80-175;4. L176-228;5. L229-263;6. L264-311;7. L312-334;8. L335-356;9. L357-368 |
35
35
  | 0.5.21 SDK 开发者:peer/batch 提交、元数据与共识缓存、原件恢复、语言差异和验收 | `docs/sdk/0.5.21-SDK开发与移植指南.md` 1. L7-21;2. L22-45;3. L46-96;4. L97-156;5. L157-212;6. L213-228;7. L229-265;8. L266-295;9. L296-303;10. L304-319;11. L320-332;12. L333-350 |
36
36
  | 0.5.22 应用集成:TypeScript 接线、审批恢复、resolve、缓存及凭证组织 | `docs/sdk/0.5.22-SDK应用集成指南.md` 1. L5-13;2. L14-35;3. L36-54;4. L55-104;5. L105-127;6. L128-164;7. L165-200;8. L201-212 |
37
+ | TypeScript 注册自动绑定 Owner E2E 代码 | [代码 L9-172、运行 L174-192、实测 L194-196](TS注册自动绑定Owner的E2E验证代码.md):注册意图、connect 自动 bind.request、owner 审批、NS/Storage 校验 |
37
38
  | 0.5.22 SDK 移植:同步、Stage/CAS、撤销续期、缓存、语言修复及 Rust | `docs/sdk/0.5.22-SDK开发与移植指南.md` 1. L5-8;2. L9-29;3. L30-39;4. L40-83;5. L84-118;6. L119-171;7. L172-188;8. L189-199 |
38
39
  | Ownership 2.1 agent.md/owner.md、普通名片边界、原格式与 Header | `docs/protocol/Ownership协议定义2.1.md` 1. L11-61;2. L62-202;3. L203-266;4. L267-634;5. L635-642;6. L643-669 |
39
40
  | Ownership 2.1 七 Facade、sub-first/owner-first 简化、三类 CAS 冲突、reset 与旧任务恢复 | `docs/sdk/Ownership-SDK编排指南2.1.md` 1. L9-40;2. L41-243;3. L244-277;4. L278-381;5. L382-415;6. L416-545;7. L546-717;8. L718-782;9. L783-824;10. L825-939 |
@@ -20,6 +20,11 @@ export type AgentMdVerification = {
20
20
  reason?: string;
21
21
  ownership_status?: OwnershipVerificationStatus | 'not_applicable';
22
22
  };
23
+ type AgentMdObservation = {
24
+ root: string;
25
+ etag: string;
26
+ version: number;
27
+ };
23
28
  export interface AgentMdFrontmatter {
24
29
  aid?: string;
25
30
  name?: string;
@@ -114,7 +119,13 @@ export declare class AgentMdManager {
114
119
  private _loadRecord;
115
120
  loadRecord(aid: string): Record<string, unknown> | null;
116
121
  private _saveRecord;
117
- saveRecord(aid: string, fields: Record<string, unknown>): Record<string, unknown>;
122
+ saveRecord(aid: string, fields: Record<string, unknown>, observation?: AgentMdObservation): Record<string, unknown>;
123
+ cacheOwnershipSnapshot(aid: string, snapshot: unknown, observation: {
124
+ root: string;
125
+ etag: string;
126
+ version: number;
127
+ content_hash: string;
128
+ }): boolean;
118
129
  private _clearConfirmedDraft;
119
130
  upload(content?: string | null, originalCondition?: OwnershipSavedFile['condition']): Promise<Record<string, unknown>>;
120
131
  readAgentMdFresh(aid: string): Promise<{
@@ -123,6 +134,8 @@ export declare class AgentMdManager {
123
134
  etag: string;
124
135
  }>;
125
136
  readOwnershipFresh(aid: string): Promise<OwnershipAgentObservation>;
137
+ _readOwnershipQuery(aid: string): Promise<OwnershipAgentObservation>;
138
+ private _readOwnership;
126
139
  submitOwnershipRaw(aid: string, raw: string, condition: OwnershipSavedFile['condition'], assertActive?: () => void, stage?: boolean): Promise<OwnershipAgentObservation>;
127
140
  private _ownershipRequest;
128
141
  uploadAs(targetAid: string, content: string, signer: AID): Promise<Record<string, unknown>>;
@@ -181,3 +194,4 @@ export declare class AgentMdManager {
181
194
  private _writeRecordUnlocked;
182
195
  private _readRecordUnlocked;
183
196
  }
197
+ export {};
package/dist/agent-md.js CHANGED
@@ -301,7 +301,7 @@ export class AgentMdManager {
301
301
  loadRecord(aid) {
302
302
  return this._loadRecord(aid);
303
303
  }
304
- _saveRecord(aid, fields, strict = false) {
304
+ _saveRecord(aid, fields, strict = false, observation) {
305
305
  const target = String(aid ?? '').trim();
306
306
  if (!target)
307
307
  return {};
@@ -311,6 +311,8 @@ export class AgentMdManager {
311
311
  let savedTo = '';
312
312
  const record = this._withRecordLock(target, () => {
313
313
  const next = { ...this._readRecordUnlocked(target, true), aid: target };
314
+ if (observation && (this.root !== observation.root || String(next.remote_etag ?? '') !== observation.etag || Number(next.observation_version ?? 0) !== observation.version))
315
+ throw ownershipFailure('unavailable', 'agent.md observation changed during download', true);
314
316
  if (hasContent) {
315
317
  const content = String(inputFields.content ?? '');
316
318
  savedTo = this.writeContent(target, content);
@@ -343,14 +345,31 @@ export class AgentMdManager {
343
345
  return { ...loaded };
344
346
  }
345
347
  catch (err) {
346
- if (strict)
348
+ if (strict || observation)
347
349
  throw err;
348
350
  this._log.debug(`agent.md cache save skipped: aid=${target} err=${err instanceof Error ? err.message : String(err)}`);
349
351
  return {};
350
352
  }
351
353
  }
352
- saveRecord(aid, fields) {
353
- return this._saveRecord(aid, fields, Object.prototype.hasOwnProperty.call(fields, 'local_draft_content'));
354
+ saveRecord(aid, fields, observation) {
355
+ return this._saveRecord(aid, fields, Object.prototype.hasOwnProperty.call(fields, 'local_draft_content'), observation);
356
+ }
357
+ cacheOwnershipSnapshot(aid, snapshot, observation) {
358
+ const target = this.safeAid(aid);
359
+ return this._withRecordLock(target, () => {
360
+ if (this.root !== observation.root)
361
+ return false;
362
+ const record = this._readRecordUnlocked(target, true);
363
+ const content = fs.existsSync(this.filePath(target)) ? this.readContent(target) : null;
364
+ const hash = content === null ? '' : AgentMdManager.contentEtag(content).slice(1, -1);
365
+ if (String(record.remote_etag ?? '') !== observation.etag || Number(record.observation_version ?? 0) !== observation.version || hash !== observation.content_hash)
366
+ return false;
367
+ record.ownership_snapshot = JSON.parse(JSON.stringify(snapshot));
368
+ record.updated_at = Date.now();
369
+ this._writeRecordUnlocked(target, record);
370
+ this._cache.set(target, { ...record, ...(content === null ? {} : { content, local_etag: `"${hash}"` }) });
371
+ return true;
372
+ });
354
373
  }
355
374
  _clearConfirmedDraft(aid, content) {
356
375
  this._withRecordLock(aid, () => {
@@ -407,8 +426,14 @@ export class AgentMdManager {
407
426
  return { exists: true, content: response.text, etag };
408
427
  }
409
428
  async readOwnershipFresh(aid) {
429
+ return this._readOwnership(aid, true);
430
+ }
431
+ async _readOwnershipQuery(aid) {
432
+ return this._readOwnership(aid, false);
433
+ }
434
+ async _readOwnership(aid, fresh) {
410
435
  const target = this.safeAid(aid);
411
- const response = await this._ownershipRequest(target, 'GET');
436
+ const response = fresh ? await this._ownershipRequest(target, 'GET') : await this._ownershipRequest(target, 'GET', undefined, undefined, false, undefined, undefined, false);
412
437
  if ([200, 400, 404].includes(response.status) && headerValue(response.headers, 'X-AUN-Ownership-Status') === 'not_applicable')
413
438
  throw ownershipFailure('not_applicable', 'group/org uses its lifecycle owner relationship');
414
439
  if (response.status === 400) {
@@ -470,9 +495,9 @@ export class AgentMdManager {
470
495
  throw ownershipFailure('unavailable', 'NS raw submission cannot be confirmed from the current document', true);
471
496
  return fresh;
472
497
  }
473
- async _ownershipRequest(target, method, raw, condition, legacyRead = false, assertActive, stage) {
498
+ async _ownershipRequest(target, method, raw, condition, legacyRead = false, assertActive, stage, fresh = true) {
474
499
  const gatewayUrl = await this._resolveGateway(target);
475
- const headers = { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', 'Cache-Control': 'no-cache', ...(!legacyRead ? { 'X-AUN-Ownership-Check': '1' } : {}) };
500
+ const headers = { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', ...(fresh ? { 'Cache-Control': 'no-cache' } : {}), ...(!legacyRead ? { 'X-AUN-Ownership-Check': '1' } : {}) };
476
501
  if (method === 'PUT') {
477
502
  if (stage)
478
503
  headers['X-AUN-Ownership-Stage'] = '1';
@@ -913,22 +938,25 @@ export class AgentMdManager {
913
938
  }
914
939
  async download(aid, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
915
940
  const target = this.safeAid(String(aid ?? this._ownerAid() ?? '').trim());
916
- const existing = this._downloadInflight.get(target);
941
+ const record = this.loadRecord(target);
942
+ const observation = { root: this.root, etag: String(record?.remote_etag ?? ''), version: Number(record?.observation_version ?? 0) };
943
+ const key = JSON.stringify([target, observation]);
944
+ const existing = this._downloadInflight.get(key);
917
945
  if (existing)
918
946
  return await existing;
919
947
  const task = (async () => {
920
948
  const release = await this._acquireDownloadSlot();
921
949
  try {
922
- return await this._downloadOnce(target, timeoutMs);
950
+ return await this._downloadOnce(target, timeoutMs, observation);
923
951
  }
924
952
  finally {
925
953
  release();
926
954
  }
927
955
  })();
928
- this._downloadInflight.set(target, task);
956
+ this._downloadInflight.set(key, task);
929
957
  task.finally(() => {
930
- if (this._downloadInflight.get(target) === task)
931
- this._downloadInflight.delete(target);
958
+ if (this._downloadInflight.get(key) === task)
959
+ this._downloadInflight.delete(key);
932
960
  }).catch(() => undefined);
933
961
  return await task;
934
962
  }
@@ -940,7 +968,9 @@ export class AgentMdManager {
940
968
  const remoteEtagCached = String(before.remote_etag ?? before.etag ?? '').trim();
941
969
  const lastModifiedCached = String(before.last_modified ?? '').trim();
942
970
  const checkedAtCached = Number(before.checked_at ?? before.fetched_at ?? 0) || 0;
943
- const cachedInSync = !!(localFound && localEtag && remoteEtagCached && localEtag === remoteEtagCached);
971
+ const verified = before.verified_agent_md;
972
+ const matchesRemote = (etag) => localEtag === etag || before.verify_status === 'verified' && verified?.etag === etag && verified.content_hash === localEtag;
973
+ const cachedInSync = !!(localFound && localEtag && remoteEtagCached && matchesRemote(remoteEtagCached));
944
974
  if (cachedInSync && AgentMdManager.checkedAtFresh(checkedAtCached, ttlDays)) {
945
975
  return {
946
976
  aid: target,
@@ -993,7 +1023,7 @@ export class AgentMdManager {
993
1023
  const remoteEtag = String(remote.etag ?? '').trim();
994
1024
  const lastModified = String(remote.last_modified ?? '').trim();
995
1025
  const saved = this.loadRecord(target) ?? before;
996
- const inSync = !!(localFound && remoteFound && localEtag && remoteEtag && localEtag === remoteEtag);
1026
+ const inSync = !!(localFound && remoteFound && localEtag && remoteEtag && matchesRemote(remoteEtag));
997
1027
  return {
998
1028
  aid: target,
999
1029
  local_found: localFound,
@@ -1022,7 +1052,7 @@ export class AgentMdManager {
1022
1052
  const hadRemoteVersion = Boolean(String(before.remote_etag ?? '').trim() || String(before.last_modified ?? '').trim());
1023
1053
  let record = { ...before };
1024
1054
  if (!same || Object.keys(before).length === 0) {
1025
- const fields = { observed_at: Date.now(), remote_status: 'found' };
1055
+ const fields = { observed_at: Date.now(), observation_version: Number(before.observation_version ?? 0) + 1, remote_status: 'found' };
1026
1056
  if (remoteEtag)
1027
1057
  fields.remote_etag = remoteEtag;
1028
1058
  if (remoteLastModified)
@@ -1194,7 +1224,7 @@ export class AgentMdManager {
1194
1224
  });
1195
1225
  return data;
1196
1226
  }
1197
- async _downloadOnce(target, timeoutMs) {
1227
+ async _downloadOnce(target, timeoutMs, observation) {
1198
1228
  this._log.debug(`downloadAgentMd enter: aid=${target}`);
1199
1229
  const gatewayUrl = await this._resolveGateway(target);
1200
1230
  const cached = this.loadRecord(target) ?? {};
@@ -1233,7 +1263,7 @@ export class AgentMdManager {
1233
1263
  });
1234
1264
  }
1235
1265
  if (response.status === 404) {
1236
- this.saveRecord(target, { remote_status: 'missing', checked_at: Date.now(), last_error: '' });
1266
+ this.saveRecord(target, { remote_status: 'missing', checked_at: Date.now(), last_error: '' }, observation);
1237
1267
  throw new NotFoundError(`agent.md not found for aid: ${target}`);
1238
1268
  }
1239
1269
  if ((response.status < 200 || response.status >= 300) && !reusedCachedNotModified) {
@@ -1307,8 +1337,9 @@ export class AgentMdManager {
1307
1337
  remote_status: 'found',
1308
1338
  verify_status: statusText,
1309
1339
  verify_error: reason,
1340
+ verified_agent_md: { etag: responseEtag, content_hash: localEtag, verification, signature, cert_pem: peer.certPem },
1310
1341
  last_error: '',
1311
- });
1342
+ }, observation);
1312
1343
  const owner = this._ownerAid();
1313
1344
  let inSync = null;
1314
1345
  if (target.toLowerCase() === owner.toLowerCase()) {
@@ -1349,8 +1380,6 @@ export class AgentMdManager {
1349
1380
  const checkedAt = Number(cached.checked_at ?? 0) || 0;
1350
1381
  if (String(cached.remote_status ?? '').trim().toLowerCase() === 'missing' && checkedAt > 0 && Date.now() - checkedAt <= AGENT_MD_NEGATIVE_CACHE_TTL_MS)
1351
1382
  return;
1352
- if (this._downloadInflight.has(target))
1353
- return;
1354
1383
  void this.download(target).catch((err) => {
1355
1384
  this.saveRecord(target, {
1356
1385
  last_error: err instanceof Error ? err.message : String(err),