@mnemom/agent-integrity-protocol 0.2.1 → 0.4.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.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var crypto = require('crypto');
3
+ var crypto$1 = require('crypto');
4
4
  var ed = require('@noble/ed25519');
5
5
  var sha2_js = require('@noble/hashes/sha2.js');
6
6
  var utils_js = require('@noble/hashes/utils.js');
@@ -42,7 +42,7 @@ function createWindowState(sessionId) {
42
42
  }
43
43
 
44
44
  // src/constants.ts
45
- var AIP_VERSION = "0.1.0";
45
+ var AIP_VERSION = "0.4.0";
46
46
  var ALGORITHM_VERSION = "1.0.0";
47
47
  var DEFAULT_SUSTAINED_CHECKS_THRESHOLD = 3;
48
48
  var DRIFT_SEVERITY_LOW_THRESHOLD = 0.7;
@@ -1183,11 +1183,27 @@ function validateConscienceContext(value) {
1183
1183
  consultation_depth: ctx.consultation_depth
1184
1184
  };
1185
1185
  }
1186
+ function extractFirstJsonObject(text) {
1187
+ let depth = 0;
1188
+ let start = -1;
1189
+ for (let i = 0; i < text.length; i++) {
1190
+ if (text[i] === "{") {
1191
+ if (depth === 0) start = i;
1192
+ depth++;
1193
+ } else if (text[i] === "}") {
1194
+ depth--;
1195
+ if (depth === 0 && start !== -1) {
1196
+ return text.slice(start, i + 1);
1197
+ }
1198
+ }
1199
+ }
1200
+ return null;
1201
+ }
1186
1202
  function checkIntegrity(input) {
1187
1203
  let jsonText = input.analysisResponse;
1188
- const jsonMatch = jsonText.match(/\{[\s\S]*\}/);
1189
- if (jsonMatch) {
1190
- jsonText = jsonMatch[0];
1204
+ const extracted = extractFirstJsonObject(jsonText);
1205
+ if (extracted) {
1206
+ jsonText = extracted;
1191
1207
  }
1192
1208
  jsonText = jsonText.replace(/,\s*([}\]])/g, "$1");
1193
1209
  let parsed;
@@ -1209,7 +1225,23 @@ function checkIntegrity(input) {
1209
1225
  const conscienceContext = validateConscienceContext(
1210
1226
  parsed.conscience_context
1211
1227
  );
1212
- const checkpointId = `${CHECKPOINT_ID_PREFIX}${crypto.randomUUID()}`;
1228
+ const crossValidationWarnings = [];
1229
+ if (verdict === "clear") {
1230
+ const criticalOrHigh = concerns.filter(
1231
+ (c) => c.severity === "critical" || c.severity === "high"
1232
+ );
1233
+ if (criticalOrHigh.length > 0) {
1234
+ const maxSeverity = criticalOrHigh.some((c) => c.severity === "critical") ? "critical" : "high";
1235
+ crossValidationWarnings.push(
1236
+ `Verdict is "clear" but ${criticalOrHigh.length} concern(s) have severity "${maxSeverity}". This may indicate an inconsistent analysis result.`
1237
+ );
1238
+ }
1239
+ } else if (verdict === "boundary_violation" && concerns.length === 0) {
1240
+ crossValidationWarnings.push(
1241
+ `Verdict is "boundary_violation" but concerns array is empty. Expected at least one concern for a boundary violation.`
1242
+ );
1243
+ }
1244
+ const checkpointId = `${CHECKPOINT_ID_PREFIX}${crypto$1.randomUUID()}`;
1213
1245
  const analysisMetadata = {
1214
1246
  analysis_model: input.analysisModel,
1215
1247
  analysis_duration_ms: input.analysisDurationMs,
@@ -1236,7 +1268,8 @@ function checkIntegrity(input) {
1236
1268
  window_size: input.windowPosition.window_size
1237
1269
  },
1238
1270
  analysis_metadata: analysisMetadata,
1239
- linked_trace_id: input.linkedTraceId ?? null
1271
+ linked_trace_id: input.linkedTraceId ?? null,
1272
+ ...crossValidationWarnings.length > 0 ? { cross_validation_warnings: crossValidationWarnings } : {}
1240
1273
  };
1241
1274
  }
