@agentunion/fastaun 0.5.20 → 0.5.22

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 (128) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/_packed_docs/CHANGELOG.md +104 -0
  3. package/_packed_docs/INDEX.md +59 -34
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +59 -52
  5. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/344/270/216/346/215/242owner/347/274/226/346/216/222/346/226/271/346/241/210.md +530 -0
  6. package/_packed_docs/agent.md/working/AID/347/261/273/345/236/213-owner/345/205/263/347/263/273/345/256/214/346/225/264/350/256/276/350/256/241/344/270/216/345/256/236/346/226/275/350/256/241/345/210/222.md +685 -0
  7. package/_packed_docs/agent.md/working/agent.md-schema/344/270/216/347/255/276/345/217/221/351/252/214/347/255/276/346/265/201/347/250/213/346/276/204/346/270/205.md +349 -0
  8. package/_packed_docs/agent.md/working//346/234/254/345/234/260Docker/346/265/213/350/257/225Bug/346/270/205/345/215/225.md +40 -0
  9. package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +307 -67
  10. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +41 -15
  11. package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
  12. package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
  13. package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +30 -8
  14. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
  15. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +33 -22
  16. package/_packed_docs/sdk/0.5.21-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +368 -0
  17. package/_packed_docs/sdk/0.5.21-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +350 -0
  18. package/_packed_docs/sdk/0.5.22-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +212 -0
  19. package/_packed_docs/sdk/0.5.22-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +199 -0
  20. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
  21. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
  22. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
  23. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
  24. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
  25. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +163 -14
  26. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
  27. package/_packed_docs/sdk/09-group-rpc-manual.md +15 -2
  28. package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
  29. package/_packed_docs/sdk/09-storage-rpc-manual.md +64 -7
  30. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +73 -0
  31. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +90 -0
  32. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +33 -25
  33. package/_packed_docs/sdk/INDEX.md +67 -33
  34. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +74 -57
  35. package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +312 -300
  36. package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +322 -382
  37. package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -708
  38. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +28 -20
  39. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +190 -97
  40. package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +158 -127
  41. package/dist/agent-md-schema.js +6 -4
  42. package/dist/agent-md-schema.js.map +1 -1
  43. package/dist/agent-md.d.ts +22 -2
  44. package/dist/agent-md.js +92 -68
  45. package/dist/agent-md.js.map +1 -1
  46. package/dist/aid-store.d.ts +13 -0
  47. package/dist/aid-store.js +50 -15
  48. package/dist/aid-store.js.map +1 -1
  49. package/dist/auth.d.ts +1 -0
  50. package/dist/auth.js +22 -2
  51. package/dist/auth.js.map +1 -1
  52. package/dist/cert-utils.d.ts +1 -0
  53. package/dist/cert-utils.js +13 -0
  54. package/dist/cert-utils.js.map +1 -1
  55. package/dist/client/delivery.d.ts +87 -1
  56. package/dist/client/delivery.js +1387 -139
  57. package/dist/client/delivery.js.map +1 -1
  58. package/dist/client/group-state.js +3 -0
  59. package/dist/client/group-state.js.map +1 -1
  60. package/dist/client/rpc-pipeline.d.ts +4 -1
  61. package/dist/client/rpc-pipeline.js +149 -37
  62. package/dist/client/rpc-pipeline.js.map +1 -1
  63. package/dist/client/v2-e2ee.d.ts +12 -2
  64. package/dist/client/v2-e2ee.js +831 -304
  65. package/dist/client/v2-e2ee.js.map +1 -1
  66. package/dist/client.d.ts +26 -2
  67. package/dist/client.js +412 -68
  68. package/dist/client.js.map +1 -1
  69. package/dist/config.d.ts +4 -0
  70. package/dist/config.js +1 -0
  71. package/dist/config.js.map +1 -1
  72. package/dist/events.d.ts +18 -1
  73. package/dist/events.js +148 -9
  74. package/dist/events.js.map +1 -1
  75. package/dist/facades.d.ts +43 -1
  76. package/dist/facades.js +363 -7
  77. package/dist/facades.js.map +1 -1
  78. package/dist/group-fs.d.ts +1 -0
  79. package/dist/group-fs.js +37 -19
  80. package/dist/group-fs.js.map +1 -1
  81. package/dist/index.d.ts +5 -3
  82. package/dist/index.js +1 -1
  83. package/dist/index.js.map +1 -1
  84. package/dist/keystore/aid-db.d.ts +6 -1
  85. package/dist/keystore/aid-db.js +184 -42
  86. package/dist/keystore/aid-db.js.map +1 -1
  87. package/dist/keystore/index.d.ts +14 -0
  88. package/dist/keystore/local-token-store.d.ts +9 -1
  89. package/dist/keystore/local-token-store.js +79 -14
  90. package/dist/keystore/local-token-store.js.map +1 -1
  91. package/dist/org-protocol.d.ts +46 -0
  92. package/dist/org-protocol.js +802 -12
  93. package/dist/org-protocol.js.map +1 -1
  94. package/dist/ownership-v21.d.ts +36 -5
  95. package/dist/ownership-v21.js +168 -52
  96. package/dist/ownership-v21.js.map +1 -1
  97. package/dist/ownership-workflow.d.ts +24 -2
  98. package/dist/ownership-workflow.js +682 -41
  99. package/dist/ownership-workflow.js.map +1 -1
  100. package/dist/secret-store/file-store.js +84 -0
  101. package/dist/secret-store/file-store.js.map +1 -1
  102. package/dist/seq-tracker.d.ts +1 -0
  103. package/dist/seq-tracker.js +2 -2
  104. package/dist/seq-tracker.js.map +1 -1
  105. package/dist/storage/lowlevel.d.ts +6 -0
  106. package/dist/storage/lowlevel.js +24 -7
  107. package/dist/storage/lowlevel.js.map +1 -1
  108. package/dist/storage/read-cache.d.ts +70 -0
  109. package/dist/storage/read-cache.js +625 -0
  110. package/dist/storage/read-cache.js.map +1 -0
  111. package/dist/storage/vfs.d.ts +2 -0
  112. package/dist/storage/vfs.js +50 -15
  113. package/dist/storage/vfs.js.map +1 -1
  114. package/dist/tools/cross-sdk-agent.d.ts +86 -3
  115. package/dist/tools/cross-sdk-agent.js +758 -46
  116. package/dist/tools/cross-sdk-agent.js.map +1 -1
  117. package/dist/transport.d.ts +3 -0
  118. package/dist/transport.js +104 -46
  119. package/dist/transport.js.map +1 -1
  120. package/dist/types.d.ts +1 -0
  121. package/dist/v2/e2ee/encrypt-group.js +1 -0
  122. package/dist/v2/e2ee/encrypt-group.js.map +1 -1
  123. package/dist/v2/e2ee/encrypt-p2p.js +1 -0
  124. package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
  125. package/dist/v2/e2ee/types.d.ts +1 -0
  126. package/dist/version.d.ts +1 -1
  127. package/dist/version.js +1 -1
  128. package/package.json +1 -1
