@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.
- package/CHANGELOG.md +104 -0
- package/_packed_docs/CHANGELOG.md +104 -0
- package/_packed_docs/INDEX.md +59 -34
- package/_packed_docs/KITE_DOCS_GUIDE.md +59 -52
- 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
- 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
- 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
- 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
- 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
- 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
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1433 -1392
- package/_packed_docs/protocol/08-AUN-E2EE.md +17 -12
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +30 -8
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +2 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +33 -22
- 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
- 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
- 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
- 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
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +1 -1
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +5 -1
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +64 -64
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +4 -1
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +42 -39
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +163 -14
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +48 -47
- package/_packed_docs/sdk/09-group-rpc-manual.md +15 -2
- package/_packed_docs/sdk/09-message-rpc-manual.md +250 -160
- package/_packed_docs/sdk/09-storage-rpc-manual.md +64 -7
- 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
- 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
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +33 -25
- package/_packed_docs/sdk/INDEX.md +67 -33
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +74 -57
- 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
- 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
- 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
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +28 -20
- 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
- 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
- package/dist/agent-md-schema.js +6 -4
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +22 -2
- package/dist/agent-md.js +92 -68
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +13 -0
- package/dist/aid-store.js +50 -15
- package/dist/aid-store.js.map +1 -1
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +22 -2
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +1 -0
- package/dist/cert-utils.js +13 -0
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +87 -1
- package/dist/client/delivery.js +1387 -139
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +3 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -1
- package/dist/client/rpc-pipeline.js +149 -37
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +12 -2
- package/dist/client/v2-e2ee.js +831 -304
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +26 -2
- package/dist/client.js +412 -68
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/events.d.ts +18 -1
- package/dist/events.js +148 -9
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +43 -1
- package/dist/facades.js +363 -7
- package/dist/facades.js.map +1 -1
- package/dist/group-fs.d.ts +1 -0
- package/dist/group-fs.js +37 -19
- package/dist/group-fs.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +6 -1
- package/dist/keystore/aid-db.js +184 -42
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +14 -0
- package/dist/keystore/local-token-store.d.ts +9 -1
- package/dist/keystore/local-token-store.js +79 -14
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +46 -0
- package/dist/org-protocol.js +802 -12
- package/dist/org-protocol.js.map +1 -1
- package/dist/ownership-v21.d.ts +36 -5
- package/dist/ownership-v21.js +168 -52
- package/dist/ownership-v21.js.map +1 -1
- package/dist/ownership-workflow.d.ts +24 -2
- package/dist/ownership-workflow.js +682 -41
- package/dist/ownership-workflow.js.map +1 -1
- package/dist/secret-store/file-store.js +84 -0
- package/dist/secret-store/file-store.js.map +1 -1
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +2 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/storage/lowlevel.d.ts +6 -0
- package/dist/storage/lowlevel.js +24 -7
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/read-cache.d.ts +70 -0
- package/dist/storage/read-cache.js +625 -0
- package/dist/storage/read-cache.js.map +1 -0
- package/dist/storage/vfs.d.ts +2 -0
- package/dist/storage/vfs.js +50 -15
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +86 -3
- package/dist/tools/cross-sdk-agent.js +758 -46
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +3 -0
- package/dist/transport.js +104 -46
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/v2/e2ee/encrypt-group.js +1 -0
- package/dist/v2/e2ee/encrypt-group.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -0
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/e2ee/types.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
462
|
-
|
|
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 !==
|
|
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 (
|
|
580
|
-
throw
|
|
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 (
|
|
591
|
-
throw
|
|
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.
|
|
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('
|
|
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('
|
|
630
|
-
|
|
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.
|
|
680
|
-
if (response.
|
|
681
|
-
|
|
682
|
-
|
|
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,
|