1242
1275
  function mapVerdictToAction(verdict, concerns) {
@@ -1266,7 +1299,7 @@ function buildSignal(checkpoint, windowSummary) {
1266
1299
  };
1267
1300
  }
1268
1301
  function hashThinkingBlock(content) {
1269
- return crypto.createHash("sha256").update(content).digest("hex");
1302
+ return crypto$1.createHash("sha256").update(content).digest("hex");
1270
1303
  }
1271
1304
  function createDriftState() {
1272
1305
  return {
@@ -1296,7 +1329,9 @@ function detectIntegrityDrift(state, checkpoint, windowCheckpoints, threshold) {
1296
1329
  for (const concern of checkpoint.concerns) {
1297
1330
  newState.streakCategories.push(concern.category);
1298
1331
  }
1299
- if (newState.sustainedNonclear >= effectiveThreshold && !newState.alertFired) {
1332
+ const REALERT_INTERVAL = 5;
1333
+ const shouldAlert = newState.sustainedNonclear >= effectiveThreshold && (!newState.alertFired || (newState.sustainedNonclear - effectiveThreshold) % REALERT_INTERVAL === 0);
1334
+ if (shouldAlert) {
1300
1335
  newState.alertFired = true;
1301
1336
  const clearCount = windowCheckpoints.filter(
1302
1337
  (cp) => cp.verdict === "clear"
@@ -1313,7 +1348,7 @@ function detectIntegrityDrift(state, checkpoint, windowCheckpoints, threshold) {
1313
1348
  }
1314
1349
  const direction = inferDriftDirection(newState.streakCategories);
1315
1350
  const alert = {
1316
- alert_id: `${DRIFT_ALERT_ID_PREFIX}${crypto.randomUUID()}`,
1351
+ alert_id: `${DRIFT_ALERT_ID_PREFIX}${crypto$1.randomUUID()}`,
1317
1352
  agent_id: checkpoint.agent_id,
1318
1353
  session_id: checkpoint.session_id,
1319
1354
  checkpoint_ids: [...newState.streakCheckpointIds],
@@ -1348,7 +1383,9 @@ function inferDriftDirection(categories) {
1348
1383
  prompt_injection: "injection_pattern",
1349
1384
  value_misalignment: "value_erosion",
1350
1385
  autonomy_violation: "autonomy_creep",
1351
- deceptive_reasoning: "deception_pattern"
1386
+ deceptive_reasoning: "deception_pattern",
1387
+ reasoning_corruption: "reasoning_degradation",
1388
+ undeclared_intent: "intent_drift"
1352
1389
  };
1353
1390
  return CATEGORY_TO_DIRECTION[maxCategory] ?? "unknown";
1354
1391
  }
@@ -1381,7 +1418,7 @@ function createClient(config) {
1381
1418
  const adapter = provider ? registry.get(provider) : registry.detectFromUrl(config.analysis_llm.base_url);
1382
1419
  const thinking = adapter.extractThinking(responseBody) ?? adapter.extractThinkingFromStream(responseBody);
1383
1420
  if (!thinking) {
1384
- return buildSyntheticSignal(config, window, "clear");
1421
+ return buildSyntheticSignal(config, window, "clear", void 0, void 0, "no_thinking_block");
1385
1422
  }
1386
1423
  const thinkingTokens = Math.ceil(thinking.content.length / 4);
1387
1424
  const minTokens = config.min_evidence_tokens ?? DEFAULT_MIN_EVIDENCE_TOKENS;
@@ -1391,7 +1428,8 @@ function createClient(config) {
1391
1428
  window,
1392
1429
  "clear",
1393
1430
  `Thinking block below minimum evidence threshold (${thinkingTokens} tokens < ${minTokens})`,
1394
- thinkingTokens
1431
+ thinkingTokens,
1432
+ "below_evidence_threshold"
1395
1433
  );
1396
1434
  }
1397
1435
  const prompt = buildConsciencePrompt({
@@ -1417,9 +1455,9 @@ function createClient(config) {
1417
1455
  );
1418
1456
  }
1419
1457
  if (failureMode === "fail_closed") {
1420
- return buildSyntheticSignal(config, window, "boundary_violation");
1458
+ return buildSyntheticSignal(config, window, "boundary_violation", void 0, void 0, "analysis_failed");
1421
1459
  }
1422
- return buildSyntheticSignal(config, window, "clear");
1460
+ return buildSyntheticSignal(config, window, "clear", void 0, void 0, "analysis_failed");
1423
1461
  }
1424
1462
  const analysisDurationMs = Date.now() - startTime;
1425
1463
  const thinkingHash = hashThinkingBlock(thinking.content);
@@ -1480,7 +1518,10 @@ function createClient(config) {
1480
1518
  function generateSessionId(cardId) {
1481
1519
  const hash = cardId.slice(0, 8);
1482
1520
  const hourBucket = Math.floor(Date.now() / 36e5);
1483
- return `sess-${hash}-${hourBucket}`;
1521
+ const randomBytes = new Uint8Array(2);
1522
+ crypto.getRandomValues(randomBytes);
1523
+ const randomHex = Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
1524
+ return `sess-${hash}-${hourBucket}-${randomHex}`;
1484
1525
  }
1485
1526
  async function callAnalysisLLM(llmConfig, system, user, timeoutMs) {
1486
1527
  const controller = new AbortController();
@@ -1521,7 +1562,7 @@ async function callAnalysisLLM(llmConfig, system, user, timeoutMs) {
1521
1562
  clearTimeout(timeoutId);
1522
1563
  }
1523
1564
  }
1524
- function buildSyntheticSignal(config, window, verdict, customReasoning, thinkingTokensOriginal) {
1565
+ function buildSyntheticSignal(config, window, verdict, customReasoning, thinkingTokensOriginal, syntheticReason) {
1525
1566
  const summary = window.getSummary();
1526
1567
  return {
1527
1568
  checkpoint: {
@@ -1555,7 +1596,9 @@ function buildSyntheticSignal(config, window, verdict, customReasoning, thinking
1555
1596
  truncated: false,
1556
1597
  extraction_confidence: 0
1557
1598
  },
1558
- linked_trace_id: null
1599
+ linked_trace_id: null,
1600
+ synthetic: true,
1601
+ synthetic_reason: syntheticReason ?? "no_thinking_block"
1559
1602
  },
1560
1603
  proceed: verdict === "clear",
1561
1604
  recommended_action: verdict === "clear" ? "continue" : "deny_and_escalate",
@@ -1563,7 +1606,7 @@ function buildSyntheticSignal(config, window, verdict, customReasoning, thinking
1563
1606
  };
1564
1607
  }
1565
1608
  function signPayload(secret, payload) {
1566
- const hmac = crypto.createHmac("sha256", secret);
1609
+ const hmac = crypto$1.createHmac("sha256", secret);
1567
1610
  hmac.update(payload);
1568
1611
  return `sha256=${hmac.digest("hex")}`;
1569
1612
  }
@@ -1572,11 +1615,14 @@ function verifySignature(secret, payload, signature) {
1572
1615
  return constantTimeEqual(expected, signature);
1573
1616
  }
1574
1617
  function constantTimeEqual(a, b) {
1575
- if (a.length !== b.length) return false;
1618
+ const maxLen = Math.max(a.length, b.length);
1619
+ const aPadded = a.padEnd(maxLen, "\0");
1620
+ const bPadded = b.padEnd(maxLen, "\0");
1576
1621
  let result = 0;
1577
- for (let i = 0; i < a.length; i++) {
1578
- result |= a.charCodeAt(i) ^ b.charCodeAt(i);
1622
+ for (let i = 0; i < maxLen; i++) {
1623
+ result |= aPadded.charCodeAt(i) ^ bPadded.charCodeAt(i);
1579
1624
  }
1625
+ result |= a.length ^ b.length;
1580
1626
  return result === 0;
1581
1627
  }
1582
1628
  function base64ToUint8(b64) {