package/dist/agent-md.js CHANGED
@@ -20,9 +20,6 @@ function relationshipFields(meta) {
20
20
  function legacyRelationship(meta) {
21
21
  if (['group', 'org'].includes(String(meta.type)))
22
22
  return undefined;
23
- const statement = meta.ownership?.statement;
24
- if (statement && typeof statement === 'object' && Object.hasOwn(statement, 'relation_version'))
25
- return undefined;
26
23
  const fields = relationshipFields(meta);
27
24
  return Object.keys(fields).length ? fields : undefined;
28
25
  }
@@ -35,7 +32,7 @@ export function buildOwnershipAgentMd(content, owner, proof) {
35
32
  }
36
33
  export function updateOwnershipAgentMd(content, fields) {
37
34
  const payload = parseAgentMdTailSignature(String(content ?? '')).payload;
38
- const match = /^---(?:\r\n|\n|\r)([\s\S]*?)(?:\r\n|\n|\r)---([\s\S]*)$/.exec(payload);
35
+ const match = /^\ufeff?---(?:\r\n|\n|\r)([\s\S]*?)(?:\r\n|\n|\r)---([\s\S]*)$/.exec(payload);
39
36
  if (!match)
40
37
  throw new ValidationError('agent.md must start with YAML frontmatter');
41
38
  const document = parseDocument(match[1], { uniqueKeys: true });
@@ -51,7 +48,7 @@ export function updateOwnershipAgentMd(content, fields) {
51
48
  for (const [name, value] of Object.entries(fields))
52
49
  document.set(name, value);
53
50
  const yamlText = document.toString().replace(/\r\n/g, '\n');
54
- const updated = `---\n${yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`}---${match[2]}`;
51
+ const updated = `${payload.startsWith('\ufeff') ? '\ufeff' : ''}---\n${yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`}---${match[2]}`;
55
52
  validateAgentMdDocument(updated, { expectedAid: String(frontmatter.aid ?? '') });
56
53
  return updated;
57
54
  }
@@ -366,7 +363,7 @@ export class AgentMdManager {
366
363
  });
367
364
  this._cache.delete(aid);
368
365
  }
