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