@agentunion/fastaun-browser 0.5.20 → 0.5.21

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 (130) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/_packed_docs/CHANGELOG.md +47 -0
  3. package/_packed_docs/INDEX.md +453 -440
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +164 -161
  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/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
  10. package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
  11. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
  12. package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +666 -658
  13. 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
  14. 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
  15. package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
  16. package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
  17. package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
  18. package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
  19. package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
  20. package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +147 -14
  21. package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
  22. package/_packed_docs/sdk/09-group-rpc-manual.md +5 -2
  23. package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
  24. package/_packed_docs/sdk/09-/345/207/255/350/257/201/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +71 -0
  25. package/_packed_docs/sdk/09-/347/273/204/347/273/207/346/234/215/345/212/241RPC/346/211/213/345/206/214.md +88 -0
  26. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +134 -130
  27. package/_packed_docs/sdk/INDEX.md +239 -221
  28. package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +930 -922
  29. 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 +106 -43
  30. 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 +180 -9
  31. 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
  32. package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +480 -476
  33. 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 +1 -1
  34. 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 +2 -2
  35. package/dist/agent-md-schema.d.ts +1 -0
  36. package/dist/agent-md-schema.d.ts.map +1 -1
  37. package/dist/agent-md-schema.js +5 -4
  38. package/dist/agent-md-schema.js.map +1 -1
  39. package/dist/agent-md.d.ts +25 -1
  40. package/dist/agent-md.d.ts.map +1 -1
  41. package/dist/agent-md.js +148 -71
  42. package/dist/agent-md.js.map +1 -1
  43. package/dist/aid-store.d.ts +13 -0
  44. package/dist/aid-store.d.ts.map +1 -1
  45. package/dist/aid-store.js +55 -8
  46. package/dist/aid-store.js.map +1 -1
  47. package/dist/aid.d.ts.map +1 -1
  48. package/dist/aid.js +2 -1
  49. package/dist/aid.js.map +1 -1
  50. package/dist/auth.d.ts.map +1 -1
  51. package/dist/auth.js +4 -0
  52. package/dist/auth.js.map +1 -1
  53. package/dist/bundle.js +4981 -1219
  54. package/dist/cert-utils.d.ts +1 -0
  55. package/dist/cert-utils.d.ts.map +1 -1
  56. package/dist/cert-utils.js +21 -0
  57. package/dist/cert-utils.js.map +1 -1
  58. package/dist/client/delivery.d.ts +101 -16
  59. package/dist/client/delivery.d.ts.map +1 -1
  60. package/dist/client/delivery.js +1172 -160
  61. package/dist/client/delivery.js.map +1 -1
  62. package/dist/client/rpc-pipeline.d.ts +4 -1
  63. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  64. package/dist/client/rpc-pipeline.js +122 -48
  65. package/dist/client/rpc-pipeline.js.map +1 -1
  66. package/dist/client/v2-e2ee.d.ts +22 -3
  67. package/dist/client/v2-e2ee.d.ts.map +1 -1
  68. package/dist/client/v2-e2ee.js +1258 -649
  69. package/dist/client/v2-e2ee.js.map +1 -1
  70. package/dist/client.d.ts +23 -2
  71. package/dist/client.d.ts.map +1 -1
  72. package/dist/client.js +391 -87
  73. package/dist/client.js.map +1 -1
  74. package/dist/config.d.ts +4 -0
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +1 -0
  77. package/dist/config.js.map +1 -1
  78. package/dist/events.d.ts +19 -1
  79. package/dist/events.d.ts.map +1 -1
  80. package/dist/events.js +146 -23
  81. package/dist/events.js.map +1 -1
  82. package/dist/facades.d.ts +42 -1
  83. package/dist/facades.d.ts.map +1 -1
  84. package/dist/facades.js +363 -7
  85. package/dist/facades.js.map +1 -1
  86. package/dist/index.d.ts +5 -3
  87. package/dist/index.d.ts.map +1 -1
  88. package/dist/index.js +1 -1
  89. package/dist/index.js.map +1 -1
  90. package/dist/keystore/index.d.ts +14 -1
  91. package/dist/keystore/index.d.ts.map +1 -1
  92. package/dist/keystore/indexeddb-identity-store.d.ts +4 -0
  93. package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
  94. package/dist/keystore/indexeddb-identity-store.js +30 -13
  95. package/dist/keystore/indexeddb-identity-store.js.map +1 -1
  96. package/dist/keystore/indexeddb-shared.d.ts +19 -2
  97. package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
  98. package/dist/keystore/indexeddb-shared.js +193 -6
  99. package/dist/keystore/indexeddb-shared.js.map +1 -1
  100. package/dist/keystore/indexeddb-token-store.d.ts +15 -2
  101. package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
  102. package/dist/keystore/indexeddb-token-store.js +308 -27
  103. package/dist/keystore/indexeddb-token-store.js.map +1 -1
  104. package/dist/org-protocol.d.ts +45 -0
  105. package/dist/org-protocol.d.ts.map +1 -1
  106. package/dist/org-protocol.js +757 -11
  107. package/dist/org-protocol.js.map +1 -1
  108. package/dist/ownership-v21.d.ts +21 -4
  109. package/dist/ownership-v21.d.ts.map +1 -1
  110. package/dist/ownership-v21.js +49 -16
  111. package/dist/ownership-v21.js.map +1 -1
  112. package/dist/ownership-workflow.d.ts +30 -0
  113. package/dist/ownership-workflow.d.ts.map +1 -1
  114. package/dist/ownership-workflow.js +412 -54
  115. package/dist/ownership-workflow.js.map +1 -1
  116. package/dist/seq-tracker.d.ts +2 -0
  117. package/dist/seq-tracker.d.ts.map +1 -1
  118. package/dist/seq-tracker.js +3 -2
  119. package/dist/seq-tracker.js.map +1 -1
  120. package/dist/tools/cross-sdk-agent.d.ts +60 -3
  121. package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
  122. package/dist/tools/cross-sdk-agent.js +433 -37
  123. package/dist/tools/cross-sdk-agent.js.map +1 -1
  124. package/dist/transport.d.ts +3 -0
  125. package/dist/transport.d.ts.map +1 -1
  126. package/dist/transport.js +62 -30
  127. package/dist/transport.js.map +1 -1
  128. package/dist/version.d.ts +1 -1
  129. package/dist/version.js +1 -1
  130. package/package.json +1 -1
