@mnemom/agent-alignment-protocol 0.5.0 → 0.6.1
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/README.md +1 -1
- package/dist/index.d.mts +103 -1
- package/dist/index.d.ts +103 -1
- package/dist/index.js +296 -1
- package/dist/index.mjs +294 -1
- package/package.json +3 -4
- package/src/index.ts +7 -1
- package/src/verification/api.ts +399 -1
- package/src/verification/models.ts +81 -0
package/dist/index.mjs
CHANGED
|
@@ -655,6 +655,297 @@ function detectDrift(card, traces, similarityThreshold = DEFAULT_SIMILARITY_THRE
|
|
|
655
655
|
}
|
|
656
656
|
return alerts;
|
|
657
657
|
}
|
|
658
|
+
function deterministicHex(input, length) {
|
|
659
|
+
let hash = 0;
|
|
660
|
+
for (let i = 0; i < input.length; i++) {
|
|
661
|
+
const char = input.charCodeAt(i);
|
|
662
|
+
hash = (hash << 5) - hash + char;
|
|
663
|
+
hash = hash & hash;
|
|
664
|
+
}
|
|
665
|
+
return Math.abs(hash).toString(16).padStart(length, "0").slice(0, length);
|
|
666
|
+
}
|
|
667
|
+
function jaccardSimilarity(a, b) {
|
|
668
|
+
const setA = new Set(a);
|
|
669
|
+
const setB = new Set(b);
|
|
670
|
+
const intersection = [...setA].filter((x) => setB.has(x)).length;
|
|
671
|
+
const union = (/* @__PURE__ */ new Set([...setA, ...setB])).size;
|
|
672
|
+
return union === 0 ? 0 : intersection / union;
|
|
673
|
+
}
|
|
674
|
+
var ROLE_KEYWORDS = [
|
|
675
|
+
"safety",
|
|
676
|
+
"executive",
|
|
677
|
+
"cfo",
|
|
678
|
+
"analyst",
|
|
679
|
+
"compliance",
|
|
680
|
+
"legal",
|
|
681
|
+
"risk",
|
|
682
|
+
"finance",
|
|
683
|
+
"security",
|
|
684
|
+
"ethics",
|
|
685
|
+
"audit",
|
|
686
|
+
"ops",
|
|
687
|
+
"operations"
|
|
688
|
+
];
|
|
689
|
+
function hasRoleKeyword(agentId) {
|
|
690
|
+
const lower = agentId.toLowerCase();
|
|
691
|
+
return ROLE_KEYWORDS.some((kw) => lower.includes(kw));
|
|
692
|
+
}
|
|
693
|
+
function getClpiRole(card) {
|
|
694
|
+
const ext = card.extensions;
|
|
695
|
+
if (!ext) return null;
|
|
696
|
+
const clpi = ext["clpi"];
|
|
697
|
+
if (!clpi || typeof clpi["role"] !== "string") return null;
|
|
698
|
+
return clpi["role"] || null;
|
|
699
|
+
}
|
|
700
|
+
function analyzeFaultLines(coherenceResult, cards, options) {
|
|
701
|
+
const reputationScores = options?.reputationScores;
|
|
702
|
+
const agentBoundedActions = /* @__PURE__ */ new Map();
|
|
703
|
+
for (const { agentId, card } of cards) {
|
|
704
|
+
agentBoundedActions.set(agentId, card.autonomy_envelope?.bounded_actions ?? []);
|
|
705
|
+
}
|
|
706
|
+
const agentConflictMap = /* @__PURE__ */ new Map();
|
|
707
|
+
for (const { agentId, card } of cards) {
|
|
708
|
+
agentConflictMap.set(agentId, new Set(card.values.conflicts_with ?? []));
|
|
709
|
+
}
|
|
710
|
+
const agentRoleMap = /* @__PURE__ */ new Map();
|
|
711
|
+
for (const { agentId, card } of cards) {
|
|
712
|
+
agentRoleMap.set(agentId, getClpiRole(card));
|
|
713
|
+
}
|
|
714
|
+
const faultLines = [];
|
|
715
|
+
for (const divergence of coherenceResult.divergence_report) {
|
|
716
|
+
const {
|
|
717
|
+
value,
|
|
718
|
+
agents_declaring,
|
|
719
|
+
agents_missing,
|
|
720
|
+
agents_conflicting,
|
|
721
|
+
impact_on_fleet_score
|
|
722
|
+
} = divergence;
|
|
723
|
+
const involvedAgents = [
|
|
724
|
+
.../* @__PURE__ */ new Set([...agents_declaring, ...agents_missing, ...agents_conflicting])
|
|
725
|
+
];
|
|
726
|
+
let classification;
|
|
727
|
+
if (agents_conflicting.length > 0) {
|
|
728
|
+
classification = "incompatible";
|
|
729
|
+
} else if (agents_declaring.length >= 2 && (() => {
|
|
730
|
+
for (let i = 0; i < agents_declaring.length; i++) {
|
|
731
|
+
for (let j = i + 1; j < agents_declaring.length; j++) {
|
|
732
|
+
const idA = agents_declaring[i];
|
|
733
|
+
const idB = agents_declaring[j];
|
|
734
|
+
const entry = coherenceResult.pairwise_matrix.find(
|
|
735
|
+
(p) => p.agent_a === idA && p.agent_b === idB || p.agent_a === idB && p.agent_b === idA
|
|
736
|
+
);
|
|
737
|
+
if (entry && entry.result.score < 0.5) {
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return false;
|
|
743
|
+
})()) {
|
|
744
|
+
classification = "priority_mismatch";
|
|
745
|
+
} else if (agents_declaring.length >= 1 && agents_missing.length >= 1 && (() => {
|
|
746
|
+
const declaringRoles = new Set(agents_declaring.map((id2) => agentRoleMap.get(id2) ?? null).filter(Boolean));
|
|
747
|
+
const missingRoles = new Set(agents_missing.map((id2) => agentRoleMap.get(id2) ?? null).filter(Boolean));
|
|
748
|
+
if (declaringRoles.size > 0) {
|
|
749
|
+
const declaringRoleArr = [...declaringRoles];
|
|
750
|
+
const isRoleExclusive = declaringRoleArr.every((role) => !missingRoles.has(role));
|
|
751
|
+
if (isRoleExclusive) return true;
|
|
752
|
+
}
|
|
753
|
+
const allInvolved = [...agents_declaring, ...agents_missing];
|
|
754
|
+
return allInvolved.some((id2) => hasRoleKeyword(id2));
|
|
755
|
+
})()) {
|
|
756
|
+
classification = "complementary";
|
|
757
|
+
} else {
|
|
758
|
+
classification = "resolvable";
|
|
759
|
+
}
|
|
760
|
+
let coordinationOverlap;
|
|
761
|
+
if (involvedAgents.length < 2) {
|
|
762
|
+
coordinationOverlap = 0.5;
|
|
763
|
+
} else {
|
|
764
|
+
const actionSets = involvedAgents.map((id2) => agentBoundedActions.get(id2) ?? []);
|
|
765
|
+
const nonEmpty = actionSets.filter((s) => s.length > 0);
|
|
766
|
+
if (nonEmpty.length < 2) {
|
|
767
|
+
coordinationOverlap = 0.5;
|
|
768
|
+
} else {
|
|
769
|
+
let total = 0;
|
|
770
|
+
let count = 0;
|
|
771
|
+
for (let i = 0; i < nonEmpty.length; i++) {
|
|
772
|
+
for (let j = i + 1; j < nonEmpty.length; j++) {
|
|
773
|
+
total += jaccardSimilarity(nonEmpty[i], nonEmpty[j]);
|
|
774
|
+
count++;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
coordinationOverlap = count > 0 ? total / count : 0.5;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
let impactScore = impact_on_fleet_score * coordinationOverlap;
|
|
781
|
+
if (reputationScores && involvedAgents.length > 0) {
|
|
782
|
+
const repValues = involvedAgents.map((id2) => (reputationScores[id2] ?? 500) / 1e3).map((r) => Math.max(1e-3, r));
|
|
783
|
+
const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
|
|
784
|
+
const geoMean = Math.exp(logSum / repValues.length);
|
|
785
|
+
impactScore *= geoMean;
|
|
786
|
+
}
|
|
787
|
+
impactScore = Math.min(1, Math.max(0, impactScore));
|
|
788
|
+
let severity;
|
|
789
|
+
if (impactScore >= 0.7) {
|
|
790
|
+
severity = "critical";
|
|
791
|
+
} else if (impactScore >= 0.4) {
|
|
792
|
+
severity = "high";
|
|
793
|
+
} else if (impactScore >= 0.2) {
|
|
794
|
+
severity = "medium";
|
|
795
|
+
} else {
|
|
796
|
+
severity = "low";
|
|
797
|
+
}
|
|
798
|
+
let resolutionHint;
|
|
799
|
+
switch (classification) {
|
|
800
|
+
case "resolvable":
|
|
801
|
+
resolutionHint = `Add value '${value}' to ${agents_missing.join(", ")} alignment card(s).`;
|
|
802
|
+
break;
|
|
803
|
+
case "priority_mismatch":
|
|
804
|
+
resolutionHint = `Align priority/definition of '${value}' across all declaring agents.`;
|
|
805
|
+
break;
|
|
806
|
+
case "incompatible":
|
|
807
|
+
resolutionHint = `Value '${value}' conflicts with ${agents_conflicting.join(", ")}. Requires human review.`;
|
|
808
|
+
break;
|
|
809
|
+
case "complementary":
|
|
810
|
+
resolutionHint = `Value '${value}' divergence appears intentional given agent specializations.`;
|
|
811
|
+
break;
|
|
812
|
+
}
|
|
813
|
+
let affectsCapabilities = [];
|
|
814
|
+
if (involvedAgents.length > 0) {
|
|
815
|
+
const firstActions = agentBoundedActions.get(involvedAgents[0]) ?? [];
|
|
816
|
+
affectsCapabilities = firstActions.filter(
|
|
817
|
+
(action) => involvedAgents.every((id2) => (agentBoundedActions.get(id2) ?? []).includes(action))
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
const idInput = [value, ...involvedAgents.sort()].join("|");
|
|
821
|
+
const id = deterministicHex(idInput, 12);
|
|
822
|
+
faultLines.push({
|
|
823
|
+
id,
|
|
824
|
+
value,
|
|
825
|
+
classification,
|
|
826
|
+
severity,
|
|
827
|
+
agents_declaring,
|
|
828
|
+
agents_missing,
|
|
829
|
+
agents_conflicting,
|
|
830
|
+
impact_score: Math.round(impactScore * 1e4) / 1e4,
|
|
831
|
+
resolution_hint: resolutionHint,
|
|
832
|
+
affects_capabilities: affectsCapabilities
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
const severityOrder = {
|
|
836
|
+
critical: 0,
|
|
837
|
+
high: 1,
|
|
838
|
+
medium: 2,
|
|
839
|
+
low: 3
|
|
840
|
+
};
|
|
841
|
+
faultLines.sort((a, b) => {
|
|
842
|
+
const sev = severityOrder[a.severity] - severityOrder[b.severity];
|
|
843
|
+
if (sev !== 0) return sev;
|
|
844
|
+
return b.impact_score - a.impact_score;
|
|
845
|
+
});
|
|
846
|
+
const alignments = [];
|
|
847
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
848
|
+
const groupAssignment = /* @__PURE__ */ new Map();
|
|
849
|
+
let nextGroupId = 0;
|
|
850
|
+
for (let i = 0; i < faultLines.length; i++) {
|
|
851
|
+
for (let j = i + 1; j < faultLines.length; j++) {
|
|
852
|
+
const sim = jaccardSimilarity(faultLines[i].agents_missing, faultLines[j].agents_missing);
|
|
853
|
+
if (sim > 0.6) {
|
|
854
|
+
const gi = groupAssignment.get(i);
|
|
855
|
+
const gj = groupAssignment.get(j);
|
|
856
|
+
if (gi === void 0 && gj === void 0) {
|
|
857
|
+
const gid = nextGroupId++;
|
|
858
|
+
grouped.set(gid, [i, j]);
|
|
859
|
+
groupAssignment.set(i, gid);
|
|
860
|
+
groupAssignment.set(j, gid);
|
|
861
|
+
} else if (gi !== void 0 && gj === void 0) {
|
|
862
|
+
grouped.get(gi).push(j);
|
|
863
|
+
groupAssignment.set(j, gi);
|
|
864
|
+
} else if (gi === void 0 && gj !== void 0) {
|
|
865
|
+
grouped.get(gj).push(i);
|
|
866
|
+
groupAssignment.set(i, gj);
|
|
867
|
+
} else if (gi !== gj) {
|
|
868
|
+
const smaller = gi < gj ? gj : gi;
|
|
869
|
+
const larger = gi < gj ? gi : gj;
|
|
870
|
+
const smallerMembers = grouped.get(smaller) ?? [];
|
|
871
|
+
const largerMembers = grouped.get(larger) ?? [];
|
|
872
|
+
const merged = [.../* @__PURE__ */ new Set([...largerMembers, ...smallerMembers])];
|
|
873
|
+
grouped.set(larger, merged);
|
|
874
|
+
grouped.delete(smaller);
|
|
875
|
+
for (const idx of smallerMembers) {
|
|
876
|
+
groupAssignment.set(idx, larger);
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
for (const [, members] of grouped) {
|
|
883
|
+
if (members.length < 2) continue;
|
|
884
|
+
const unique = [...new Set(members)];
|
|
885
|
+
const groupFaultLines = unique.map((i) => faultLines[i]);
|
|
886
|
+
const minorityAgents = [
|
|
887
|
+
...new Set(groupFaultLines.flatMap((fl) => fl.agents_missing))
|
|
888
|
+
];
|
|
889
|
+
const majorityAgents = [
|
|
890
|
+
...new Set(groupFaultLines.flatMap((fl) => fl.agents_declaring))
|
|
891
|
+
];
|
|
892
|
+
let jaccardSum = 0;
|
|
893
|
+
let jaccardCount = 0;
|
|
894
|
+
for (let i = 0; i < unique.length; i++) {
|
|
895
|
+
for (let j = i + 1; j < unique.length; j++) {
|
|
896
|
+
jaccardSum += jaccardSimilarity(
|
|
897
|
+
groupFaultLines[i].agents_missing,
|
|
898
|
+
groupFaultLines[j].agents_missing
|
|
899
|
+
);
|
|
900
|
+
jaccardCount++;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
const alignmentScore = jaccardCount > 0 ? jaccardSum / jaccardCount : 0;
|
|
904
|
+
const hasHigherSeverity = groupFaultLines.some(
|
|
905
|
+
(fl) => fl.severity === "critical" || fl.severity === "high"
|
|
906
|
+
);
|
|
907
|
+
let severity = unique.length >= 3 ? "high" : "medium";
|
|
908
|
+
if (hasHigherSeverity && severity === "medium") {
|
|
909
|
+
severity = "high";
|
|
910
|
+
}
|
|
911
|
+
const sortedFaultLineIds = groupFaultLines.map((fl) => fl.id).sort();
|
|
912
|
+
const alignmentId = deterministicHex(sortedFaultLineIds.join("|"), 12);
|
|
913
|
+
alignments.push({
|
|
914
|
+
id: alignmentId,
|
|
915
|
+
fault_line_ids: sortedFaultLineIds,
|
|
916
|
+
minority_agents: minorityAgents,
|
|
917
|
+
majority_agents: majorityAgents,
|
|
918
|
+
alignment_score: Math.round(alignmentScore * 1e4) / 1e4,
|
|
919
|
+
severity,
|
|
920
|
+
description: `${groupFaultLines.length} fault lines consistently isolate ${minorityAgents.join(", ")} from the team`
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
const summary = {
|
|
924
|
+
total: faultLines.length,
|
|
925
|
+
resolvable: faultLines.filter((fl) => fl.classification === "resolvable").length,
|
|
926
|
+
priority_mismatch: faultLines.filter((fl) => fl.classification === "priority_mismatch").length,
|
|
927
|
+
incompatible: faultLines.filter((fl) => fl.classification === "incompatible").length,
|
|
928
|
+
complementary: faultLines.filter((fl) => fl.classification === "complementary").length,
|
|
929
|
+
critical_count: faultLines.filter((fl) => fl.severity === "critical").length
|
|
930
|
+
};
|
|
931
|
+
const analysisIdInput = [
|
|
932
|
+
String(coherenceResult.fleet_score),
|
|
933
|
+
...faultLines.map((fl) => fl.id).sort()
|
|
934
|
+
].join("|");
|
|
935
|
+
const analysisId = deterministicHex(analysisIdInput, 16);
|
|
936
|
+
return {
|
|
937
|
+
analysis_id: analysisId,
|
|
938
|
+
fleet_score: coherenceResult.fleet_score,
|
|
939
|
+
fault_lines: faultLines,
|
|
940
|
+
alignments,
|
|
941
|
+
summary
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
function checkFleetFaultLines(cards, options) {
|
|
945
|
+
const coherence = checkFleetCoherence(cards);
|
|
946
|
+
const analysis = analyzeFaultLines(coherence, cards, options);
|
|
947
|
+
return { coherence, analysis };
|
|
948
|
+
}
|
|
658
949
|
function evaluateCondition(condition, trace) {
|
|
659
950
|
if (!condition) {
|
|
660
951
|
return false;
|
|
@@ -665,7 +956,7 @@ function evaluateCondition(condition, trace) {
|
|
|
665
956
|
const actual = trace.action.type ?? "";
|
|
666
957
|
return actual === expected;
|
|
667
958
|
}
|
|
668
|
-
const numericMatch = condition.match(
|
|
959
|
+
const numericMatch = condition.match(/^\s*(\w+)\s*([><=!]+)\s*(\d+(?:\.\d+)?)\s*$/);
|
|
669
960
|
if (numericMatch) {
|
|
670
961
|
const [, field, op, valueStr] = numericMatch;
|
|
671
962
|
const value = parseFloat(valueStr);
|
|
@@ -830,8 +1121,10 @@ export {
|
|
|
830
1121
|
NEAR_BOUNDARY_THRESHOLD,
|
|
831
1122
|
OUTLIER_STD_DEV_THRESHOLD,
|
|
832
1123
|
VIOLATION_SEVERITY,
|
|
1124
|
+
analyzeFaultLines,
|
|
833
1125
|
checkCoherence,
|
|
834
1126
|
checkFleetCoherence,
|
|
1127
|
+
checkFleetFaultLines,
|
|
835
1128
|
computeCentroid,
|
|
836
1129
|
cosineSimilarity,
|
|
837
1130
|
createViolation,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mnemom/agent-alignment-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Agent Alignment Protocol (AAP) - Verification and drift detection for AI agents",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
24
24
|
"test": "vitest run",
|
|
25
25
|
"test:watch": "vitest",
|
|
26
|
-
|
|
27
26
|
"typecheck": "tsc --noEmit"
|
|
28
27
|
},
|
|
29
28
|
"keywords": [
|
|
@@ -47,7 +46,7 @@
|
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
48
|
"@types/node": "^20.0.0",
|
|
50
|
-
|
|
49
|
+
"@vitest/coverage-v8": "^1.6.1",
|
|
51
50
|
"tsup": "^8.0.0",
|
|
52
51
|
"typescript": "^5.0.0",
|
|
53
52
|
"vitest": "^1.0.0"
|
|
@@ -55,4 +54,4 @@
|
|
|
55
54
|
"engines": {
|
|
56
55
|
"node": ">=18.0.0"
|
|
57
56
|
}
|
|
58
|
-
}
|
|
57
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
37
|
// Main API exports
|
|
38
|
-
export { verifyTrace, checkCoherence, checkFleetCoherence, detectDrift } from "./verification/api";
|
|
38
|
+
export { verifyTrace, checkCoherence, checkFleetCoherence, detectDrift, analyzeFaultLines, checkFleetFaultLines } from "./verification/api";
|
|
39
39
|
|
|
40
40
|
// Schema types
|
|
41
41
|
export type {
|
|
@@ -114,6 +114,12 @@ export type {
|
|
|
114
114
|
FleetCluster,
|
|
115
115
|
ValueDivergence,
|
|
116
116
|
AgentCoherenceSummary,
|
|
117
|
+
// Fault Line Analysis (E-06)
|
|
118
|
+
FaultLineClassification,
|
|
119
|
+
FaultLine,
|
|
120
|
+
FaultLineSummary,
|
|
121
|
+
FaultLineAlignment,
|
|
122
|
+
FaultLineAnalysis,
|
|
117
123
|
} from "./verification/models";
|
|
118
124
|
|
|
119
125
|
// Utility exports
|