@mnemom/agent-alignment-protocol 0.6.0 → 0.6.2

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.d.mts CHANGED
@@ -40,7 +40,24 @@ interface ValueDefinition {
40
40
  }
41
41
  /** Value declarations (SPEC Section 4.4). */
42
42
  interface Values {
43
- /** List of value identifiers */
43
+ /**
44
+ * Behavioral and ethical values the agent applies in its decision-making.
45
+ *
46
+ * This field is actively monitored by AIP at runtime: every value listed here
47
+ * is expected to appear in AP-Trace `values_applied` fields when it influences
48
+ * a decision. Declaring a value the agent never applies produces verification
49
+ * warnings and degrades trust scoring.
50
+ *
51
+ * **Include**: Ethical and behavioral commitments — e.g. `transparency`,
52
+ * `honesty`, `accuracy`, `safety`, `accountability`, `helpfulness`,
53
+ * `deliberation_before_action`. These describe HOW the agent reasons.
54
+ *
55
+ * **Do not include**: Role capabilities, operational principles, or job-function
56
+ * descriptors — e.g. `fiduciary_precision`, `organizational_clarity`. These
57
+ * describe WHAT the agent is in its role and belong in `extensions.clpi.role`
58
+ * or other `extensions` metadata. Capability names (e.g. `read_documents`)
59
+ * belong in `autonomy_envelope.bounded_actions`.
60
+ */
44
61
  declared: string[];
45
62
  /** Definitions for non-standard values */
46
63
  definitions?: Record<string, ValueDefinition> | null;
package/dist/index.d.ts CHANGED
@@ -40,7 +40,24 @@ interface ValueDefinition {
40
40
  }
41
41
  /** Value declarations (SPEC Section 4.4). */
42
42
  interface Values {
43
- /** List of value identifiers */
43
+ /**
44
+ * Behavioral and ethical values the agent applies in its decision-making.
45
+ *
46
+ * This field is actively monitored by AIP at runtime: every value listed here
47
+ * is expected to appear in AP-Trace `values_applied` fields when it influences
48
+ * a decision. Declaring a value the agent never applies produces verification
49
+ * warnings and degrades trust scoring.
50
+ *
51
+ * **Include**: Ethical and behavioral commitments — e.g. `transparency`,
52
+ * `honesty`, `accuracy`, `safety`, `accountability`, `helpfulness`,
53
+ * `deliberation_before_action`. These describe HOW the agent reasons.
54
+ *
55
+ * **Do not include**: Role capabilities, operational principles, or job-function
56
+ * descriptors — e.g. `fiduciary_precision`, `organizational_clarity`. These
57
+ * describe WHAT the agent is in its role and belong in `extensions.clpi.role`
58
+ * or other `extensions` metadata. Capability names (e.g. `read_documents`)
59
+ * belong in `autonomy_envelope.bounded_actions`.
60
+ */
44
61
  declared: string[];
45
62
  /** Definitions for non-standard values */
46
63
  definitions?: Record<string, ValueDefinition> | null;
package/dist/index.js CHANGED
@@ -747,6 +747,13 @@ function hasRoleKeyword(agentId) {
747
747
  const lower = agentId.toLowerCase();
748
748
  return ROLE_KEYWORDS.some((kw) => lower.includes(kw));
749
749
  }
750
+ function getClpiRole(card) {
751
+ const ext = card.extensions;
752
+ if (!ext) return null;
753
+ const clpi = ext["clpi"];
754
+ if (!clpi || typeof clpi["role"] !== "string") return null;
755
+ return clpi["role"] || null;
756
+ }
750
757
  function analyzeFaultLines(coherenceResult, cards, options) {
751
758
  const reputationScores = options?.reputationScores;
752
759
  const agentBoundedActions = /* @__PURE__ */ new Map();
@@ -757,6 +764,10 @@ function analyzeFaultLines(coherenceResult, cards, options) {
757
764
  for (const { agentId, card } of cards) {
758
765
  agentConflictMap.set(agentId, new Set(card.values.conflicts_with ?? []));
759
766
  }
767
+ const agentRoleMap = /* @__PURE__ */ new Map();
768
+ for (const { agentId, card } of cards) {
769
+ agentRoleMap.set(agentId, getClpiRole(card));
770
+ }
760
771
  const faultLines = [];
761
772
  for (const divergence of coherenceResult.divergence_report) {
762
773
  const {
@@ -789,6 +800,13 @@ function analyzeFaultLines(coherenceResult, cards, options) {
789
800
  })()) {
790
801
  classification = "priority_mismatch";
791
802
  } else if (agents_declaring.length >= 1 && agents_missing.length >= 1 && (() => {
803
+ const declaringRoles = new Set(agents_declaring.map((id2) => agentRoleMap.get(id2) ?? null).filter(Boolean));
804
+ const missingRoles = new Set(agents_missing.map((id2) => agentRoleMap.get(id2) ?? null).filter(Boolean));
805
+ if (declaringRoles.size > 0) {
806
+ const declaringRoleArr = [...declaringRoles];
807
+ const isRoleExclusive = declaringRoleArr.every((role) => !missingRoles.has(role));
808
+ if (isRoleExclusive) return true;
809
+ }
792
810
  const allInvolved = [...agents_declaring, ...agents_missing];
793
811
  return allInvolved.some((id2) => hasRoleKeyword(id2));
794
812
  })()) {
@@ -816,23 +834,29 @@ function analyzeFaultLines(coherenceResult, cards, options) {
816
834
  coordinationOverlap = count > 0 ? total / count : 0.5;
817
835
  }
818
836
  }
819
- let impactScore = impact_on_fleet_score * coordinationOverlap;
820
- if (reputationScores && involvedAgents.length > 0) {
821
- const repValues = involvedAgents.map((id2) => (reputationScores[id2] ?? 500) / 1e3).map((r) => Math.max(1e-3, r));
822
- const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
823
- const geoMean = Math.exp(logSum / repValues.length);
824
- impactScore *= geoMean;
825
- }
826
- impactScore = Math.min(1, Math.max(0, impactScore));
837
+ let impactScore;
827
838
  let severity;
828
- if (impactScore >= 0.7) {
829
- severity = "critical";
830
- } else if (impactScore >= 0.4) {
831
- severity = "high";
832
- } else if (impactScore >= 0.2) {
833
- severity = "medium";
834
- } else {
839
+ if (classification === "complementary") {
840
+ impactScore = 0;
835
841
  severity = "low";
842
+ } else {
843
+ impactScore = impact_on_fleet_score * coordinationOverlap;
844
+ if (reputationScores && involvedAgents.length > 0) {
845
+ const repValues = involvedAgents.map((id2) => (reputationScores[id2] ?? 500) / 1e3).map((r) => Math.max(1e-3, r));
846
+ const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
847
+ const geoMean = Math.exp(logSum / repValues.length);
848
+ impactScore *= geoMean;
849
+ }
850
+ impactScore = Math.min(1, Math.max(0, impactScore));
851
+ if (impactScore >= 0.7) {
852
+ severity = "critical";
853
+ } else if (impactScore >= 0.4) {
854
+ severity = "high";
855
+ } else if (impactScore >= 0.2) {
856
+ severity = "medium";
857
+ } else {
858
+ severity = "low";
859
+ }
836
860
  }
837
861
  let resolutionHint;
838
862
  switch (classification) {
@@ -883,12 +907,15 @@ function analyzeFaultLines(coherenceResult, cards, options) {
883
907
  return b.impact_score - a.impact_score;
884
908
  });
885
909
  const alignments = [];
910
+ const actionableFaultLines = faultLines.filter(
911
+ (fl) => fl.classification === "resolvable" || fl.classification === "incompatible"
912
+ );
886
913
  const grouped = /* @__PURE__ */ new Map();
887
914
  const groupAssignment = /* @__PURE__ */ new Map();
888
915
  let nextGroupId = 0;
889
- for (let i = 0; i < faultLines.length; i++) {
890
- for (let j = i + 1; j < faultLines.length; j++) {
891
- const sim = jaccardSimilarity(faultLines[i].agents_missing, faultLines[j].agents_missing);
916
+ for (let i = 0; i < actionableFaultLines.length; i++) {
917
+ for (let j = i + 1; j < actionableFaultLines.length; j++) {
918
+ const sim = jaccardSimilarity(actionableFaultLines[i].agents_missing, actionableFaultLines[j].agents_missing);
892
919
  if (sim > 0.6) {
893
920
  const gi = groupAssignment.get(i);
894
921
  const gj = groupAssignment.get(j);
@@ -921,7 +948,7 @@ function analyzeFaultLines(coherenceResult, cards, options) {
921
948
  for (const [, members] of grouped) {
922
949
  if (members.length < 2) continue;
923
950
  const unique = [...new Set(members)];
924
- const groupFaultLines = unique.map((i) => faultLines[i]);
951
+ const groupFaultLines = unique.map((i) => actionableFaultLines[i]);
925
952
  const minorityAgents = [
926
953
  ...new Set(groupFaultLines.flatMap((fl) => fl.agents_missing))
927
954
  ];
package/dist/index.mjs CHANGED
@@ -690,6 +690,13 @@ function hasRoleKeyword(agentId) {
690
690
  const lower = agentId.toLowerCase();
691
691
  return ROLE_KEYWORDS.some((kw) => lower.includes(kw));
692
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
+ }
693
700
  function analyzeFaultLines(coherenceResult, cards, options) {
694
701
  const reputationScores = options?.reputationScores;
695
702
  const agentBoundedActions = /* @__PURE__ */ new Map();
@@ -700,6 +707,10 @@ function analyzeFaultLines(coherenceResult, cards, options) {
700
707
  for (const { agentId, card } of cards) {
701
708
  agentConflictMap.set(agentId, new Set(card.values.conflicts_with ?? []));
702
709
  }
710
+ const agentRoleMap = /* @__PURE__ */ new Map();
711
+ for (const { agentId, card } of cards) {
712
+ agentRoleMap.set(agentId, getClpiRole(card));
713
+ }
703
714
  const faultLines = [];
704
715
  for (const divergence of coherenceResult.divergence_report) {
705
716
  const {
@@ -732,6 +743,13 @@ function analyzeFaultLines(coherenceResult, cards, options) {
732
743
  })()) {
733
744
  classification = "priority_mismatch";
734
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
+ }
735
753
  const allInvolved = [...agents_declaring, ...agents_missing];
736
754
  return allInvolved.some((id2) => hasRoleKeyword(id2));
737
755
  })()) {
@@ -759,23 +777,29 @@ function analyzeFaultLines(coherenceResult, cards, options) {
759
777
  coordinationOverlap = count > 0 ? total / count : 0.5;
760
778
  }
761
779
  }
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));
780
+ let impactScore;
770
781
  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 {
782
+ if (classification === "complementary") {
783
+ impactScore = 0;
778
784
  severity = "low";
785
+ } else {
786
+ impactScore = impact_on_fleet_score * coordinationOverlap;
787
+ if (reputationScores && involvedAgents.length > 0) {
788
+ const repValues = involvedAgents.map((id2) => (reputationScores[id2] ?? 500) / 1e3).map((r) => Math.max(1e-3, r));
789
+ const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
790
+ const geoMean = Math.exp(logSum / repValues.length);
791
+ impactScore *= geoMean;
792
+ }
793
+ impactScore = Math.min(1, Math.max(0, impactScore));
794
+ if (impactScore >= 0.7) {
795
+ severity = "critical";
796
+ } else if (impactScore >= 0.4) {
797
+ severity = "high";
798
+ } else if (impactScore >= 0.2) {
799
+ severity = "medium";
800
+ } else {
801
+ severity = "low";
802
+ }
779
803
  }
780
804
  let resolutionHint;
781
805
  switch (classification) {
@@ -826,12 +850,15 @@ function analyzeFaultLines(coherenceResult, cards, options) {
826
850
  return b.impact_score - a.impact_score;
827
851
  });
828
852
  const alignments = [];
853
+ const actionableFaultLines = faultLines.filter(
854
+ (fl) => fl.classification === "resolvable" || fl.classification === "incompatible"
855
+ );
829
856
  const grouped = /* @__PURE__ */ new Map();
830
857
  const groupAssignment = /* @__PURE__ */ new Map();
831
858
  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);
859
+ for (let i = 0; i < actionableFaultLines.length; i++) {
860
+ for (let j = i + 1; j < actionableFaultLines.length; j++) {
861
+ const sim = jaccardSimilarity(actionableFaultLines[i].agents_missing, actionableFaultLines[j].agents_missing);
835
862
  if (sim > 0.6) {
836
863
  const gi = groupAssignment.get(i);
837
864
  const gj = groupAssignment.get(j);
@@ -864,7 +891,7 @@ function analyzeFaultLines(coherenceResult, cards, options) {
864
891
  for (const [, members] of grouped) {
865
892
  if (members.length < 2) continue;
866
893
  const unique = [...new Set(members)];
867
- const groupFaultLines = unique.map((i) => faultLines[i]);
894
+ const groupFaultLines = unique.map((i) => actionableFaultLines[i]);
868
895
  const minorityAgents = [
869
896
  ...new Set(groupFaultLines.flatMap((fl) => fl.agents_missing))
870
897
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/agent-alignment-protocol",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
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",
@@ -54,4 +54,4 @@
54
54
  "engines": {
55
55
  "node": ">=18.0.0"
56
56
  }
57
- }
57
+ }
@@ -49,7 +49,24 @@ export interface ValueDefinition {
49
49
 
50
50
  /** Value declarations (SPEC Section 4.4). */
51
51
  export interface Values {
52
- /** List of value identifiers */
52
+ /**
53
+ * Behavioral and ethical values the agent applies in its decision-making.
54
+ *
55
+ * This field is actively monitored by AIP at runtime: every value listed here
56
+ * is expected to appear in AP-Trace `values_applied` fields when it influences
57
+ * a decision. Declaring a value the agent never applies produces verification
58
+ * warnings and degrades trust scoring.
59
+ *
60
+ * **Include**: Ethical and behavioral commitments — e.g. `transparency`,
61
+ * `honesty`, `accuracy`, `safety`, `accountability`, `helpfulness`,
62
+ * `deliberation_before_action`. These describe HOW the agent reasons.
63
+ *
64
+ * **Do not include**: Role capabilities, operational principles, or job-function
65
+ * descriptors — e.g. `fiduciary_precision`, `organizational_clarity`. These
66
+ * describe WHAT the agent is in its role and belong in `extensions.clpi.role`
67
+ * or other `extensions` metadata. Capability names (e.g. `read_documents`)
68
+ * belong in `autonomy_envelope.bounded_actions`.
69
+ */
53
70
  declared: string[];
54
71
  /** Definitions for non-standard values */
55
72
  definitions?: Record<string, ValueDefinition> | null;
@@ -788,6 +788,15 @@ function hasRoleKeyword(agentId: string): boolean {
788
788
  return ROLE_KEYWORDS.some(kw => lower.includes(kw));
789
789
  }
790
790
 
791
+ /** Extract the CLPI role from a card's extensions, if present. */
792
+ function getClpiRole(card: AlignmentCard): string | null {
793
+ const ext = (card as unknown as { extensions?: Record<string, unknown> }).extensions;
794
+ if (!ext) return null;
795
+ const clpi = ext['clpi'] as Record<string, unknown> | undefined;
796
+ if (!clpi || typeof clpi['role'] !== 'string') return null;
797
+ return clpi['role'] || null;
798
+ }
799
+
791
800
  /**
792
801
  * Analyze fault lines in a fleet based on a FleetCoherenceResult.
793
802
  *
@@ -815,6 +824,12 @@ export function analyzeFaultLines(
815
824
  agentConflictMap.set(agentId, new Set(card.values.conflicts_with ?? []));
816
825
  }
817
826
 
827
+ // Build lookup: agentId → CLPI role (from extensions.clpi.role)
828
+ const agentRoleMap = new Map<string, string | null>();
829
+ for (const { agentId, card } of cards) {
830
+ agentRoleMap.set(agentId, getClpiRole(card));
831
+ }
832
+
818
833
  const faultLines: FaultLine[] = [];
819
834
 
820
835
  for (const divergence of coherenceResult.divergence_report) {
@@ -863,7 +878,18 @@ export function analyzeFaultLines(
863
878
  agents_declaring.length >= 1 &&
864
879
  agents_missing.length >= 1 &&
865
880
  (() => {
866
- // complementary: heuristic — some agent (declaring or missing) has a role keyword
881
+ // complementary: the declaring agents all share a CLPI role that the missing
882
+ // agents do NOT share — indicating intentional role specialization, not a gap.
883
+ // Primary check: extensions.clpi.role (authoritative)
884
+ const declaringRoles = new Set(agents_declaring.map(id => agentRoleMap.get(id) ?? null).filter(Boolean));
885
+ const missingRoles = new Set(agents_missing.map(id => agentRoleMap.get(id) ?? null).filter(Boolean));
886
+ if (declaringRoles.size > 0) {
887
+ // All declaring agents share a role that none of the missing agents have
888
+ const declaringRoleArr = [...declaringRoles];
889
+ const isRoleExclusive = declaringRoleArr.every(role => !missingRoles.has(role));
890
+ if (isRoleExclusive) return true;
891
+ }
892
+ // Fallback: agent ID contains a role keyword (original heuristic)
867
893
  const allInvolved = [...agents_declaring, ...agents_missing];
868
894
  return allInvolved.some(id => hasRoleKeyword(id));
869
895
  })()
@@ -897,30 +923,38 @@ export function analyzeFaultLines(
897
923
  }
898
924
 
899
925
  // --- impact_score ---
900
- let impactScore = impact_on_fleet_score * coordinationOverlap;
901
-
902
- // Reputation weighting: multiply by geometric mean of reputation/1000
903
- if (reputationScores && involvedAgents.length > 0) {
904
- const repValues = involvedAgents
905
- .map(id => (reputationScores[id] ?? 500) / 1000)
906
- .map(r => Math.max(0.001, r)); // avoid log(0)
907
- const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
908
- const geoMean = Math.exp(logSum / repValues.length);
909
- impactScore *= geoMean;
910
- }
911
-
912
- impactScore = Math.min(1, Math.max(0, impactScore));
913
-
914
- // --- Severity ---
926
+ // Complementary faults are intentional — they carry zero risk by definition.
927
+ let impactScore: number;
915
928
  let severity: Severity;
916
- if (impactScore >= 0.7) {
917
- severity = 'critical';
918
- } else if (impactScore >= 0.4) {
919
- severity = 'high';
920
- } else if (impactScore >= 0.2) {
921
- severity = 'medium';
922
- } else {
929
+
930
+ if (classification === 'complementary') {
931
+ impactScore = 0;
923
932
  severity = 'low';
933
+ } else {
934
+ impactScore = impact_on_fleet_score * coordinationOverlap;
935
+
936
+ // Reputation weighting: multiply by geometric mean of reputation/1000
937
+ if (reputationScores && involvedAgents.length > 0) {
938
+ const repValues = involvedAgents
939
+ .map(id => (reputationScores[id] ?? 500) / 1000)
940
+ .map(r => Math.max(0.001, r)); // avoid log(0)
941
+ const logSum = repValues.reduce((sum, r) => sum + Math.log(r), 0);
942
+ const geoMean = Math.exp(logSum / repValues.length);
943
+ impactScore *= geoMean;
944
+ }
945
+
946
+ impactScore = Math.min(1, Math.max(0, impactScore));
947
+
948
+ // --- Severity ---
949
+ if (impactScore >= 0.7) {
950
+ severity = 'critical';
951
+ } else if (impactScore >= 0.4) {
952
+ severity = 'high';
953
+ } else if (impactScore >= 0.2) {
954
+ severity = 'medium';
955
+ } else {
956
+ severity = 'low';
957
+ }
924
958
  }
925
959
 
926
960
  // --- Resolution hint ---
@@ -981,17 +1015,22 @@ export function analyzeFaultLines(
981
1015
  });
982
1016
 
983
1017
  // --- Fault line alignment detection ---
1018
+ // Only consider resolvable and incompatible faults — complementary faults are intentional
1019
+ // role specialization and should NEVER trigger a structural fault line alert.
984
1020
  const alignments: FaultLineAlignment[] = [];
1021
+ const actionableFaultLines = faultLines.filter(
1022
+ fl => fl.classification === 'resolvable' || fl.classification === 'incompatible'
1023
+ );
985
1024
 
986
- // For each pair of fault lines, compute Jaccard of agents_missing sets
1025
+ // For each pair of actionable fault lines, compute Jaccard of agents_missing sets
987
1026
  // Group fault lines with similarity > 0.6
988
1027
  const grouped = new Map<number, number[]>(); // groupId → faultLine indices
989
1028
  const groupAssignment = new Map<number, number>(); // faultLine index → groupId
990
1029
  let nextGroupId = 0;
991
1030
 
992
- for (let i = 0; i < faultLines.length; i++) {
993
- for (let j = i + 1; j < faultLines.length; j++) {
994
- const sim = jaccardSimilarity(faultLines[i].agents_missing, faultLines[j].agents_missing);
1031
+ for (let i = 0; i < actionableFaultLines.length; i++) {
1032
+ for (let j = i + 1; j < actionableFaultLines.length; j++) {
1033
+ const sim = jaccardSimilarity(actionableFaultLines[i].agents_missing, actionableFaultLines[j].agents_missing);
995
1034
  if (sim > 0.6) {
996
1035
  // Find or create groups for i and j
997
1036
  const gi = groupAssignment.get(i);
@@ -1028,7 +1067,7 @@ export function analyzeFaultLines(
1028
1067
  for (const [, members] of grouped) {
1029
1068
  if (members.length < 2) continue;
1030
1069
  const unique = [...new Set(members)];
1031
- const groupFaultLines = unique.map(i => faultLines[i]);
1070
+ const groupFaultLines = unique.map(i => actionableFaultLines[i]);
1032
1071
 
1033
1072
  const minorityAgents = [
1034
1073
  ...new Set(groupFaultLines.flatMap(fl => fl.agents_missing)),