@mnemom/agent-alignment-protocol 0.5.0 → 0.6.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/README.md +1 -1
- package/dist/index.d.mts +103 -1
- package/dist/index.d.ts +103 -1
- package/dist/index.js +278 -1
- package/dist/index.mjs +276 -1
- package/package.json +2 -3
- package/src/index.ts +7 -1
- package/src/verification/api.ts +373 -1
- package/src/verification/models.ts +81 -0
package/dist/index.mjs
CHANGED
|
@@ -655,6 +655,279 @@ 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 analyzeFaultLines(coherenceResult, cards, options) {
|
|
694
|
+
const reputationScores = options?.reputationScores;
|
|
695
|
+
const agentBoundedActions = /* @__PURE__ */ new Map();
|
|
696
|
+
for (const { agentId, card } of cards) {
|
|
697
|
+
agentBoundedActions.set(agentId, card.autonomy_envelope?.bounded_actions ?? []);
|
|
698
|
+
}
|
|
699
|
+
const agentConflictMap = /* @__PURE__ */ new Map();
|
|
700
|
+
for (const { agentId, card } of cards) {
|
|
701
|
+
agentConflictMap.set(agentId, new Set(card.values.conflicts_with ?? []));
|
|
702
|
+
}
|
|
703
|
+
const faultLines = [];
|
|
704
|
+
for (const divergence of coherenceResult.divergence_report) {
|
|
705
|
+
const {
|
|
706
|
+
value,
|
|
707
|
+
agents_declaring,
|
|
708
|
+
agents_missing,
|
|
709
|
+
agents_conflicting,
|
|
710
|
+
impact_on_fleet_score
|
|
711
|
+
} = divergence;
|
|
712
|
+
const involvedAgents = [
|
|
713
|
+
.../* @__PURE__ */ new Set([...agents_declaring, ...agents_missing, ...agents_conflicting])
|
|
714
|
+
];
|
|
715
|
+
let classification;
|
|
716
|
+
if (agents_conflicting.length > 0) {
|
|
717
|
+
classification = "incompatible";
|
|
718
|
+
} else if (agents_declaring.length >= 2 && (() => {
|
|
719
|
+
for (let i = 0; i < agents_declaring.length; i++) {
|
|
720
|
+
for (let j = i + 1; j < agents_declaring.length; j++) {
|
|
721
|
+
const idA = agents_declaring[i];
|
|
722
|
+
const idB = agents_declaring[j];
|
|
723
|
+
const entry = coherenceResult.pairwise_matrix.find(
|
|
724
|
+
(p) => p.agent_a === idA && p.agent_b === idB || p.agent_a === idB && p.agent_b === idA
|
|
725
|
+
);
|
|
726
|
+
if (entry && entry.result.score < 0.5) {
|
|
727
|
+
return true;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return false;
|
|
732
|
+
})()) {
|
|
733
|
+
classification = "priority_mismatch";
|
|
734
|
+
} else if (agents_declaring.length >= 1 && agents_missing.length >= 1 && (() => {
|
|
735
|
+
const allInvolved = [...agents_declaring, ...agents_missing];
|
|
736
|
+
return allInvolved.some((id2) => hasRoleKeyword(id2));
|
|
737
|
+
})()) {
|
|
738
|
+
classification = "complementary";
|
|
739
|
+
} else {
|
|
740
|
+
classification = "resolvable";
|
|
741
|
+
}
|
|
742
|
+
let coordinationOverlap;
|
|
743
|
+
if (involvedAgents.length < 2) {
|
|
744
|
+
coordinationOverlap = 0.5;
|
|
745
|
+
} else {
|
|
746
|
+
const actionSets = involvedAgents.map((id2) => agentBoundedActions.get(id2) ?? []);
|
|
747
|
+
const nonEmpty = actionSets.filter((s) => s.length > 0);
|
|
748
|
+
if (nonEmpty.length < 2) {
|
|
749
|
+
coordinationOverlap = 0.5;
|
|
750
|
+
} else {
|
|
751
|
+
let total = 0;
|
|
752
|
+
let count = 0;
|
|
753
|
+
for (let i = 0; i < nonEmpty.length; i++) {
|
|
754
|
+
for (let j = i + 1; j < nonEmpty.length; j++) {
|
|
755
|
+
total += jaccardSimilarity(nonEmpty[i], nonEmpty[j]);
|
|
756
|
+
count++;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
coordinationOverlap = count > 0 ? total / count : 0.5;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
let impactScore = impact_on_fleet_score * coordinationOverlap;
|
|
763
|
+
if (reputationScores && involvedAgents.length > 0) {
|
|
764
|
+
const repValues = involvedAgents.map((id2) => (reputationScores[id2] ?? 500) / 1e3).map((r) => Math.max(1e-3, r));
|
|
765
|
+
const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
|
|
766
|
+
const geoMean = Math.exp(logSum / repValues.length);
|
|
767
|
+
impactScore *= geoMean;
|
|
768
|
+
}
|
|
769
|
+
impactScore = Math.min(1, Math.max(0, impactScore));
|
|
770
|
+
let severity;
|
|
771
|
+
if (impactScore >= 0.7) {
|
|
772
|
+
severity = "critical";
|
|
773
|
+
} else if (impactScore >= 0.4) {
|
|
774
|
+
severity = "high";
|
|
775
|
+
} else if (impactScore >= 0.2) {
|
|
776
|
+
severity = "medium";
|
|
777
|
+
} else {
|
|
778
|
+
severity = "low";
|
|
779
|
+
}
|
|
780
|
+
let resolutionHint;
|
|
781
|
+
switch (classification) {
|
|
782
|
+
case "resolvable":
|
|
783
|
+
resolutionHint = `Add value '${value}' to ${agents_missing.join(", ")} alignment card(s).`;
|
|
784
|
+
break;
|
|
785
|
+
case "priority_mismatch":
|
|
786
|
+
resolutionHint = `Align priority/definition of '${value}' across all declaring agents.`;
|
|
787
|
+
break;
|
|
788
|
+
case "incompatible":
|
|
789
|
+
resolutionHint = `Value '${value}' conflicts with ${agents_conflicting.join(", ")}. Requires human review.`;
|
|
790
|
+
break;
|
|
791
|
+
case "complementary":
|
|
792
|
+
resolutionHint = `Value '${value}' divergence appears intentional given agent specializations.`;
|
|
793
|
+
break;
|
|
794
|
+
}
|
|
795
|
+
let affectsCapabilities = [];
|
|
796
|
+
if (involvedAgents.length > 0) {
|
|
797
|
+
const firstActions = agentBoundedActions.get(involvedAgents[0]) ?? [];
|
|
798
|
+
affectsCapabilities = firstActions.filter(
|
|
799
|
+
(action) => involvedAgents.every((id2) => (agentBoundedActions.get(id2) ?? []).includes(action))
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
const idInput = [value, ...involvedAgents.sort()].join("|");
|
|
803
|
+
const id = deterministicHex(idInput, 12);
|
|
804
|
+
faultLines.push({
|
|
805
|
+
id,
|
|
806
|
+
value,
|
|
807
|
+
classification,
|
|
808
|
+
severity,
|
|
809
|
+
agents_declaring,
|
|
810
|
+
agents_missing,
|
|
811
|
+
agents_conflicting,
|
|
812
|
+
impact_score: Math.round(impactScore * 1e4) / 1e4,
|
|
813
|
+
resolution_hint: resolutionHint,
|
|
814
|
+
affects_capabilities: affectsCapabilities
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
const severityOrder = {
|
|
818
|
+
critical: 0,
|
|
819
|
+
high: 1,
|
|
820
|
+
medium: 2,
|
|
821
|
+
low: 3
|
|
822
|
+
};
|
|
823
|
+
faultLines.sort((a, b) => {
|
|
824
|
+
const sev = severityOrder[a.severity] - severityOrder[b.severity];
|
|
825
|
+
if (sev !== 0) return sev;
|
|
826
|
+
return b.impact_score - a.impact_score;
|
|
827
|
+
});
|
|
828
|
+
const alignments = [];
|
|
829
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
830
|
+
const groupAssignment = /* @__PURE__ */ new Map();
|
|
831
|
+
let nextGroupId = 0;
|
|
832
|
+
for (let i = 0; i < faultLines.length; i++) {
|
|
833
|
+
for (let j = i + 1; j < faultLines.length; j++) {
|
|
834
|
+
const sim = jaccardSimilarity(faultLines[i].agents_missing, faultLines[j].agents_missing);
|
|
835
|
+
if (sim > 0.6) {
|
|
836
|
+
const gi = groupAssignment.get(i);
|
|
837
|
+
const gj = groupAssignment.get(j);
|
|
838
|
+
if (gi === void 0 && gj === void 0) {
|
|
839
|
+
const gid = nextGroupId++;
|
|
840
|
+
grouped.set(gid, [i, j]);
|
|
841
|
+
groupAssignment.set(i, gid);
|
|
842
|
+
groupAssignment.set(j, gid);
|
|
843
|
+
} else if (gi !== void 0 && gj === void 0) {
|
|
844
|
+
grouped.get(gi).push(j);
|
|
845
|
+
groupAssignment.set(j, gi);
|
|
846
|
+
} else if (gi === void 0 && gj !== void 0) {
|
|
847
|
+
grouped.get(gj).push(i);
|
|
848
|
+
groupAssignment.set(i, gj);
|
|
849
|
+
} else if (gi !== gj) {
|
|
850
|
+
const smaller = gi < gj ? gj : gi;
|
|
851
|
+
const larger = gi < gj ? gi : gj;
|
|
852
|
+
const smallerMembers = grouped.get(smaller) ?? [];
|
|
853
|
+
const largerMembers = grouped.get(larger) ?? [];
|
|
854
|
+
const merged = [.../* @__PURE__ */ new Set([...largerMembers, ...smallerMembers])];
|
|
855
|
+
grouped.set(larger, merged);
|
|
856
|
+
grouped.delete(smaller);
|
|
857
|
+
for (const idx of smallerMembers) {
|
|
858
|
+
groupAssignment.set(idx, larger);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
for (const [, members] of grouped) {
|
|
865
|
+
if (members.length < 2) continue;
|
|
866
|
+
const unique = [...new Set(members)];
|
|
867
|
+
const groupFaultLines = unique.map((i) => faultLines[i]);
|
|
868
|
+
const minorityAgents = [
|
|
869
|
+
...new Set(groupFaultLines.flatMap((fl) => fl.agents_missing))
|
|
870
|
+
];
|
|
871
|
+
const majorityAgents = [
|
|
872
|
+
...new Set(groupFaultLines.flatMap((fl) => fl.agents_declaring))
|
|
873
|
+
];
|
|
874
|
+
let jaccardSum = 0;
|
|
875
|
+
let jaccardCount = 0;
|
|
876
|
+
for (let i = 0; i < unique.length; i++) {
|
|
877
|
+
for (let j = i + 1; j < unique.length; j++) {
|
|
878
|
+
jaccardSum += jaccardSimilarity(
|
|
879
|
+
groupFaultLines[i].agents_missing,
|
|
880
|
+
groupFaultLines[j].agents_missing
|
|
881
|
+
);
|
|
882
|
+
jaccardCount++;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
const alignmentScore = jaccardCount > 0 ? jaccardSum / jaccardCount : 0;
|
|
886
|
+
const hasHigherSeverity = groupFaultLines.some(
|
|
887
|
+
(fl) => fl.severity === "critical" || fl.severity === "high"
|
|
888
|
+
);
|
|
889
|
+
let severity = unique.length >= 3 ? "high" : "medium";
|
|
890
|
+
if (hasHigherSeverity && severity === "medium") {
|
|
891
|
+
severity = "high";
|
|
892
|
+
}
|
|
893
|
+
const sortedFaultLineIds = groupFaultLines.map((fl) => fl.id).sort();
|
|
894
|
+
const alignmentId = deterministicHex(sortedFaultLineIds.join("|"), 12);
|
|
895
|
+
alignments.push({
|
|
896
|
+
id: alignmentId,
|
|
897
|
+
fault_line_ids: sortedFaultLineIds,
|
|
898
|
+
minority_agents: minorityAgents,
|
|
899
|
+
majority_agents: majorityAgents,
|
|
900
|
+
alignment_score: Math.round(alignmentScore * 1e4) / 1e4,
|
|
901
|
+
severity,
|
|
902
|
+
description: `${groupFaultLines.length} fault lines consistently isolate ${minorityAgents.join(", ")} from the team`
|
|
903
|
+
});
|
|
904
|
+
}
|
|
905
|
+
const summary = {
|
|
906
|
+
total: faultLines.length,
|
|
907
|
+
resolvable: faultLines.filter((fl) => fl.classification === "resolvable").length,
|
|
908
|
+
priority_mismatch: faultLines.filter((fl) => fl.classification === "priority_mismatch").length,
|
|
909
|
+
incompatible: faultLines.filter((fl) => fl.classification === "incompatible").length,
|
|
910
|
+
complementary: faultLines.filter((fl) => fl.classification === "complementary").length,
|
|
911
|
+
critical_count: faultLines.filter((fl) => fl.severity === "critical").length
|
|
912
|
+
};
|
|
913
|
+
const analysisIdInput = [
|
|
914
|
+
String(coherenceResult.fleet_score),
|
|
915
|
+
...faultLines.map((fl) => fl.id).sort()
|
|
916
|
+
].join("|");
|
|
917
|
+
const analysisId = deterministicHex(analysisIdInput, 16);
|
|
918
|
+
return {
|
|
919
|
+
analysis_id: analysisId,
|
|
920
|
+
fleet_score: coherenceResult.fleet_score,
|
|
921
|
+
fault_lines: faultLines,
|
|
922
|
+
alignments,
|
|
923
|
+
summary
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
function checkFleetFaultLines(cards, options) {
|
|
927
|
+
const coherence = checkFleetCoherence(cards);
|
|
928
|
+
const analysis = analyzeFaultLines(coherence, cards, options);
|
|
929
|
+
return { coherence, analysis };
|
|
930
|
+
}
|
|
658
931
|
function evaluateCondition(condition, trace) {
|
|
659
932
|
if (!condition) {
|
|
660
933
|
return false;
|
|
@@ -665,7 +938,7 @@ function evaluateCondition(condition, trace) {
|
|
|
665
938
|
const actual = trace.action.type ?? "";
|
|
666
939
|
return actual === expected;
|
|
667
940
|
}
|
|
668
|
-
const numericMatch = condition.match(
|
|
941
|
+
const numericMatch = condition.match(/^\s*(\w+)\s*([><=!]+)\s*(\d+(?:\.\d+)?)\s*$/);
|
|
669
942
|
if (numericMatch) {
|
|
670
943
|
const [, field, op, valueStr] = numericMatch;
|
|
671
944
|
const value = parseFloat(valueStr);
|
|
@@ -830,8 +1103,10 @@ export {
|
|
|
830
1103
|
NEAR_BOUNDARY_THRESHOLD,
|
|
831
1104
|
OUTLIER_STD_DEV_THRESHOLD,
|
|
832
1105
|
VIOLATION_SEVERITY,
|
|
1106
|
+
analyzeFaultLines,
|
|
833
1107
|
checkCoherence,
|
|
834
1108
|
checkFleetCoherence,
|
|
1109
|
+
checkFleetFaultLines,
|
|
835
1110
|
computeCentroid,
|
|
836
1111
|
cosineSimilarity,
|
|
837
1112
|
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.0",
|
|
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"
|
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
|