package/dist/agent-md.js CHANGED
@@ -18,7 +18,7 @@ export function buildOwnershipAgentMdV21(content, statement) {
18
18
  }
19
19
  function updateOwnershipAgentMd(content, fields) {
20
20
  const payload = parseAgentMdTailSignature(String(content ?? '')).payload;
21
- const match = /^---(?:\r\n|\n|\r)([\s\S]*?)(?:\r\n|\n|\r)---([\s\S]*)$/.exec(payload);
21
+ const match = /^\ufeff?---(?:\r\n|\n|\r)([\s\S]*?)(?:\r\n|\n|\r)---([\s\S]*)$/.exec(payload);
22
22
  if (!match)
23
23
  throw new ValidationError('agent.md must start with YAML frontmatter');
24
24
  const document = parseDocument(match[1], { uniqueKeys: true });
@@ -34,7 +34,7 @@ function updateOwnershipAgentMd(content, fields) {
34
34
  for (const [name, value] of Object.entries(fields))
35
35
  document.set(name, value);
36
36
  const yamlText = document.toString().replace(/\r\n/g, '\n');
37
- const updated = `---\n${yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`}---${match[2]}`;
37
+ const updated = `${payload.startsWith('\ufeff') ? '\ufeff' : ''}---\n${yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`}---${match[2]}`;
38
38
  validateAgentMdDocument(updated, { expectedAid: String(frontmatter.aid ?? '') });
39
39
  return updated;
40
40
  }
@@ -206,6 +206,10 @@ function requestWithNodeHost(input, init, timeoutMs) {
206
206
  }
207
207
  });
208
208
  }