369
- async upload(content) {
366
+ async upload(content, originalCondition) {
370
367
  const target = this._ownerAid();
371
368
  if (!target)
372
369
  throw new ValidationError('uploadAgentMd requires local AID');
@@ -382,7 +379,7 @@ export class AgentMdManager {
382
379
  return await this.uploadAs(target, rawContent, current);
383
380
  if (parseAgentMdTailSignature(rawContent).signatures.length > 1) {
384
381
  await this._verifyDocument(rawContent, target, current, true, true);
385
- return await this._uploadPrepared(target, rawContent, current);
382
+ return await this._uploadPrepared(target, rawContent, current, originalCondition);
386
383
  }
387
384
  const signed = current.signAgentMd(rawContent);
388
385
  if (!signed.ok || !signed.data) {
@@ -391,15 +388,40 @@ export class AgentMdManager {
391
388
  }
392
389
  const signedContent = signed.data.signed;
393
390
  verifyAgentMdBodyUpdate(signedContent, current.certPem, target);
394
- return await this._uploadPrepared(target, signedContent, current);
391
+ return await this._uploadPrepared(target, signedContent, current, originalCondition);
392
+ }
393
+ async readAgentMdFresh(aid) {
394
+ const target = this.safeAid(aid), gateway = await this._resolveGateway(target);
395
+ const response = await requestText(this._url(target, gateway, this._uploadViaGatewayHost), {
396
+ method: 'GET', verifySsl: this._verifySsl,
397
+ headers: { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', 'Cache-Control': 'no-cache', ...(this._uploadViaGatewayHost ? { Host: target } : {}) },
398
+ timeoutMs: DEFAULT_HTTP_TIMEOUT_MS, redirectsLeft: 0, strictUtf8: true,
399
+ });
400
+ if (response.status === 404)
401
+ return { exists: false, content: '', etag: '' };
402
+ if (response.status !== 200)
403
+ throw ownershipFailure('unavailable', `agent.md fresh read failed: HTTP ${response.status}`, true);
404
+ const etag = headerValue(response.headers, 'ETag');
405
+ if (!etag)
406
+ throw ownershipFailure('unavailable', 'agent.md fresh read has no ETag', true);
407
+ return { exists: true, content: response.text, etag };
395
408
  }
396
409
  async readOwnershipFresh(aid) {
397
410
  const target = this.safeAid(aid);
398
411
  const response = await this._ownershipRequest(target, 'GET');
412
+ if ([200, 400, 404].includes(response.status) && headerValue(response.headers, 'X-AUN-Ownership-Status') === 'not_applicable')
413
+ throw ownershipFailure('not_applicable', 'group/org uses its lifecycle owner relationship');
414
+ if (response.status === 400) {
415
+ let value;
416
+ try {
417
+ value = JSON.parse(response.text);
418
+ }
419
+ catch { }
420
+ if (isJsonObject(value) && value.error === 'invalid')
421
+ throw ownershipFailure('invalid', 'NS rejected the current agent.md: HTTP 400');
422
+ }
399
423
  if (response.status !== 200 && response.status !== 404)
400
424
  throw ownershipFailure('unavailable', `NS fresh read failed: HTTP ${response.status}`, true);
401
- if (headerValue(response.headers, 'X-AUN-Ownership-Status') === 'not_applicable')
402
- throw ownershipFailure('not_applicable', 'group/org uses its lifecycle owner relationship');
403
425
  let type;
404
426
  if (response.status === 200) {
405
427
  try {
@@ -417,9 +439,10 @@ export class AgentMdManager {
417
439
  const etag = headerValue(response.headers, 'ETag');
418
440
  if (!/^(?:0|[1-9][0-9]*)$/.test(version) || !Number.isSafeInteger(Number(version)) || !['locked', 'unlocked'].includes(lock) || response.status === 200 && !etag || response.status === 404 && (version !== '0' || lock !== 'unlocked'))
419
441
  throw ownershipFailure('unavailable', 'NS fresh metadata is incomplete', true);
420
- return { sub_aid: target, exists: response.status === 200, content: response.status === 200 ? response.text : '', etag, protocol, relation_version: Number(version), lock: lock, observed_at: Math.floor(Date.now() / 1000) };
442
+ const ownershipStatus = headerValue(response.headers, 'X-AUN-Ownership-Status');
443
+ return { sub_aid: target, exists: response.status === 200, content: response.status === 200 ? response.text : '', etag, protocol, relation_version: Number(version), lock: lock, stage_supported: headerValue(response.headers, 'X-AUN-Ownership-Stage') === '1', ...(ownershipStatus ? { ownership_status: ownershipStatus } : {}), ...(ownershipStatus === 'pending' ? { pending: true } : {}), observed_at: Math.floor(Date.now() / 1000) };
421
444
  }
422
- async submitOwnershipRaw(aid, raw, condition) {
445
+ async submitOwnershipRaw(aid, raw, condition, assertActive, stage) {
423
446
  const target = this.safeAid(aid), current = this._currentAid();
424
447
  if (!current?.isPrivateKeyValid() || current.aid !== target || this._ownerAid() !== target)
425
448
  throw ownershipFailure('forbidden', 'raw agent.md submission requires its own authenticated identity');
@@ -427,7 +450,9 @@ export class AgentMdManager {
427
450
  throw new ValidationError('raw agent.md requires one original condition');
428
451
  parseOwnershipFile(raw, 'agent', { expectedSubAid: target });
429
452
  verifyAgentMdUpload(raw, current.certPem, target);
430
- const response = await this._ownershipRequest(target, 'PUT', raw, condition);
453
+ if (stage && (await this.readOwnershipFresh(target)).stage_supported !== true)
454
+ throw ownershipFailure('unsupported_protocol', 'NS does not support Ownership Stage', true);
455
+ const response = stage ? await this._ownershipRequest(target, 'PUT', raw, condition, false, assertActive, true) : await this._ownershipRequest(target, 'PUT', raw, condition, false, assertActive);
431
456
  if (response.status === 409 || response.status === 412)
432
457
  throw ownershipFailure('conflict', `NS original condition failed: HTTP ${response.status}`);
433
458
  if (response.status === 428)
@@ -438,16 +463,19 @@ export class AgentMdManager {
438
463
  throw ownershipFailure('invalid', 'NS rejected the submitted agent.md: HTTP 400');
439
464
  if (response.status < 200 || response.status >= 300)
440
465
  throw ownershipFailure('unavailable', `NS raw submission failed: HTTP ${response.status}`, true);
466
+ if (stage && (headerValue(response.headers, 'X-AUN-Ownership-Stage') !== '1' || headerValue(response.headers, 'X-AUN-Ownership-Lock') !== 'unlocked' || headerValue(response.headers, 'X-AUN-Ownership-Status') !== 'pending'))
467
+ throw ownershipFailure('unsupported_protocol', 'NS did not confirm Ownership Stage', true);
441
468
  const fresh = await this.readOwnershipFresh(target);
442
469
  if (fresh.content !== raw)
443
470
  throw ownershipFailure('unavailable', 'NS raw submission cannot be confirmed from the current document', true);
444
- this.saveRecord(target, { content: raw, local_etag: AgentMdManager.contentEtag(raw), remote_etag: fresh.etag, fetched_at: Date.now(), checked_at: Date.now(), remote_status: 'found' });
445
471
  return fresh;
446
472
  }
447
- async _ownershipRequest(target, method, raw, condition, legacyRead = false) {
473
+ async _ownershipRequest(target, method, raw, condition, legacyRead = false, assertActive, stage) {
448
474
  const gatewayUrl = await this._resolveGateway(target);
449
475
  const headers = { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', 'Cache-Control': 'no-cache', ...(!legacyRead ? { 'X-AUN-Ownership-Check': '1' } : {}) };
450
476
  if (method === 'PUT') {
477
+ if (stage)
478
+ headers['X-AUN-Ownership-Stage'] = '1';
451
479
  headers['Content-Type'] = 'text/markdown; charset=utf-8';
452
480
  headers.Authorization = `Bearer ${await this._uploadAccessToken(target, gatewayUrl)}`;
453
481
  if (condition?.if_match)
@@ -458,11 +486,13 @@ export class AgentMdManager {
458
486
  const viaGateway = this._uploadViaGatewayHost;
459
487
  if (viaGateway)
460
488
  headers.Host = target;
461
- const request = (url) => requestText(url, { method, verifySsl: this._verifySsl, headers, body: raw, timeoutMs: DEFAULT_HTTP_TIMEOUT_MS, redirectsLeft: 0, strictUtf8: true });
462
- let response = await request(this._url(target, gatewayUrl, viaGateway));
489
+ const request = (url) => { if (method === 'PUT')
490
+ assertActive?.(); return requestText(url, { method, verifySsl: this._verifySsl, headers, body: raw, timeoutMs: DEFAULT_HTTP_TIMEOUT_MS, redirectsLeft: 0, strictUtf8: true }); };
491
+ const primary = this._url(target, gatewayUrl, viaGateway);
492
+ let response = await request(primary);
463
493
  if (response.status === 404 && !headerValue(response.headers, 'X-AUN-Ownership-Protocol') && headerValue(response.headers, 'X-AUN-Ownership-Status') !== 'not_applicable') {
464
494
  const fallback = nameServiceAgentMdUrl(target, gatewayUrl);
465
- if (fallback && fallback !== this._url(target, gatewayUrl, viaGateway)) {
495
+ if (fallback && fallback !== primary) {
466
496
  headers.Host = target;
467
497
  response = await request(fallback);
468
498
  }
@@ -522,6 +552,27 @@ export class AgentMdManager {
522
552
  try {
523
553
  if (pending.relation?.legacy === true)
524
554
  return await this._resumeLegacyBodyUpload(aid, pending, signer ?? this._currentAid());
555
+ if (legacyRelationship(meta) && pending.relation && Object.keys(pending.relation).length === 4 && ['relation_version', 'lock', 'owner_aid', 'statement'].every((field) => Object.hasOwn(pending.relation, field))) {
556
+ const relation = pending.relation, statement = parseOwnershipFile(pending.content, 'agent', { expectedSubAid: aid }).statement;
557
+ if (!statement || !isDeepStrictEqual(statement, relation.statement) || statement.relation_version !== relation.relation_version || statement.owner_aid !== relation.owner_aid || !['locked', 'unlocked'].includes(String(relation.lock)) || pending.content_etag !== AgentMdManager.contentEtag(pending.content))
558
+ throw ownershipFailure('persistence_unavailable', 'pending original relationship context is corrupt');
559
+ const identity = signer ?? this._currentAid();
560
+ if (!identity || identity.aid !== aid || this._currentAid()?.aid !== aid || this._ownerAid() !== aid)
561
+ throw ownershipFailure('forbidden', 'body recovery requires its current subject identity');
562
+ await this._verifyDocument(pending.content, aid, identity, true, true);
563
+ const remote = await this.readAgentMdFresh(aid);
564
+ if (!remote.exists || !isDeepStrictEqual(legacyRelationship(meta), legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter)))
565
+ throw ownershipFailure('conflict', 'pending agent.md relationship material changed');
566
+ if (remote.etag === pending.content_etag && remote.content === pending.content) {
567
+ this.saveRecord(aid, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
568
+ this._clearConfirmedDraft(aid, pending.content);
569
+ return true;
570
+ }
571
+ if (!condition.if_match || remote.etag !== condition.if_match)
572
+ throw ownershipFailure('conflict', 'pending agent.md original document condition changed');
573
+ await this._uploadPrepared(aid, pending.content, identity, condition);
574
+ return true;
575
+ }
525
576
  if (legacyRelationship(meta))
526
577
  throw ownershipFailure('unsupported_protocol', 'legacy ownership pending agent.md requires its original relationship snapshot');
527
578
  if (['group', 'org'].includes(String(meta.type))) {
@@ -556,28 +607,15 @@ export class AgentMdManager {
556
607
  if (multipleSignatures) {
557
608
  if (!identity)
558
609
  throw new StateError('pending agent.md requires a signer identity');
559
- await this._verifyDocument(pending.content, aid, identity, true);
560
- }
561
- const remote = await this.readOwnershipFresh(aid);
562
- const fail = (code, reason, retryable = false) => ownershipFailure(code, reason, retryable);
563
- const candidate = parseOwnershipFile(pending.content, 'agent', { expectedSubAid: aid });
564
- const relation = pending.relation && Object.keys(pending.relation).length ? pending.relation : undefined;
565
- const sameStatement = (first, second) => first == null && second == null || first !== null && second !== null && typeof first === 'object' && typeof second === 'object' && ['version', 'declaration_id', 'owner_aid', 'sub_aid', 'relation_version', 'declared_at'].every((field) => first[field] === second[field]);
566
- if (candidate.statement && !relation)
567
- throw fail('persistence_unavailable', 'pending ownership body update requires its original relationship context');
568
- if (relation) {
569
- if (remote.relation_version !== relation.relation_version || remote.lock !== relation.lock)
570
- throw fail('conflict', 'pending agent.md original relationship changed');
571
- if (!sameStatement(candidate.statement, relation.statement))
572
- throw fail('unsupported_protocol', 'ownership changes require their persisted 2.1 task');
573
- if (remote.exists && (remote.relation_version > 0 || candidate.statement)) {
574
- const current = parseOwnershipFile(remote.content, 'agent', { expectedSubAid: aid });
575
- if (!sameStatement(current.statement, relation.statement) || (remote.owner_aid ?? current.statement?.owner_aid ?? null) !== relation.owner_aid)
576
- throw fail('conflict', 'pending agent.md relationship statement changed');
577
- }
610
+ await this._verifyDocument(pending.content, aid, identity, true, true);
578
611
  }
579
- if (!relation && (remote.relation_version !== 0 || remote.lock !== 'unlocked'))
580
- throw fail('unavailable', 'pending agent.md cannot overwrite relationship history', true);
612
+ if (pending.content_etag !== AgentMdManager.contentEtag(pending.content))
613
+ throw ownershipFailure('persistence_unavailable', 'pending agent.md original bytes are corrupt');
614
+ if (!identity)
615
+ throw new StateError('pending agent.md requires a signer identity');
616
+ if (!multipleSignatures)
617
+ verifyAgentMdBodyUpdate(pending.content, identity.certPem, aid);
618
+ const remote = await this.readAgentMdFresh(aid);
581
619
  if (remote.exists && remote.etag === pending.content_etag && remote.content === pending.content) {
582
620
  const saved = this.saveRecord(aid, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
583
621
  if (!saved.aid)
@@ -587,10 +625,8 @@ export class AgentMdManager {
587
625
  }
588
626
  if (condition.if_match && (!remote.exists || remote.etag !== condition.if_match) || condition.if_none_match === '*' && remote.exists)
589
627
  throw ownershipFailure('conflict', 'pending agent.md original document condition changed');
590
- if (!identity)
591
- throw new StateError('pending agent.md requires a signer identity');
592
- if (!multipleSignatures)
593
- verifyAgentMdUpload(pending.content, identity.certPem, aid);
628
+ if (remote.exists && legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter))
629
+ throw ownershipFailure('conflict', 'legacy relationship material cannot be removed or replaced');
594
630
  await this._uploadPrepared(aid, pending.content, identity, condition);
595
631
  return true;
596
632
  }
@@ -603,34 +639,27 @@ export class AgentMdManager {
603
639
  const meta = validateAgentMdDocument(signedContent, { expectedAid: target, validateOwnership: false }).frontmatter;
604
640
  const entity = ['group', 'org'].includes(String(meta.type));
605
641
  const legacy = legacyRelationship(meta);
606
- const draftStrict = !originalCondition;
642
+ const draftStrict = !originalCondition || !this._pendingUpload(target, true);
607
643
  if (legacy && (signer.aid !== target || this._currentAid()?.aid !== target || this._ownerAid() !== target))
608
644
  throw ownershipFailure('forbidden', 'legacy relationship body updates require current subject identity');
609
- if (legacy && Object.hasOwn(meta, 'owner'))
610
- validateOwnershipFrontmatter({ aid: meta.aid, type: meta.type, owner: meta.owner }, target);
611
- if (!entity && !legacy)
612
- parseOwnershipFile(signedContent, 'agent', { expectedSubAid: target });
613
645
  if (draftStrict && this._saveRecord(target, { local_draft_content: signedContent }, true).local_draft_content !== signedContent)
614
646
  throw new StateError('agent.md local draft persistence failed');
615
647
  const entityContext = entity ? await this._entityContext(target, meta, signer) : undefined;
616
648
  let condition = originalCondition;
617
649
  let relation = entityContext ?? (this._pendingUpload(target, draftStrict))?.relation;
618
650
  if (!condition) {
619
- const fresh = entity ? await this._readEntityFresh(target) : legacy ? await this._readLegacyBodyBase(target) : await this.readOwnershipFresh(target);
651
+ const fresh = entity ? await this._readEntityFresh(target) : legacy ? await this._readLegacyBodyBase(target) : await this.readAgentMdFresh(target);
620
652
  condition = fresh.exists ? { if_match: fresh.etag } : { if_none_match: '*' };
621
653
  if (!entity) {
622
654
  if (legacy) {
623
655
  if (!fresh.exists || !fresh.etag || !isDeepStrictEqual(legacy, legacyRelationship(validateAgentMdReadDocument(fresh.content, { expectedAid: target }).frontmatter)))
624
- throw ownershipFailure('unsupported_protocol', 'legacy relationship material must remain unchanged');
656
+ throw ownershipFailure('conflict', 'legacy relationship material must remain unchanged');
625
657
  relation = { legacy: true, base_content: fresh.content, base_content_etag: AgentMdManager.contentEtag(fresh.content), base_etag: fresh.etag, fields: legacy };
626
658
  }
627
659
  else {
628
660
  if (fresh.exists && legacyRelationship(validateAgentMdReadDocument(fresh.content, { expectedAid: target }).frontmatter))
629
- throw ownershipFailure('unsupported_protocol', 'legacy relationship material cannot be removed or replaced');
630
- const authority = fresh;
631
- const candidate = parseOwnershipFile(signedContent, 'agent', { expectedSubAid: target });
632
- const statement = fresh.exists && (authority.relation_version > 0 || candidate.statement) ? parseOwnershipFile(fresh.content, 'agent', { expectedSubAid: target }).statement : undefined;
633
- relation = { relation_version: authority.relation_version, lock: authority.lock, owner_aid: authority.owner_aid ?? statement?.owner_aid ?? null, statement: statement ?? null };
661
+ throw ownershipFailure('conflict', 'legacy relationship material cannot be removed or replaced');
662
+ relation = undefined;
634
663
  }
635
664
  }
636
665
  }
@@ -676,16 +705,10 @@ export class AgentMdManager {
676
705
  return parsed;
677
706
  }
678
707
  async _readLegacyBodyBase(target) {
679
- const response = await this._ownershipRequest(target, 'GET', undefined, undefined, true);
680
- if (response.status === 404)
681
- return { exists: false, content: '', etag: '' };
682
- if (response.status !== 200)
683
- throw ownershipFailure('unavailable', `legacy agent.md fresh read failed: HTTP ${response.status}`, true);
684
- await this._verifyLegacyBodyContent(target, response.text);
685
- const etag = headerValue(response.headers, 'ETag');
686
- if (!etag)
687
- throw ownershipFailure('unavailable', 'legacy agent.md original ETag is missing', true);
688
- return { exists: true, content: response.text, etag };
708
+ const response = await this.readAgentMdFresh(target);
709
+ if (response.exists)
710
+ await this._verifyLegacyBodyContent(target, response.content);
711
+ return response;
689
712
  }
690
713
  async _resumeLegacyBodyUpload(target, pending, identity) {
691
714
  const relation = pending.relation;
@@ -771,7 +794,8 @@ export class AgentMdManager {
771
794
  async _verifyDocument(content, target, identity, current = false, bodyUpdate = false) {
772
795
  const certificates = new Map();
773
796
  const parsed = parseAgentMdTailSignature(content, !current);
774
- const meta = validateAgentMdReadDocument(content).frontmatter;
797
+ const meta = validateAgentMdReadDocument(content, { expectedAid: target }).frontmatter;
798
+ const historicalEntityOwner = !current && ['group', 'org'].includes(String(meta.type)) && parsed.signatures.length === 1 && parsed.fields?.signer_aid !== undefined && parsed.fields.signer_aid === meta.owner && parsed.fields.signer_aid !== meta.aid;
775
799
  const readCompatibility = !current && (!['group', 'org'].includes(String(meta.type)) || meta.type === 'group' && !Object.hasOwn(meta, 'entity_cert_fingerprint') && parsed.signatures.length === 1 && (parsed.fields?.signer_aid === undefined || parsed.fields.signer_aid === meta.aid));
776
800
  for (const item of parsed.signatures.slice(1)) {
777
801
  const fields = item.fields;
@@ -781,7 +805,7 @@ export class AgentMdManager {
781
805
  const resolver = (aid, fingerprint) => certificates.get(`${aid};${fingerprint}`);
782
806
  if (current)
783
807
  (bodyUpdate ? verifyAgentMdBodyUpdate : verifyAgentMdUpload)(content, identity.certPem, target, undefined, resolver);
784
- const result = verifyDocument(content, { expectedAid: target, primaryCertPem: identity.certPem, requireCurrentValidity: current, readCompatibility, certificateResolver: resolver });
808
+ const result = verifyDocument(content, { expectedAid: historicalEntityOwner ? String(meta.owner) : target, primaryCertPem: identity.certPem, requireCurrentValidity: current, readCompatibility, certificateResolver: resolver });
785
809
  if (result.status === 'invalid')
786
810
  throw ownershipFailure('invalid', result.reason ?? 'document signature verification failed');
787
811
  return result;
@@ -845,7 +869,6 @@ export class AgentMdManager {
845
869
  const uploadHeaders = {
846
870
  'X-AUN-Signature-Format': 'multi-v1',
847
871
  'Content-Type': 'text/markdown; charset=utf-8',
848
- ...(!entity && !legacyRelationship(meta) ? { 'X-AUN-Ownership-Check': '1' } : {}),
849
872
  ...(condition.if_match ? { 'If-Match': condition.if_match } : { 'If-None-Match': '*' }),
850
873
  ...(this._uploadViaGatewayHost ? { Host: target } : {}),
851
874
  ...(authorization && !entity ? { 'X-AUN-AgentMD-Owner-Authorization': authorization } : {}),
@@ -1296,6 +1319,7 @@ export class AgentMdManager {
1296
1319
  return {
1297
1320
  aid: target,
1298
1321
  content,
1322
+ frontmatter,
1299
1323
  verification,
1300
1324
  signature,
1301
1325
  cert_pem: peer.certPem,