@agentunion/fastaun 0.5.28 → 0.5.30

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 (101) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/_packed_docs/CHANGELOG.md +41 -0
  3. package/_packed_docs/INDEX.md +20 -6
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +14 -11
  5. package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +18 -1
  6. package/_packed_docs/cli//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241CLI/345/274/200/345/217/221/344/270/216/344/275/277/347/224/250/346/214/207/345/215/227.md +869 -0
  7. package/_packed_docs/sdk/0.5.27-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +408 -0
  8. package/_packed_docs/sdk/0.5.27-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +410 -0
  9. package/_packed_docs/sdk/0.5.28-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +228 -0
  10. package/_packed_docs/sdk/0.5.28-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +146 -0
  11. package/_packed_docs/sdk/0.5.29-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +180 -0
  12. package/_packed_docs/sdk/0.5.29-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +164 -0
  13. package/_packed_docs/sdk/0.5.30-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +61 -0
  14. package/_packed_docs/sdk/0.5.30-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +68 -0
  15. 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 -90
  16. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +7 -3
  17. package/_packed_docs/sdk/INDEX.md +20 -4
  18. 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
  19. 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 +1 -1
  20. package/_packed_docs//346/216/247/345/210/266/346/266/210/346/201/257/344/270/216/350/256/276/345/244/207/350/203/275/345/212/233/345/215/217/345/225/206/346/226/271/346/241/210.md +426 -0
  21. package/_packed_docs//346/266/210/346/201/257/345/220/214/346/255/245/344/270/216/345/216/206/345/217/262/346/213/211/345/217/226/344/273/243/347/240/201/345/256/241/346/237/245/351/227/256/351/242/230/346/270/205/345/215/225.md +279 -229
  22. package/_packed_docs//351/231/214/347/224/237/344/272/272/346/213/222/347/273/235/345/215/217/350/256/256TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +138 -0
  23. package/_packed_docs//351/231/214/347/224/237/344/272/272/346/213/222/347/273/235/345/215/217/350/256/256/350/257/246/347/273/206/350/256/276/350/256/241.md +231 -0
  24. package/_packed_docs//351/243/216/346/216/247/345/223/215/345/272/224/344/270/216SDK/345/215/217/345/220/214/346/226/271/346/241/210.md +381 -0
  25. package/dist/agent-md-changes.d.ts +2 -14
  26. package/dist/agent-md-changes.js +41 -212
  27. package/dist/agent-md-changes.js.map +1 -1
  28. package/dist/agent-md-schema.js +2 -2
  29. package/dist/agent-md-schema.js.map +1 -1
  30. package/dist/agent-md.d.ts +4 -6
  31. package/dist/agent-md.js +66 -57
  32. package/dist/agent-md.js.map +1 -1
  33. package/dist/aid-store.d.ts +3 -1
  34. package/dist/aid-store.js +65 -42
  35. package/dist/aid-store.js.map +1 -1
  36. package/dist/client/control-messages.d.ts +0 -1
  37. package/dist/client/control-messages.js +90 -70
  38. package/dist/client/control-messages.js.map +1 -1
  39. package/dist/client/delivery.js +2 -1
  40. package/dist/client/delivery.js.map +1 -1
  41. package/dist/client/group-state.js +2 -3
  42. package/dist/client/group-state.js.map +1 -1
  43. package/dist/client/lifecycle.js +0 -2
  44. package/dist/client/lifecycle.js.map +1 -1
  45. package/dist/client/rpc-pipeline.js +8 -0
  46. package/dist/client/rpc-pipeline.js.map +1 -1
  47. package/dist/client/v2-e2ee.js +15 -11
  48. package/dist/client/v2-e2ee.js.map +1 -1
  49. package/dist/client.d.ts +2 -2
  50. package/dist/client.js +101 -51
  51. package/dist/client.js.map +1 -1
  52. package/dist/config.js +23 -18
  53. package/dist/config.js.map +1 -1
  54. package/dist/discovery.js +2 -1
  55. package/dist/discovery.js.map +1 -1
  56. package/dist/facades.d.ts +20 -0
  57. package/dist/facades.js +9 -0
  58. package/dist/facades.js.map +1 -1
  59. package/dist/index.d.ts +1 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/keystore/aid-db.js +14 -2
  62. package/dist/keystore/aid-db.js.map +1 -1
  63. package/dist/keystore/index.d.ts +3 -0
  64. package/dist/keystore/index.js +61 -1
  65. package/dist/keystore/index.js.map +1 -1
  66. package/dist/keystore/local-identity-store.js +112 -76
  67. package/dist/keystore/local-identity-store.js.map +1 -1
  68. package/dist/keystore/local-token-store.js +15 -26
  69. package/dist/keystore/local-token-store.js.map +1 -1
  70. package/dist/ownership-v21.d.ts +2 -1
  71. package/dist/ownership-v21.js +17 -3
  72. package/dist/ownership-v21.js.map +1 -1
  73. package/dist/ownership-workflow.js +13 -8
  74. package/dist/ownership-workflow.js.map +1 -1
  75. package/dist/register-flow.js +3 -2
  76. package/dist/register-flow.js.map +1 -1
  77. package/dist/risk-control.js +2 -2
  78. package/dist/risk-control.js.map +1 -1
  79. package/dist/secret-store/file-store.js +68 -56
  80. package/dist/secret-store/file-store.js.map +1 -1
  81. package/dist/semantic-facade.js +4 -2
  82. package/dist/semantic-facade.js.map +1 -1
  83. package/dist/semantic-protocol.d.ts +2 -12
  84. package/dist/semantic-protocol.js +33 -48
  85. package/dist/semantic-protocol.js.map +1 -1
  86. package/dist/storage/lowlevel.d.ts +4 -0
  87. package/dist/storage/lowlevel.js +58 -1
  88. package/dist/storage/lowlevel.js.map +1 -1
  89. package/dist/storage/vfs.d.ts +4 -0
  90. package/dist/storage/vfs.js +57 -12
  91. package/dist/storage/vfs.js.map +1 -1
  92. package/dist/tools/cross-sdk-agent.d.ts +1 -1
  93. package/dist/tools/cross-sdk-agent.js +26 -35
  94. package/dist/tools/cross-sdk-agent.js.map +1 -1
  95. package/dist/transport.js +1 -1
  96. package/dist/transport.js.map +1 -1
  97. package/dist/validators.js +9 -8
  98. package/dist/validators.js.map +1 -1
  99. package/dist/version.d.ts +1 -1
  100. package/dist/version.js +1 -1
  101. package/package.json +8 -8
