@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/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(/(\w+)\s*([><=!]+)\s*(\d+(?:\.\d+)?)/);
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.5.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