209
+ export function readAgentMdFrontmatter(content) {
210
+ const payload = parseAgentMdTailSignatureForRead(content).payload;
211
+ return payload.replace(/^\ufeff/, '').split(/\r\n|\n|\r/, 1)[0]?.trim() === '---' ? parseFileFrontmatter(payload, true, false) : {};
212
+ }
209
213
  async function fetchWithTimeout(input, init, timeoutMs = DEFAULT_HTTP_TIMEOUT_MS) {
210
214
  if (new Headers(init.headers).has('host')) {
211
215
  return await requestWithNodeHost(input, init, timeoutMs);
@@ -344,10 +348,19 @@ export class AgentMdManager {
344
348
  if (fallback && fallback !== url)
345
349
  response = await read(fallback);
346
350
  }
347
- if (!response.ok && response.status !== 404)
348
- throw new StateError(`NS ownership check unavailable: HTTP ${response.status}`, { stringCode: 'unavailable', retryable: true });
349
351
  if (headerValue(response.headers, 'X-AUN-Ownership-Status') === 'not_applicable')
350
352
  throw new StateError('group/org uses its lifecycle owner relationship', { stringCode: 'not_applicable' });
353
+ if (response.status === 400) {
354
+ let failure;
355
+ try {
356
+ failure = await response.json();
357
+ }
358
+ catch { }
359
+ if (isJsonObject(failure) && failure.error === 'invalid')
360
+ throw new ValidationError('NS ownership material is invalid', { stringCode: 'invalid' });
361
+ }
362
+ if (!response.ok && response.status !== 404)
363
+ throw new StateError(`NS ownership check unavailable: HTTP ${response.status}`, { stringCode: 'unavailable', retryable: true });
351
364
  const protocol = headerValue(response.headers, 'X-AUN-Ownership-Protocol');
352
365
  const version = headerValue(response.headers, 'X-AUN-Ownership-Version');
353
366
  const lock = headerValue(response.headers, 'X-AUN-Ownership-Lock');
@@ -369,7 +382,7 @@ export class AgentMdManager {
369
382
  }
370
383
  return { sub_aid: target, exists: response.ok, content, etag, protocol, relation_version: Number(version), lock: lock, observed_at: Math.trunc(Date.now() / 1000) };
371
384
  }
372
- async submitOwnershipRaw(aid, content, condition) {
385
+ async submitOwnershipRaw(aid, content, condition, guard) {
373
386
  const target = this._safeAid(aid);
374
387
  if (this._currentAid()?.aid !== target)
375
388
  throw new ValidationError('ownership upload requires its own identity', { stringCode: 'forbidden' });
@@ -378,15 +391,44 @@ export class AgentMdManager {
378
391
  throw new ValidationError('ownership upload requires its original document condition');
379
392
  const gatewayUrl = await this._resolveGateway(target);
380
393
  const token = await this._uploadAccessToken(target, gatewayUrl);
381
- const response = await fetchWithTimeout(this._url(target, gatewayUrl), {
382
- method: 'PUT', cache: 'no-store', redirect: 'error', body: content,
383
- headers: { 'X-AUN-Signature-Format': 'multi-v1', 'Content-Type': 'text/markdown; charset=utf-8', Authorization: `Bearer ${token}`,
384
- ...(condition.if_match ? { 'If-Match': condition.if_match } : { 'If-None-Match': '*' }) },
385
- });
394
+ const viaGatewayHost = this._uploadViaGatewayHost && nodeBuiltin('node:http') !== null;
395
+ let url = this._url(target, gatewayUrl);
396
+ if (viaGatewayHost) {
397
+ const parsed = new URL(gatewayUrl);
398
+ const port = parsed.port && parsed.port !== '443' && parsed.port !== '20001' ? `:${parsed.port}` : '';
399
+ url = `${agentMdHttpScheme(gatewayUrl)}://${parsed.hostname}${port}/agent.md`;
400
+ }
401
+ const submit = async (targetUrl) => {
402
+ await guard?.();
403
+ return fetchWithTimeout(targetUrl, {
404
+ method: 'PUT', cache: 'no-store', redirect: 'error', body: content,
405
+ headers: { 'X-AUN-Signature-Format': 'multi-v1', 'Content-Type': 'text/markdown; charset=utf-8', Authorization: `Bearer ${token}`,
406
+ ...(viaGatewayHost ? { Host: target } : {}),
407
+ ...(condition.if_match ? { 'If-Match': condition.if_match } : { 'If-None-Match': '*' }) },
408
+ });
409
+ };
410
+ let response;
411
+ try {
412
+ response = await submit(url);
413
+ }
414
+ catch (error) {
415
+ if (!viaGatewayHost)
416
+ throw error;
417
+ const fallback = nameServiceAgentMdUrl(target, gatewayUrl);
418
+ if (!fallback || fallback === url)
419
+ throw error;
420
+ url = fallback;
421
+ response = await submit(url);
422
+ }
423
+ if (response.status === 404 && !headerValue(response.headers, 'X-AUN-Ownership-Protocol') && headerValue(response.headers, 'X-AUN-Ownership-Status') !== 'not_applicable' && viaGatewayHost) {
424
+ const fallback = nameServiceAgentMdUrl(target, gatewayUrl);
425
+ if (fallback && fallback !== url)
426
+ response = await submit(fallback);
427
+ }
386
428
  if (!response.ok) {
387
429
  const reason = (await response.text()).trim();
388
430
  const code = response.status === 409 || response.status === 412 ? 'conflict' : response.status === 401 || response.status === 403 ? 'forbidden' : response.status === 428 ? 'unsupported_protocol' : response.status >= 500 ? 'unavailable' : 'invalid';
389
- throw new StateError(`ownership agent.md upload failed: HTTP ${response.status}${reason ? ` ${reason}` : ''}`, { stringCode: /owner_locked/.test(reason) ? 'owner_locked' : code, retryable: code === 'unavailable' });
431
+ throw new StateError(`agent.md upload failed: HTTP ${response.status}${reason ? ` ${reason}` : ''}`, { stringCode: /owner_locked/.test(reason) ? 'owner_locked' : code, retryable: code === 'unavailable' });
390
432
  }
391
433
  return await this.readOwnershipFresh(target);
392
434
  }
@@ -476,9 +518,30 @@ export class AgentMdManager {
476
518
  throw new StateError('pending agent.md requires its original document condition', { stringCode: 'persistence_unavailable' });
477
519
  const pendingDocument = validateAgentMdDocument(pending.content, { expectedAid: aid, validateOwnership: false });
478
520
  const meta = pendingDocument.frontmatter;
479
- if (this._isLegacyBodyRelation(meta) && pending.relation?.protocol !== 'legacy')
521
+ const priorRelation = this._isLegacyBodyRelation(meta) && pending.relation && Object.keys(pending.relation).length === 4 && ['relation_version', 'lock', 'owner_aid', 'statement'].every((field) => Object.hasOwn(pending.relation, field));
522
+ if (this._isLegacyBodyRelation(meta) && pending.relation?.protocol !== 'legacy' && !priorRelation)
480
523
  throw new StateError('legacy ownership pending agent.md requires its original relationship snapshot', { stringCode: 'unsupported_protocol' });
481
524
  try {
525
+ if (priorRelation) {
526
+ const relation = pending.relation, statement = parseOwnershipFile(pending.content, 'agent', { expectedSubAid: aid }).statement;
527
+ if (!statement || !this._sameBodyRelation(statement, relation.statement) || statement.relation_version !== relation.relation_version || statement.owner_aid !== relation.owner_aid || !['locked', 'unlocked'].includes(String(relation.lock)) || pending.content_etag !== await AgentMdManager.contentEtag(pending.content))
528
+ throw new StateError('pending original relationship context is corrupt', { stringCode: 'persistence_unavailable' });
529
+ const identity = signer ?? this._currentAid();
530
+ if (!identity || identity.aid !== aid || this._currentAid()?.aid !== aid || this._ownerAid() !== aid)
531
+ throw new StateError('body recovery requires its current subject identity', { stringCode: 'forbidden' });
532
+ await this._verifyBodyUpload(aid, pending.content, identity);
533
+ const remote = await this.readAgentMdFresh(aid);
534
+ if (!remote.exists || !this._sameBodyRelation(this._bodyRelationFields(meta), this._bodyRelationFields(validateAgentMdDocumentForRead(remote.content, { expectedAid: aid, validateOwnership: false }).frontmatter)))
535
+ throw new StateError('pending agent.md relationship material changed', { stringCode: 'conflict' });
536
+ if (remote.etag === pending.content_etag && remote.content === pending.content) {
537
+ await this.saveRecord(aid, { content: pending.content, remote_etag: remote.etag, local_draft_content: '', pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
538
+ return true;
539
+ }
540
+ if (!condition.if_match || remote.etag !== condition.if_match)
541
+ throw new StateError('pending agent.md original document condition changed', { stringCode: 'conflict' });
542
+ await this._uploadPrepared(aid, pending.content, identity, condition);
543
+ return true;
544
+ }
482
545
  if (pending.relation?.protocol === 'legacy') {
483
546
  const relation = pending.relation;
484
547
  if (!condition.if_match || condition.if_match !== relation.original_etag || typeof relation.original_content !== 'string' || relation.original_content_etag !== await AgentMdManager.contentEtag(relation.original_content) || pending.content_etag !== await AgentMdManager.contentEtag(pending.content))
@@ -487,7 +550,7 @@ export class AgentMdManager {
487
550
  if (!this._isLegacyBodyRelation(original) || !this._sameBodyRelation(this._bodyRelationFields(original), relation.fields))
488
551
  throw new StateError('pending legacy original relationship snapshot is corrupt', { stringCode: 'persistence_unavailable' });
489
552
  if (!this._sameBodyRelation(this._bodyRelationFields(meta), relation.fields))
490
- throw new StateError('legacy relationship changes require their original protocol', { stringCode: 'unsupported_protocol' });
553
+ throw new StateError('legacy relationship material changed', { stringCode: 'conflict' });
491
554
  const identity = signer ?? this._currentAid();
492
555
  if (!identity || identity.aid.toLowerCase() !== aid.toLowerCase() || this._currentAid()?.aid.toLowerCase() !== aid.toLowerCase())
493
556
  throw new StateError('legacy body recovery requires its current subject identity', { stringCode: 'forbidden' });
@@ -533,26 +596,11 @@ export class AgentMdManager {
533
596
  throw new StateError('pending agent.md requires a signer identity');
534
597
  await this._verifyBodyUpload(aid, pending.content, identity);
535
598
  }
536
- const remote = await this.readOwnershipFresh(aid);
537
- const fail = (code, reason, retryable = false) => new StateError(reason, { stringCode: code, retryable });
538
- const candidate = parseOwnershipFile(pending.content, 'agent', { expectedSubAid: aid });
539
- const relation = pending.relation && Object.keys(pending.relation).length ? pending.relation : undefined;
540
- const sameStatement = (first, second) => first == null && second == null || isJsonObject(first) && isJsonObject(second) && ['version', 'declaration_id', 'owner_aid', 'sub_aid', 'relation_version', 'declared_at'].every((field) => first[field] === second[field]);
541
- if (candidate.statement && !relation)
542
- throw fail('persistence_unavailable', 'pending ownership body update requires its original relationship context');
543
- if (relation) {
544
- if (remote.relation_version !== relation.relation_version || remote.lock !== relation.lock)
545
- throw fail('conflict', 'pending agent.md original relationship changed');
546
- if (!sameStatement(candidate.statement, relation.statement))
547
- throw fail('unsupported_protocol', 'ownership changes require their persisted 2.1 task');
548
- if (remote.exists && (remote.relation_version > 0 || candidate.statement)) {
549
- const current = parseOwnershipFile(remote.content, 'agent', { expectedSubAid: aid });
550
- if (!sameStatement(current.statement, relation.statement) || (remote.owner_aid ?? current.statement?.owner_aid ?? null) !== relation.owner_aid)
551
- throw fail('conflict', 'pending agent.md relationship statement changed');
552
- }
553
- }
554
- if (!relation && (remote.relation_version !== 0 || remote.lock !== 'unlocked'))
555
- throw fail('unavailable', 'pending agent.md cannot overwrite relationship history', true);
599
+ if (pending.content_etag !== await AgentMdManager.contentEtag(pending.content))
600
+ throw new StateError('pending agent.md original bytes are corrupt', { stringCode: 'persistence_unavailable' });
601
+ const remote = await this.readAgentMdFresh(aid);
602
+ if (remote.exists && this._isLegacyBodyRelation(validateAgentMdDocumentForRead(remote.content, { expectedAid: aid, validateOwnership: false }).frontmatter))
603
+ throw new StateError('legacy relationship material cannot be removed or replaced', { stringCode: 'conflict' });
556
604
  if (remote.exists && remote.etag === pending.content_etag && remote.content === pending.content) {
557
605
  const saved = await this.saveRecord(aid, { content: pending.content, remote_etag: remote.etag, local_draft_content: '', pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
558
606
  if (!saved.aid)
@@ -580,12 +628,7 @@ export class AgentMdManager {
580
628
  _isLegacyBodyRelation(meta) {
581
629
  if (['group', 'org'].includes(String(meta.type)))
582
630
  return false;
583
- if (Object.hasOwn(meta, 'ownership')) {
584
- const ownership = meta.ownership;
585
- const statement = isJsonObject(ownership) ? ownership.statement : undefined;
586
- return isJsonObject(statement) && statement.version === 2 && !Object.hasOwn(statement, 'relation_version');
587
- }
588
- return ['owner', 'ownership_proof', 'ownership_release'].some((field) => Object.hasOwn(meta, field));
631
+ return ['owner', 'ownership', 'ownership_proof', 'ownership_release'].some((field) => Object.hasOwn(meta, field));
589
632
  }
590
633
  _bodyRelationFields(meta) {
591
634
  return Object.fromEntries(['owner', 'ownership', 'ownership_proof', 'ownership_release'].filter((field) => Object.hasOwn(meta, field)).map((field) => [field, meta[field]]));
@@ -594,14 +637,32 @@ export class AgentMdManager {
594
637
  const stable = (value) => Array.isArray(value) ? value.map(stable) : isJsonObject(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable(value[key])])) : value;
595
638
  return JSON.stringify(stable(first)) === JSON.stringify(stable(second));
596
639
  }
597
- async _readLegacyBodyFresh(target) {
640
+ async readAgentMdFresh(aid) {
641
+ const target = this._safeAid(aid);
598
642
  const gateway = await this._resolveGateway(target);
599
- const response = await fetchWithTimeout(this._url(target, gateway), { method: 'GET', headers: { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', 'Cache-Control': 'no-cache' }, cache: 'no-store', redirect: 'error' });
643
+ const viaGatewayHost = this._uploadViaGatewayHost && nodeBuiltin('node:http') !== null;
644
+ const headers = { 'X-AUN-Signature-Format': 'multi-v1', Accept: 'text/markdown', 'Cache-Control': 'no-cache', ...(viaGatewayHost ? { Host: target } : {}) };
645
+ let url = this._url(target, gateway);
646
+ if (viaGatewayHost) {
647
+ const parsed = new URL(gateway);
648
+ const port = parsed.port && parsed.port !== '443' && parsed.port !== '20001' ? `:${parsed.port}` : '';
649
+ url = `${agentMdHttpScheme(gateway)}://${parsed.hostname}${port}/agent.md`;
650
+ }
651
+ let response;
652
+ try {
653
+ response = await fetchWithTimeout(url, { method: 'GET', headers, cache: 'no-store', redirect: 'error' });
654
+ }
655
+ catch (error) {
656
+ const fallback = viaGatewayHost ? nameServiceAgentMdUrl(target, gateway) : '';
657
+ if (!fallback || fallback === url)
658
+ throw error;
659
+ response = await fetchWithTimeout(fallback, { method: 'GET', headers, cache: 'no-store', redirect: 'error' });
660
+ }
600
661
  if (response.status === 404)
601
- throw new StateError('legacy relationship cannot be created by an ordinary body update', { stringCode: 'unsupported_protocol' });
662
+ return { exists: false, content: '', etag: '', last_modified: '', status: 404 };
602
663
  const etag = headerValue(response.headers, 'ETag');
603
664
  if (response.status !== 200 || !etag)
604
- throw new StateError('legacy original document or ETag is unavailable', { stringCode: 'unavailable', retryable: true });
665
+ throw new StateError('agent.md original document or ETag is unavailable', { stringCode: 'unavailable', retryable: true });
605
666
  let content;
606
667
  try {
607
668
  content = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(await response.arrayBuffer());
@@ -609,7 +670,13 @@ export class AgentMdManager {
609
670
  catch {
610
671
  throw new ValidationError('agent.md must be valid UTF-8', { stringCode: 'invalid' });
611
672
  }
612
- return { content, etag, last_modified: headerValue(response.headers, 'Last-Modified'), status: response.status };
673
+ return { exists: true, content, etag, last_modified: headerValue(response.headers, 'Last-Modified'), status: 200 };
674
+ }
675
+ async _readLegacyBodyFresh(target) {
676
+ const fresh = await this.readAgentMdFresh(target);
677
+ if (!fresh.exists)
678
+ throw new StateError('legacy relationship cannot be created by an ordinary body update', { stringCode: 'unsupported_protocol' });
679
+ return fresh;
613
680
  }
614
681
  async _verifyLegacyBodyOriginal(target, content) {
615
682
  const parsed = validateAgentMdDocumentForRead(content, { expectedAid: target, validateOwnership: false, requireSignature: true });
@@ -658,30 +725,21 @@ export class AgentMdManager {
658
725
  if (entity)
659
726
  await this._verifyEntityDocument(target, signed);
660
727
  let condition = originalCondition;
661
- let relation = entityContext ?? (await this.pendingUpload(target))?.relation;
728
+ const pendingRelation = originalCondition ? (await this.pendingUpload(target))?.relation : undefined;
729
+ let relation = entityContext ?? (pendingRelation?.protocol === 'legacy' ? pendingRelation : {});
662
730
  if (!condition) {
663
731
  let fresh;
664
732
  if (!entity && this._isLegacyBodyRelation(meta))
665
733
  fresh = await this._readLegacyBodyFresh(target);
666
- else {
667
- try {
668
- fresh = entity ? await this._readEntityFresh(target) : await this.readOwnershipFresh(target);
669
- }
670
- catch (error) {
671
- if (entity || !['unavailable', 'unsupported_protocol'].includes(String(error?.stringCode)))
672
- throw error;
673
- const legacy = await this._readLegacyBodyFresh(target);
674
- const legacyMeta = validateAgentMdDocumentForRead(legacy.content, { expectedAid: target, validateOwnership: false }).frontmatter;
675
- if (!this._isLegacyBodyRelation(legacyMeta))
676
- throw error;
677
- fresh = legacy;
678
- }
679
- }
734
+ else
735
+ fresh = entity ? await this._readEntityFresh(target) : await this.readAgentMdFresh(target);
680
736
  const freshMeta = !entity && (!('exists' in fresh) || fresh.exists) && fresh.content ? validateAgentMdDocumentForRead(fresh.content, { expectedAid: target, validateOwnership: false }).frontmatter : undefined;
681
737
  const legacy = !entity && (this._isLegacyBodyRelation(meta) || freshMeta && this._isLegacyBodyRelation(freshMeta));
682
738
  if (legacy) {
683
- if (!freshMeta || !this._isLegacyBodyRelation(freshMeta) || !this._sameBodyRelation(this._bodyRelationFields(meta), this._bodyRelationFields(freshMeta)))
739
+ if (!freshMeta || !this._isLegacyBodyRelation(freshMeta))
684
740
  throw new StateError('legacy relationship changes require their original protocol', { stringCode: 'unsupported_protocol' });
741
+ if (!this._sameBodyRelation(this._bodyRelationFields(meta), this._bodyRelationFields(freshMeta)))
742
+ throw new StateError('legacy relationship material changed', { stringCode: 'conflict' });
685
743
  if (!fresh.etag)
686
744
  throw new StateError('legacy original document ETag is unavailable', { stringCode: 'unavailable', retryable: true });
687
745
  await this._verifyLegacyBodyOriginal(target, fresh.content);
@@ -689,20 +747,14 @@ export class AgentMdManager {
689
747
  relation = { protocol: 'legacy', original_content: fresh.content, original_content_etag: await AgentMdManager.contentEtag(fresh.content), original_etag: fresh.etag, fields: this._bodyRelationFields(freshMeta) };
690
748
  }
691
749
  else {
692
- const authority = fresh;
693
- condition = authority.exists ? { if_match: fresh.etag } : { if_none_match: '*' };
694
- if (!entity) {
695
- const candidate = parseOwnershipFile(signed, 'agent', { expectedSubAid: target });
696
- const statement = authority.exists && (authority.relation_version > 0 || candidate.statement) ? parseOwnershipFile(fresh.content, 'agent', { expectedSubAid: target }).statement : undefined;
697
- relation = { relation_version: authority.relation_version, lock: authority.lock, owner_aid: authority.owner_aid ?? statement?.owner_aid ?? null, statement: statement ?? null };
698
- }
750
+ condition = 'exists' in fresh && fresh.exists ? { if_match: fresh.etag } : { if_none_match: '*' };
699
751
  }
700
752
  }
701
753
  if (relation?.protocol === 'legacy') {
702
754
  if (signer.aid.toLowerCase() !== target.toLowerCase() || this._currentAid()?.aid.toLowerCase() !== target.toLowerCase() || this._currentAid()?.certFingerprint !== signer.certFingerprint)
703
755
  throw new StateError('legacy body update requires its current subject identity', { stringCode: 'forbidden' });
704
756
  if (!this._isLegacyBodyRelation(meta) || !this._sameBodyRelation(this._bodyRelationFields(meta), relation.fields))
705
- throw new StateError('legacy relationship changes require their original protocol', { stringCode: 'unsupported_protocol' });
757
+ throw new StateError('legacy relationship material changed', { stringCode: 'conflict' });
706
758
  await this._verifyBodyUpload(target, signed, signer);
707
759
  }
708
760
  const entityAuthorization = '';
@@ -718,7 +770,7 @@ export class AgentMdManager {
718
770
  if (delegated && !owner?.isPrivateKeyValid())
719
771
  throw new StateError('owner agent.md upload requires a loaded owner identity');
720
772
  const authorization = entity ? '' : delegated ? await this._ownerUploadAuthorization(target, signed, owner) : '';
721
- const result = await this._uploadHttp(target, signed, authorization, condition, relation?.protocol === 'legacy');
773
+ const result = await this._uploadHttp(target, signed, authorization, condition);
722
774
  const remoteEtag = String(result.etag ?? '').trim();
723
775
  const contentEtag = await AgentMdManager.contentEtag(signed);
724
776
  const saved = await this.saveRecord(target, {
@@ -927,6 +979,7 @@ export class AgentMdManager {
927
979
  return {
928
980
  aid: target,
929
981
  content: contentResult.content,
982
+ frontmatter: readAgentMdFrontmatter(contentResult.content),
930
983
  verification,
931
984
  signature,
932
985
  cert_pem: peer.certPem,
@@ -1127,6 +1180,31 @@ export class AgentMdManager {
1127
1180
  const root = this.root;
1128
1181
  return await this._readStorage(this._contentKey(aid), root);
1129
1182
  }
1183
+ async ownType() {
1184
+ try {
1185
+ const target = this._ownerAid();
1186
+ const current = this._currentAid();
1187
+ if (!target || !current?.isCertValid() || current.aid.toLowerCase() !== target.toLowerCase())
1188
+ return '';
1189
+ const record = await this.loadRecord(target);
1190
+ const content = String(record?.local_draft_content ?? '') || String(record?.content ?? '') || await this.readContent(target) || '';
1191
+ if (!content)
1192
+ return '';
1193
+ const parsed = validateAgentMdDocumentForRead(content, { expectedAid: target, validateOwnership: false, requireSignature: true });
1194
+ const type = parseFileFrontmatter(parsed.payload, false, false).type;
1195
+ if (typeof type !== 'string' || !type.trim())
1196
+ return '';
1197
+ const verified = await current.verifyAgentMd(content, async (signer, fingerprint) => {
1198
+ if (signer.toLowerCase() === current.aid.toLowerCase() && await certMatchesFingerprint(current.certPem, fingerprint))
1199
+ return current;
1200
+ throw new StateError('自身 agent.md 签名证书不在本地可信身份中');
1201
+ });
1202
+ return verified.ok && verified.data.status === 'verified' ? type.trim() : '';
1203
+ }
1204
+ catch {
1205
+ return '';
1206
+ }
1207
+ }
1130
1208
  async writeContent(aid, content) {
1131
1209
  const root = this.root;
1132
1210
  await this._writeStorage(this._contentKey(aid), String(content ?? ''), root);
@@ -1333,7 +1411,7 @@ export class AgentMdManager {
1333
1411
  _url(aid, gatewayUrl) {
1334
1412
  return `${agentMdHttpScheme(gatewayUrl)}://${this._safeAid(aid)}/agent.md`;
1335
1413
  }
1336
- async _uploadHttp(aid, content, ownerAuthorization = '', condition, legacyBodyUpdate = false) {
1414
+ async _uploadHttp(aid, content, ownerAuthorization = '', condition) {
1337
1415
  if (!condition || Object.keys(condition).length !== 1 || !(typeof condition.if_match === 'string' && condition.if_match || condition.if_none_match === '*'))
1338
1416
  throw new StateError('agent.md upload requires its original document condition', { stringCode: 'persistence_unavailable' });
1339
1417
  const gatewayUrl = await this._resolveGateway(aid);
@@ -1344,7 +1422,6 @@ export class AgentMdManager {
1344
1422
  const viaGatewayHost = this._uploadViaGatewayHost && nodeBuiltin('node:http') !== null;
1345
1423
  const headers = { 'X-AUN-Signature-Format': 'multi-v1',
1346
1424
  'Content-Type': 'text/markdown; charset=utf-8',
1347
- ...(!entity && !(legacyBodyUpdate && this._isLegacyBodyRelation(meta)) ? { 'X-AUN-Ownership-Check': '1' } : {}),
1348
1425
  ...(condition.if_match ? { 'If-Match': condition.if_match } : { 'If-None-Match': '*' }),
1349
1426
  ...(viaGatewayHost ? { Host: this._safeAid(aid) } : {}),
1350
1427
  ...(!entity && authorization ? { 'X-AUN-AgentMD-Owner-Authorization': authorization } : {}),