package/dist/agent-md.js CHANGED
@@ -13,6 +13,7 @@ import { AUNError, ClientSignatureError, NotFoundError, StateError, ValidationEr
13
13
  import { isJsonObject } from './types.js';
14
14
  import { ownershipAid, ownershipFailure, parseOwnershipFile } from './ownership-v21.js';
15
15
  import { resultOk } from './result.js';
16
+ import { INVALID_AID_FORMAT } from './error-codes.js';
16
17
  const DEFAULT_HTTP_TIMEOUT_MS = 30_000;
17
18
  function relationshipFields(meta) {
18
19
  return Object.fromEntries(['owner', 'ownership', 'ownership_proof', 'ownership_release'].filter((key) => Object.hasOwn(meta, key)).map((key) => [key, meta[key]]));
@@ -48,7 +49,7 @@ export function updateOwnershipAgentMd(content, fields) {
48
49
  document.delete('ownership_release');
49
50
  for (const [name, value] of Object.entries(fields))
50
51
  document.set(name, value);
51
- const yamlText = document.toString().replace(/\r\n/g, '\n');
52
+ const yamlText = document.toString({ lineWidth: 0, blockQuote: 'literal' }).replace(/\r\n/g, '\n');
52
53
  const updated = `${payload.startsWith('\ufeff') ? '\ufeff' : ''}---\n${yamlText.endsWith('\n') ? yamlText : `${yamlText}\n`}---${match[2]}`;
53
54
  validateAgentMdDocument(updated, { expectedAid: String(frontmatter.aid ?? '') });
54
55
  return updated;
@@ -193,21 +194,6 @@ function requestText(url, opts) {
193
194
  });
194
195
  }
195
196
  export class AgentMdManager {
196
- static _commitListeners = new Map();
197
- static _commitRoot(aunPath) {
198
- const root = path.resolve(aunPath, 'AIDs');
199
- return process.platform === 'win32' ? root.toLowerCase() : root;
200
- }
201
- static subscribeCommits(aunPath, callback) {
202
- const root = this._commitRoot(aunPath), listeners = this._commitListeners.get(root) ?? new Set();
203
- listeners.add(callback);
204
- this._commitListeners.set(root, listeners);
205
- return () => {
206
- listeners.delete(callback);
207
- if (!listeners.size && this._commitListeners.get(root) === listeners)
208
- this._commitListeners.delete(root);
209
- };
210
- }
211
197
  _aunPath;
212
198
  _verifySsl;
213
199
  _log;
@@ -278,7 +264,7 @@ export class AgentMdManager {
278
264
  safeAid(aid) {
279
265
  const target = String(aid ?? '').trim();
280
266
  if (!target || target.includes('/') || target.includes('\\') || target.includes('\0')) {
281
- throw new ValidationError('agent.md aid is empty or contains path separators');
267
+ throw new ValidationError('agent.md aid is empty or contains path separators', { stringCode: INVALID_AID_FORMAT });
282
268
  }
283
269
  this._aidValidator?.(target);
284
270
  return target;
@@ -310,7 +296,9 @@ export class AgentMdManager {
310
296
  loaded.local_etag = AgentMdManager.contentEtag(content);
311
297
  }
312
298
  catch (err) {
313
- if (fs.existsSync(this.metaPath(target))) {
299
+ delete loaded.content;
300
+ delete loaded.local_etag;
301
+ if (err?.code !== 'ENOENT' && fs.existsSync(this.metaPath(target))) {
314
302
  this._log.warn(`agent.md content read failed: aid=${target} err=${err instanceof Error ? err.message : String(err)}`);
315
303
  }
316
304
  }
@@ -336,9 +324,6 @@ export class AgentMdManager {
336
324
  loadRecord(aid) {
337
325
  return this._loadRecord(aid);
338
326
  }
339
- listRecordedAids() {
340
- return fs.readdirSync(this.root, { withFileTypes: true }).filter((entry) => entry.isDirectory() && fs.existsSync(path.join(this.root, entry.name, 'agentmd.json'))).map((entry) => this.safeAid(entry.name));
341
- }
342
327
  updateRecord(aid, change) {
343
328
  const target = this.safeAid(aid);
344
329
  this._withRecordLock(target, () => {
@@ -350,11 +335,24 @@ export class AgentMdManager {
350
335
  this._cache.delete(target);
351
336
  }
352
337
  setOnCommitted(callback) { this._onCommitted = callback; }
338
+ async _saveCommittedRecord(aid, fields, strict = false) {
339
+ const pending = this._loadRecord(aid, true)?.pending_agent_md_notice;
340
+ const saved = this._saveRecord(aid, fields, strict);
341
+ if (saved.aid && pending?.message_id && this._onCommitted) {
342
+ const notice = (Array.isArray(saved.agent_md_change_outbox) ? saved.agent_md_change_outbox : []).find((item) => item.message_id === pending.message_id);
343
+ if (notice) {
344
+ try {
345
+ await this._onCommitted(aid, notice);
346
+ }
347
+ catch { }
348
+ }
349
+ }
350
+ return saved;
351
+ }
353
352
  _saveRecord(aid, fields, strict = false, observation) {
354
353
  const target = String(aid ?? '').trim();
355
354
  if (!target)
356
355
  return {};
357
- let completedNotice = false;
358
356
  try {
359
357
  const inputFields = { ...fields };
360
358
  const hasContent = Object.prototype.hasOwnProperty.call(inputFields, 'content') && inputFields.content !== undefined && inputFields.content !== null;
@@ -369,7 +367,6 @@ export class AgentMdManager {
369
367
  outbox.push(notice);
370
368
  inputFields.agent_md_change_outbox = outbox;
371
369
  inputFields.pending_agent_md_notice = {};
372
- completedNotice = true;
373
370
  }
374
371
  if (observation && (this.root !== observation.root || String(next.remote_etag ?? '') !== observation.etag || Number(next.observation_version ?? 0) !== observation.version))
375
372
  throw ownershipFailure('unavailable', 'agent.md observation changed during download', true);
@@ -398,21 +395,16 @@ export class AgentMdManager {
398
395
  }
399
396
  else {
400
397
  const current = this._loadRecord(target, strict);
401
- if (current && typeof current.content === 'string')
398
+ if (current && typeof current.content === 'string') {
402
399
  loaded.content = current.content;
403
- }
404
- this._cache.set(target, { ...loaded });
405
- if (completedNotice) {
406
- const listeners = new Set(AgentMdManager._commitListeners.get(AgentMdManager._commitRoot(this._aunPath)));
407
- if (this._onCommitted)
408
- listeners.add(this._onCommitted);
409
- for (const callback of listeners) {
410
- try {
411
- callback(target);
412
- }
413
- catch { }
400
+ loaded.local_etag = current.local_etag;
401
+ }
402
+ else {
403
+ delete loaded.content;
404
+ delete loaded.local_etag;
414
405
  }
415
406
  }
407
+ this._cache.set(target, { ...loaded });
416
408
  return { ...loaded };
417
409
  }
418
410
  catch (err) {
@@ -658,15 +650,15 @@ export class AgentMdManager {
658
650
  if (!identity || identity.aid !== aid || this._currentAid()?.aid !== aid || this._ownerAid() !== aid)
659
651
  throw ownershipFailure('forbidden', 'body recovery requires its current subject identity');
660
652
  await this._verifyDocument(pending.content, aid, identity, true, true);
661
- const remote = await this.readAgentMdFresh(aid);
662
- if (!remote.exists || !isDeepStrictEqual(legacyRelationship(meta), legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter)))
663
- throw ownershipFailure('conflict', 'pending agent.md relationship material changed');
653
+ const remote = await this.readOwnershipFresh(aid);
654
+ if (remote.lock === 'locked' && (!remote.exists || !isDeepStrictEqual(legacyRelationship(meta), legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter))))
655
+ throw ownershipFailure('conflict', 'locked agent.md relationship material must remain unchanged');
664
656
  if (remote.etag === pending.content_etag && remote.content === pending.content) {
665
- 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: '' });
657
+ await this._saveCommittedRecord(aid, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
666
658
  this._clearConfirmedDraft(aid, pending.content);
667
659
  return true;
668
660
  }
669
- if (!condition.if_match || remote.etag !== condition.if_match)
661
+ if (condition.if_match && (!remote.exists || remote.etag !== condition.if_match) || condition.if_none_match === '*' && remote.exists)
670
662
  throw ownershipFailure('conflict', 'pending agent.md original document condition changed');
671
663
  await this._uploadPrepared(aid, pending.content, identity, condition);
672
664
  return true;
@@ -689,7 +681,7 @@ export class AgentMdManager {
689
681
  await this._verifyDocument(pending.content, aid, identity, true);
690
682
  const remote = await this._readEntityFresh(aid);
691
683
  if (remote.exists && remote.content === pending.content && remote.etag === pending.content_etag) {
692
- 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: '' });
684
+ const saved = await this._saveCommittedRecord(aid, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
693
685
  if (!saved.aid)
694
686
  throw new StateError('agent.md pending completion persistence failed');
695
687
  this._clearConfirmedDraft(aid, pending.content);
@@ -713,9 +705,9 @@ export class AgentMdManager {
713
705
  throw new StateError('pending agent.md requires a signer identity');
714
706
  if (!multipleSignatures)
715
707
  verifyAgentMdBodyUpdate(pending.content, identity.certPem, aid);
716
- const remote = await this.readAgentMdFresh(aid);
708
+ const remote = await this.readOwnershipFresh(aid);
717
709
  if (remote.exists && remote.etag === pending.content_etag && remote.content === pending.content) {
718
- 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: '' });
710
+ const saved = await this._saveCommittedRecord(aid, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
719
711
  if (!saved.aid)
720
712
  throw new StateError('agent.md pending completion persistence failed');
721
713
  this._clearConfirmedDraft(aid, pending.content);
@@ -723,8 +715,8 @@ export class AgentMdManager {
723
715
  }
724
716
  if (condition.if_match && (!remote.exists || remote.etag !== condition.if_match) || condition.if_none_match === '*' && remote.exists)
725
717
  throw ownershipFailure('conflict', 'pending agent.md original document condition changed');
726
- if (remote.exists && legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter))
727
- throw ownershipFailure('conflict', 'legacy relationship material cannot be removed or replaced');
718
+ if (remote.lock === 'locked' && remote.exists && legacyRelationship(validateAgentMdReadDocument(remote.content, { expectedAid: aid }).frontmatter))
719
+ throw ownershipFailure('conflict', 'locked agent.md relationship material must remain unchanged');
728
720
  await this._uploadPrepared(aid, pending.content, identity, condition);
729
721
  return true;
730
722
  }
@@ -746,19 +738,24 @@ export class AgentMdManager {
746
738
  let condition = originalCondition;
747
739
  let relation = entityContext ?? (this._pendingUpload(target, draftStrict))?.relation;
748
740
  if (!condition) {
749
- const fresh = entity ? await this._readEntityFresh(target) : legacy ? await this._readLegacyBodyBase(target) : await this.readAgentMdFresh(target);
741
+ const fresh = entity ? await this._readEntityFresh(target) : await this._readBodyOriginalFresh(target);
750
742
  condition = fresh.exists ? { if_match: fresh.etag } : { if_none_match: '*' };
751
743
  if (!entity) {
752
- if (legacy) {
753
- if (!fresh.exists || !fresh.etag || !isDeepStrictEqual(legacy, legacyRelationship(validateAgentMdReadDocument(fresh.content, { expectedAid: target }).frontmatter)))
754
- throw ownershipFailure('conflict', 'legacy relationship material must remain unchanged');
744
+ // 锁未知时按 locked 处理:关系字段必须与原件完全一致
745
+ const locked = 'lock' in fresh && fresh.lock !== 'unlocked';
746
+ const original = fresh.exists ? legacyRelationship(validateAgentMdReadDocument(fresh.content, { expectedAid: target }).frontmatter) : undefined;
747
+ if (locked && !isDeepStrictEqual(legacy, original))
748
+ throw ownershipFailure('conflict', 'locked agent.md relationship material must remain unchanged');
749
+ if (original)
750
+ await this._verifyLegacyBodyContent(target, fresh.content);
751
+ if (legacy && locked)
755
752
  relation = { legacy: true, base_content: fresh.content, base_content_etag: AgentMdManager.contentEtag(fresh.content), base_etag: fresh.etag, fields: legacy };
753
+ else if (legacy) {
754
+ const statement = parseOwnershipFile(signedContent, 'agent', { expectedSubAid: target }).statement;
755
+ relation = { relation_version: statement.relation_version, lock: 'unlocked', owner_aid: statement.owner_aid, statement };
756
756
  }
757
- else {
758
- if (fresh.exists && legacyRelationship(validateAgentMdReadDocument(fresh.content, { expectedAid: target }).frontmatter))
759
- throw ownershipFailure('conflict', 'legacy relationship material cannot be removed or replaced');
757
+ else
760
758
  relation = undefined;
761
- }
762
759
  }
763
760
  }
764
761
  const uploader = this._currentAid();
@@ -767,7 +764,8 @@ export class AgentMdManager {
767
764
  const ownership = isJsonObject(meta.ownership) ? meta.ownership : {};
768
765
  const statement = isJsonObject(ownership.statement) ? ownership.statement : {};
769
766
  const noticeOwner = String(statement.owner_aid ?? ownership.owner_aid ?? meta.owner ?? '').trim();
770
- const previousNotice = existing?.pending_content === signedContent && isJsonObject(existing.pending_agent_md_notice) ? existing.pending_agent_md_notice : null;
767
+ const previousNotice = existing?.pending_content === signedContent && isJsonObject(existing.pending_agent_md_notice) ? existing.pending_agent_md_notice
768
+ : (Array.isArray(existing?.agent_md_change_outbox) ? existing.agent_md_change_outbox : []).find((item) => item.aid === target && item.owner_aid === noticeOwner && item.sender_aid === (uploader?.aid ?? this._ownerAid()) && item.etag === AgentMdManager.contentEtag(signedContent));
771
769
  const notice = previousNotice ?? { sender_aid: uploader?.aid ?? this._ownerAid(), owner_aid: noticeOwner, aid: target, document_type: String(meta.type ?? 'agent'), message_id: crypto.randomUUID() };
772
770
  const pending = this._saveRecord(target, { pending_operation: 'replace', pending_target_aid: target, pending_content: signedContent, pending_content_etag: AgentMdManager.contentEtag(signedContent), pending_error: '', pending_condition: condition, pending_relation: relation, pending_authorization: entityAuthorization, pending_agent_md_notice: notice }, draftStrict);
773
771
  const savedCondition = pending.pending_condition;
@@ -781,7 +779,7 @@ export class AgentMdManager {
781
779
  const authorization = entity ? entityAuthorization : delegated ? this._ownerUploadAuthorization(target, signedContent, owner) : '';
782
780
  const result = await this._uploadHttp(target, signedContent, authorization, condition);
783
781
  const remoteEtag = String(result.etag ?? '').trim();
784
- const saved = this._saveRecord(target, {
782
+ const saved = await this._saveCommittedRecord(target, {
785
783
  content: signedContent, local_etag: AgentMdManager.contentEtag(signedContent), remote_etag: remoteEtag || undefined,
786
784
  last_modified: String(result.last_modified ?? result.lastModified ?? '').trim(), fetched_at: Date.now(), checked_at: Date.now(),
787
785
  remote_status: remoteEtag ? 'found' : 'unknown', last_error: '',
@@ -808,8 +806,19 @@ export class AgentMdManager {
808
806
  throw ownershipFailure('invalid', 'stored legacy agent.md signature is invalid');
809
807
  return parsed;
810
808
  }
809
+ // 普通正文更新先严格读取以取得 NS 锁;关系 invalid/unavailable 时不阻断,回落普通原件读取,锁未知
810
+ async _readBodyOriginalFresh(target) {
811
+ try {
812
+ return await this.readOwnershipFresh(target);
813
+ }
814
+ catch (error) {
815
+ if (!(error instanceof AUNError) || !['invalid', 'unavailable'].includes(error.stringCode))
816
+ throw error;
817
+ return { ...await this.readAgentMdFresh(target), lock: 'unknown' };
818
+ }
819
+ }
811
820
  async _readLegacyBodyBase(target) {
812
- const response = await this.readAgentMdFresh(target);
821
+ const response = await this._readBodyOriginalFresh(target);
813
822
  if (response.exists)
814
823
  await this._verifyLegacyBodyContent(target, response.content);
815
824
  return response;
@@ -832,7 +841,7 @@ export class AgentMdManager {
832
841
  if (!remote.exists)
833
842
  throw ownershipFailure('conflict', 'pending legacy agent.md original document is missing');
834
843
  if (remote.content === pending.content && remote.etag === pending.content_etag) {
835
- const saved = this.saveRecord(target, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
844
+ const saved = await this._saveCommittedRecord(target, { content: pending.content, remote_etag: remote.etag, pending_operation: '', pending_content: '', pending_content_etag: '', pending_error: '', pending_condition: {}, pending_relation: {}, pending_authorization: '' });
836
845
  if (!saved.aid)
837
846
  throw new StateError('agent.md pending completion persistence failed');
838
847
  this._clearConfirmedDraft(target, pending.content);