chainlesschain 0.51.0 → 0.81.0
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/package.json +1 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-Rvi759IS.js → AppLayout-6SPt_8Y_.js} +1 -1
- package/src/assets/web-panel/assets/{Dashboard-DBhFxXYQ.js → Dashboard-Br7kCwKJ.js} +2 -2
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +1 -0
- package/src/assets/web-panel/assets/{index-uL0cZ8N_.js → index-tN-8TosE.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/a2a.js +380 -0
- package/src/commands/agent-network.js +785 -0
- package/src/commands/automation.js +654 -0
- package/src/commands/bi.js +348 -0
- package/src/commands/crosschain.js +218 -0
- package/src/commands/dao.js +565 -0
- package/src/commands/did-v2.js +620 -0
- package/src/commands/dlp.js +341 -0
- package/src/commands/economy.js +578 -0
- package/src/commands/evolution.js +391 -0
- package/src/commands/evomap.js +394 -0
- package/src/commands/federation.js +283 -0
- package/src/commands/hmemory.js +442 -0
- package/src/commands/inference.js +318 -0
- package/src/commands/lowcode.js +356 -0
- package/src/commands/marketplace.js +256 -0
- package/src/commands/perf.js +433 -0
- package/src/commands/pipeline.js +449 -0
- package/src/commands/plugin-ecosystem.js +517 -0
- package/src/commands/privacy.js +321 -0
- package/src/commands/reputation.js +261 -0
- package/src/commands/sandbox.js +401 -0
- package/src/commands/siem.js +246 -0
- package/src/commands/sla.js +259 -0
- package/src/commands/social.js +311 -0
- package/src/commands/sso.js +798 -0
- package/src/commands/stress.js +230 -0
- package/src/commands/terraform.js +245 -0
- package/src/commands/workflow.js +320 -0
- package/src/commands/zkp.js +562 -1
- package/src/index.js +21 -0
- package/src/lib/a2a-protocol.js +451 -0
- package/src/lib/agent-economy.js +479 -0
- package/src/lib/agent-network.js +1121 -0
- package/src/lib/app-builder.js +239 -0
- package/src/lib/automation-engine.js +948 -0
- package/src/lib/bi-engine.js +338 -0
- package/src/lib/cross-chain.js +345 -0
- package/src/lib/dao-governance.js +569 -0
- package/src/lib/did-v2-manager.js +1127 -0
- package/src/lib/dlp-engine.js +389 -0
- package/src/lib/evolution-system.js +453 -0
- package/src/lib/evomap-federation.js +177 -0
- package/src/lib/evomap-governance.js +276 -0
- package/src/lib/federation-hardening.js +259 -0
- package/src/lib/hierarchical-memory.js +481 -0
- package/src/lib/inference-network.js +330 -0
- package/src/lib/perf-tuning.js +734 -0
- package/src/lib/pipeline-orchestrator.js +928 -0
- package/src/lib/plugin-ecosystem.js +1109 -0
- package/src/lib/privacy-computing.js +427 -0
- package/src/lib/reputation-optimizer.js +299 -0
- package/src/lib/sandbox-v2.js +306 -0
- package/src/lib/siem-exporter.js +333 -0
- package/src/lib/skill-marketplace.js +325 -0
- package/src/lib/sla-manager.js +275 -0
- package/src/lib/social-graph-analytics.js +707 -0
- package/src/lib/sso-manager.js +841 -0
- package/src/lib/stress-tester.js +330 -0
- package/src/lib/terraform-manager.js +363 -0
- package/src/lib/workflow-engine.js +454 -1
- package/src/lib/zkp-engine.js +523 -20
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +0 -1
package/src/lib/a2a-protocol.js
CHANGED
|
@@ -369,3 +369,454 @@ function _notifySubscribers(taskId, status) {
|
|
|
369
369
|
|
|
370
370
|
// Export for testing
|
|
371
371
|
export { _subscriptions };
|
|
372
|
+
|
|
373
|
+
// ─── V2 Canonical Surface (Phase 81) ─────────────────────────────
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Frozen task status enum (Phase 81 — adds INPUT_REQUIRED and CANCELED)
|
|
377
|
+
*/
|
|
378
|
+
export const TASK_STATUS_V2 = Object.freeze({
|
|
379
|
+
SUBMITTED: "submitted",
|
|
380
|
+
WORKING: "working",
|
|
381
|
+
INPUT_REQUIRED: "input-required",
|
|
382
|
+
COMPLETED: "completed",
|
|
383
|
+
FAILED: "failed",
|
|
384
|
+
CANCELED: "canceled",
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Frozen agent card status enum (Phase 81)
|
|
389
|
+
*/
|
|
390
|
+
export const CARD_STATUS_V2 = Object.freeze({
|
|
391
|
+
ACTIVE: "active",
|
|
392
|
+
INACTIVE: "inactive",
|
|
393
|
+
EXPIRED: "expired",
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Frozen subscription type enum (Phase 81)
|
|
398
|
+
*/
|
|
399
|
+
export const SUBSCRIPTION_TYPE = Object.freeze({
|
|
400
|
+
TASK_UPDATE: "task_update",
|
|
401
|
+
AGENT_STATUS: "agent_status",
|
|
402
|
+
CAPABILITY_CHANGE: "capability_change",
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Frozen capability-negotiation outcome enum (Phase 81)
|
|
407
|
+
*/
|
|
408
|
+
export const NEGOTIATION_RESULT = Object.freeze({
|
|
409
|
+
COMPATIBLE: "compatible",
|
|
410
|
+
PARTIAL: "partial",
|
|
411
|
+
INCOMPATIBLE: "incompatible",
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Task state machine — only allow documented transitions.
|
|
416
|
+
* Terminal: completed, failed, canceled.
|
|
417
|
+
*/
|
|
418
|
+
const _allowedTaskTransitions = Object.freeze({
|
|
419
|
+
submitted: new Set(["working", "canceled", "failed"]),
|
|
420
|
+
working: new Set(["input-required", "completed", "failed", "canceled"]),
|
|
421
|
+
"input-required": new Set(["working", "canceled", "failed"]),
|
|
422
|
+
completed: new Set(),
|
|
423
|
+
failed: new Set(),
|
|
424
|
+
canceled: new Set(),
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Agent card status transitions.
|
|
429
|
+
*/
|
|
430
|
+
const _allowedCardTransitions = Object.freeze({
|
|
431
|
+
active: new Set(["inactive", "expired"]),
|
|
432
|
+
inactive: new Set(["active", "expired"]),
|
|
433
|
+
expired: new Set(["active"]),
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
// In-memory V2 state (parallel to existing DB-backed surface)
|
|
437
|
+
const _v2Tasks = new Map(); // taskId → { agentId, status, input, output, error, history[], deadline, inputPrompt, cancelReason, createdAt, updatedAt }
|
|
438
|
+
const _v2Cards = new Map(); // cardId → { cardId, status, updatedAt }
|
|
439
|
+
const _v2TypedSubs = new Map(); // key `${type}:${resourceId}` → Set<callback>
|
|
440
|
+
|
|
441
|
+
// ─── Card status & validation ────────────────────────────────────
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Validate an A2A agent card against the Phase 81 schema.
|
|
445
|
+
* Returns { valid, errors[] } — does not throw.
|
|
446
|
+
*/
|
|
447
|
+
export function validateAgentCard(card) {
|
|
448
|
+
const errors = [];
|
|
449
|
+
if (!card || typeof card !== "object") {
|
|
450
|
+
return { valid: false, errors: ["card must be an object"] };
|
|
451
|
+
}
|
|
452
|
+
if (!card.name || typeof card.name !== "string") {
|
|
453
|
+
errors.push("name is required and must be a string");
|
|
454
|
+
}
|
|
455
|
+
if (card.capabilities !== undefined && !Array.isArray(card.capabilities)) {
|
|
456
|
+
errors.push("capabilities must be an array");
|
|
457
|
+
}
|
|
458
|
+
if (card.skills !== undefined && !Array.isArray(card.skills)) {
|
|
459
|
+
errors.push("skills must be an array");
|
|
460
|
+
}
|
|
461
|
+
if (card.url !== undefined && typeof card.url !== "string") {
|
|
462
|
+
errors.push("url must be a string");
|
|
463
|
+
}
|
|
464
|
+
if (card.version !== undefined && !/^\d+\.\d+\.\d+$/.test(card.version)) {
|
|
465
|
+
errors.push("version must follow semver major.minor.patch");
|
|
466
|
+
}
|
|
467
|
+
if (
|
|
468
|
+
card.auth_type !== undefined &&
|
|
469
|
+
!["none", "bearer", "basic", "oauth2"].includes(card.auth_type)
|
|
470
|
+
) {
|
|
471
|
+
errors.push("auth_type must be one of none|bearer|basic|oauth2");
|
|
472
|
+
}
|
|
473
|
+
return { valid: errors.length === 0, errors };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Transition an agent card between active/inactive/expired.
|
|
478
|
+
*/
|
|
479
|
+
export function setCardStatus(db, cardId, status) {
|
|
480
|
+
if (!cardId) throw new Error("Card ID is required");
|
|
481
|
+
if (!Object.values(CARD_STATUS_V2).includes(status)) {
|
|
482
|
+
throw new Error(`Invalid card status: ${status}`);
|
|
483
|
+
}
|
|
484
|
+
const prev = _v2Cards.get(cardId)?.status || "active";
|
|
485
|
+
const allowed = _allowedCardTransitions[prev] || new Set();
|
|
486
|
+
if (prev !== status && !allowed.has(status)) {
|
|
487
|
+
throw new Error(`Invalid card transition: ${prev} → ${status}`);
|
|
488
|
+
}
|
|
489
|
+
const now = nowISO();
|
|
490
|
+
_v2Cards.set(cardId, { cardId, status, updatedAt: now });
|
|
491
|
+
if (db) {
|
|
492
|
+
try {
|
|
493
|
+
ensureA2ATables(db);
|
|
494
|
+
db.prepare(
|
|
495
|
+
`UPDATE a2a_agent_cards SET status = ?, updated_at = ? WHERE id = ?`,
|
|
496
|
+
).run(status, now, cardId);
|
|
497
|
+
} catch (_err) {
|
|
498
|
+
// DB may not support UPDATE in mock — in-memory state is authoritative
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return { cardId, status, updatedAt: now };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get a card's V2 status (falls back to 'active' if no V2 entry exists).
|
|
506
|
+
*/
|
|
507
|
+
export function getCardStatusV2(cardId) {
|
|
508
|
+
return _v2Cards.get(cardId)?.status || "active";
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// ─── Task lifecycle V2 ───────────────────────────────────────────
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Submit a task with optional timeout tracking.
|
|
515
|
+
*/
|
|
516
|
+
export function sendTaskV2(db, { agentId, input, timeoutMs }) {
|
|
517
|
+
if (!agentId) throw new Error("Agent ID is required");
|
|
518
|
+
if (!input) throw new Error("Task input is required");
|
|
519
|
+
const taskId = generateId("task");
|
|
520
|
+
const now = nowISO();
|
|
521
|
+
const deadline =
|
|
522
|
+
typeof timeoutMs === "number" && timeoutMs > 0
|
|
523
|
+
? Date.now() + timeoutMs
|
|
524
|
+
: null;
|
|
525
|
+
const task = {
|
|
526
|
+
taskId,
|
|
527
|
+
agentId,
|
|
528
|
+
status: TASK_STATUS_V2.SUBMITTED,
|
|
529
|
+
input,
|
|
530
|
+
output: "",
|
|
531
|
+
error: "",
|
|
532
|
+
history: [{ status: TASK_STATUS_V2.SUBMITTED, timestamp: now }],
|
|
533
|
+
deadline,
|
|
534
|
+
inputPrompt: null,
|
|
535
|
+
cancelReason: null,
|
|
536
|
+
createdAt: now,
|
|
537
|
+
updatedAt: now,
|
|
538
|
+
};
|
|
539
|
+
_v2Tasks.set(taskId, task);
|
|
540
|
+
_notifyTyped(SUBSCRIPTION_TYPE.TASK_UPDATE, taskId, {
|
|
541
|
+
taskId,
|
|
542
|
+
status: task.status,
|
|
543
|
+
});
|
|
544
|
+
return { taskId, status: task.status, deadline };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function _transitionTask(taskId, nextStatus, patch = {}) {
|
|
548
|
+
const task = _v2Tasks.get(taskId);
|
|
549
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
550
|
+
const allowed = _allowedTaskTransitions[task.status] || new Set();
|
|
551
|
+
if (!allowed.has(nextStatus)) {
|
|
552
|
+
throw new Error(`Invalid task transition: ${task.status} → ${nextStatus}`);
|
|
553
|
+
}
|
|
554
|
+
const now = nowISO();
|
|
555
|
+
task.status = nextStatus;
|
|
556
|
+
task.updatedAt = now;
|
|
557
|
+
task.history.push({
|
|
558
|
+
status: nextStatus,
|
|
559
|
+
timestamp: now,
|
|
560
|
+
...patch.historyExtra,
|
|
561
|
+
});
|
|
562
|
+
if (patch.output !== undefined) task.output = patch.output;
|
|
563
|
+
if (patch.error !== undefined) task.error = patch.error;
|
|
564
|
+
if (patch.inputPrompt !== undefined) task.inputPrompt = patch.inputPrompt;
|
|
565
|
+
if (patch.cancelReason !== undefined) task.cancelReason = patch.cancelReason;
|
|
566
|
+
_notifyTyped(SUBSCRIPTION_TYPE.TASK_UPDATE, taskId, {
|
|
567
|
+
taskId,
|
|
568
|
+
status: nextStatus,
|
|
569
|
+
});
|
|
570
|
+
return { taskId, status: nextStatus };
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Move a task from submitted → working (or input-required → working).
|
|
575
|
+
*/
|
|
576
|
+
export function startWorking(_db, taskId) {
|
|
577
|
+
return _transitionTask(taskId, TASK_STATUS_V2.WORKING);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Request user input while working → input-required.
|
|
582
|
+
*/
|
|
583
|
+
export function requestInput(_db, taskId, prompt) {
|
|
584
|
+
if (!prompt) throw new Error("Prompt is required");
|
|
585
|
+
return _transitionTask(taskId, TASK_STATUS_V2.INPUT_REQUIRED, {
|
|
586
|
+
inputPrompt: prompt,
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Provide requested input; input-required → working (clears prompt).
|
|
592
|
+
*/
|
|
593
|
+
export function provideInput(_db, taskId, responseInput) {
|
|
594
|
+
const task = _v2Tasks.get(taskId);
|
|
595
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
596
|
+
if (task.status !== TASK_STATUS_V2.INPUT_REQUIRED) {
|
|
597
|
+
throw new Error(
|
|
598
|
+
`provideInput requires status input-required, got ${task.status}`,
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
const result = _transitionTask(taskId, TASK_STATUS_V2.WORKING, {
|
|
602
|
+
inputPrompt: null,
|
|
603
|
+
historyExtra: { response: responseInput },
|
|
604
|
+
});
|
|
605
|
+
task.input = `${task.input}\n${responseInput}`;
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Mark task complete (from working only).
|
|
611
|
+
*/
|
|
612
|
+
export function completeTaskV2(_db, taskId, output, artifacts = []) {
|
|
613
|
+
const task = _v2Tasks.get(taskId);
|
|
614
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
615
|
+
const res = _transitionTask(taskId, TASK_STATUS_V2.COMPLETED, {
|
|
616
|
+
output: output || "",
|
|
617
|
+
});
|
|
618
|
+
task.artifacts = artifacts;
|
|
619
|
+
return res;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* Mark task failed.
|
|
624
|
+
*/
|
|
625
|
+
export function failTaskV2(_db, taskId, error) {
|
|
626
|
+
return _transitionTask(taskId, TASK_STATUS_V2.FAILED, {
|
|
627
|
+
error: error || "Unknown error",
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* Cancel a non-terminal task.
|
|
633
|
+
*/
|
|
634
|
+
export function cancelTask(_db, taskId, reason) {
|
|
635
|
+
return _transitionTask(taskId, TASK_STATUS_V2.CANCELED, {
|
|
636
|
+
cancelReason: reason || "user_requested",
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Check task timeout — if past deadline and not terminal, auto-fails.
|
|
642
|
+
* Returns { timedOut: boolean, status }.
|
|
643
|
+
*/
|
|
644
|
+
export function checkTaskTimeout(_db, taskId, now = Date.now()) {
|
|
645
|
+
const task = _v2Tasks.get(taskId);
|
|
646
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
647
|
+
const terminal = [
|
|
648
|
+
TASK_STATUS_V2.COMPLETED,
|
|
649
|
+
TASK_STATUS_V2.FAILED,
|
|
650
|
+
TASK_STATUS_V2.CANCELED,
|
|
651
|
+
];
|
|
652
|
+
if (terminal.includes(task.status)) {
|
|
653
|
+
return { timedOut: false, status: task.status };
|
|
654
|
+
}
|
|
655
|
+
if (task.deadline && now >= task.deadline) {
|
|
656
|
+
_transitionTask(taskId, TASK_STATUS_V2.FAILED, { error: "timeout" });
|
|
657
|
+
return { timedOut: true, status: TASK_STATUS_V2.FAILED };
|
|
658
|
+
}
|
|
659
|
+
return { timedOut: false, status: task.status };
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Get V2 task snapshot.
|
|
664
|
+
*/
|
|
665
|
+
export function getTaskV2(taskId) {
|
|
666
|
+
const task = _v2Tasks.get(taskId);
|
|
667
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
668
|
+
return { ...task, history: [...task.history] };
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* List V2 tasks (optional filter by agentId / status).
|
|
673
|
+
*/
|
|
674
|
+
export function listTasksV2({ agentId, status } = {}) {
|
|
675
|
+
const out = [];
|
|
676
|
+
for (const task of _v2Tasks.values()) {
|
|
677
|
+
if (agentId && task.agentId !== agentId) continue;
|
|
678
|
+
if (status && task.status !== status) continue;
|
|
679
|
+
out.push({ ...task, history: [...task.history] });
|
|
680
|
+
}
|
|
681
|
+
return out;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// ─── Typed subscriptions ─────────────────────────────────────────
|
|
685
|
+
|
|
686
|
+
function _notifyTyped(type, resourceId, payload) {
|
|
687
|
+
const key = `${type}:${resourceId}`;
|
|
688
|
+
const subs = _v2TypedSubs.get(key);
|
|
689
|
+
if (subs) {
|
|
690
|
+
for (const cb of subs) {
|
|
691
|
+
try {
|
|
692
|
+
cb(payload);
|
|
693
|
+
} catch (_err) {
|
|
694
|
+
// Subscriber error should not break lifecycle
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Subscribe with typed filter.
|
|
702
|
+
*/
|
|
703
|
+
export function subscribeTyped(type, resourceId, callback) {
|
|
704
|
+
if (!Object.values(SUBSCRIPTION_TYPE).includes(type)) {
|
|
705
|
+
throw new Error(`Invalid subscription type: ${type}`);
|
|
706
|
+
}
|
|
707
|
+
if (!resourceId) throw new Error("resourceId is required");
|
|
708
|
+
const key = `${type}:${resourceId}`;
|
|
709
|
+
if (!_v2TypedSubs.has(key)) _v2TypedSubs.set(key, new Set());
|
|
710
|
+
_v2TypedSubs.get(key).add(callback);
|
|
711
|
+
return () => {
|
|
712
|
+
const subs = _v2TypedSubs.get(key);
|
|
713
|
+
if (subs) {
|
|
714
|
+
subs.delete(callback);
|
|
715
|
+
if (subs.size === 0) _v2TypedSubs.delete(key);
|
|
716
|
+
}
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// ─── Capability negotiation V2 ───────────────────────────────────
|
|
721
|
+
|
|
722
|
+
function _parseVersion(v) {
|
|
723
|
+
if (typeof v !== "string") return null;
|
|
724
|
+
const m = v.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
725
|
+
if (!m) return null;
|
|
726
|
+
return { major: +m[1], minor: +m[2], patch: +m[3] };
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function _semverCompatible(clientVer, serverVer) {
|
|
730
|
+
const c = _parseVersion(clientVer);
|
|
731
|
+
const s = _parseVersion(serverVer);
|
|
732
|
+
if (!c || !s) return true; // Unknown versions → assume compatible
|
|
733
|
+
if (c.major !== s.major) return false;
|
|
734
|
+
// Minor: client can be ≤ server minor
|
|
735
|
+
if (c.minor > s.minor) return false;
|
|
736
|
+
return true;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Negotiate capabilities against an agent's declared skills.
|
|
741
|
+
* @param {object} opts — { required: string[], preferred?: string[], version?: string }
|
|
742
|
+
* @param {object} agentCard — { capabilities: string[], version?: string }
|
|
743
|
+
*/
|
|
744
|
+
export function negotiateCapabilityV2(
|
|
745
|
+
agentCard,
|
|
746
|
+
{ required = [], preferred = [], version },
|
|
747
|
+
) {
|
|
748
|
+
if (!agentCard || typeof agentCard !== "object") {
|
|
749
|
+
throw new Error("agentCard is required");
|
|
750
|
+
}
|
|
751
|
+
const caps = Array.isArray(agentCard.capabilities)
|
|
752
|
+
? agentCard.capabilities
|
|
753
|
+
: [];
|
|
754
|
+
const missingRequired = required.filter((c) => !caps.includes(c));
|
|
755
|
+
const supportedPreferred = preferred.filter((c) => caps.includes(c));
|
|
756
|
+
const missingPreferred = preferred.filter((c) => !caps.includes(c));
|
|
757
|
+
const versionOk = version
|
|
758
|
+
? _semverCompatible(version, agentCard.version)
|
|
759
|
+
: true;
|
|
760
|
+
|
|
761
|
+
let result;
|
|
762
|
+
if (!versionOk || missingRequired.length > 0) {
|
|
763
|
+
result = NEGOTIATION_RESULT.INCOMPATIBLE;
|
|
764
|
+
} else if (missingPreferred.length > 0) {
|
|
765
|
+
result = NEGOTIATION_RESULT.PARTIAL;
|
|
766
|
+
} else {
|
|
767
|
+
result = NEGOTIATION_RESULT.COMPATIBLE;
|
|
768
|
+
}
|
|
769
|
+
return {
|
|
770
|
+
result,
|
|
771
|
+
missingRequired,
|
|
772
|
+
supportedPreferred,
|
|
773
|
+
missingPreferred,
|
|
774
|
+
versionOk,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// ─── Stats V2 ────────────────────────────────────────────────────
|
|
779
|
+
|
|
780
|
+
export function getA2AStatsV2() {
|
|
781
|
+
const byStatus = {};
|
|
782
|
+
for (const v of Object.values(TASK_STATUS_V2)) byStatus[v] = 0;
|
|
783
|
+
let withDeadline = 0;
|
|
784
|
+
let canceled = 0;
|
|
785
|
+
for (const task of _v2Tasks.values()) {
|
|
786
|
+
byStatus[task.status] = (byStatus[task.status] || 0) + 1;
|
|
787
|
+
if (task.deadline) withDeadline += 1;
|
|
788
|
+
if (task.cancelReason) canceled += 1;
|
|
789
|
+
}
|
|
790
|
+
const cardsByStatus = {};
|
|
791
|
+
for (const v of Object.values(CARD_STATUS_V2)) cardsByStatus[v] = 0;
|
|
792
|
+
for (const c of _v2Cards.values()) {
|
|
793
|
+
cardsByStatus[c.status] = (cardsByStatus[c.status] || 0) + 1;
|
|
794
|
+
}
|
|
795
|
+
return {
|
|
796
|
+
tasks: {
|
|
797
|
+
total: _v2Tasks.size,
|
|
798
|
+
byStatus,
|
|
799
|
+
withDeadline,
|
|
800
|
+
canceledWithReason: canceled,
|
|
801
|
+
},
|
|
802
|
+
cards: {
|
|
803
|
+
tracked: _v2Cards.size,
|
|
804
|
+
byStatus: cardsByStatus,
|
|
805
|
+
},
|
|
806
|
+
subscriptions: {
|
|
807
|
+
legacy: _subscriptions.size,
|
|
808
|
+
typed: _v2TypedSubs.size,
|
|
809
|
+
},
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Reset V2-only in-memory state (for tests).
|
|
815
|
+
*/
|
|
816
|
+
export function _resetV2State() {
|
|
817
|
+
_v2Tasks.clear();
|
|
818
|
+
_v2Cards.clear();
|
|
819
|
+
_v2TypedSubs.clear();
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
export { _v2Tasks, _v2Cards, _v2TypedSubs };
|