@aria_asi/cli 0.2.30 → 0.2.31
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/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +88 -20
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +526 -2
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.js +111 -0
- package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +2 -0
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +231 -19
- package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/shell.js +76 -3
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +52 -25
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +185 -76
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +63 -14
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +2 -0
- package/dist/assets/hooks/aria-stop-gate.mjs +225 -52
- package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
- package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
- package/dist/assets/opencode-plugins/harness-gate/index.js +23 -1
- package/dist/assets/opencode-plugins/harness-stop/index.js +93 -4
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +12 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +479 -0
- package/dist/runtime/doctrine_trigger_map.json +479 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +433 -0
- package/dist/runtime/manifest.json +1 -1
- package/dist/runtime/metering.mjs +100 -0
- package/dist/runtime/onboarding-engine.mjs +89 -0
- package/dist/runtime/plugin-engine.mjs +196 -0
- package/dist/runtime/sdk/BUNDLED.json +1 -1
- package/dist/runtime/sdk/index.d.ts +7 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1094 -47
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +7 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-cognition-substrate-binding.mjs +52 -25
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-tool-gate.mjs +185 -76
- package/hooks/aria-preturn-memory-gate.mjs +63 -14
- package/hooks/aria-repo-doctrine-gate.mjs +2 -0
- package/hooks/aria-stop-gate.mjs +225 -52
- package/hooks/lib/canonical-lenses.mjs +6 -5
- package/hooks/lib/gate-loop-state.mjs +50 -0
- package/hooks/lib/hook-message-window.mjs +121 -0
- package/hooks/test-tier-lens-labeling.mjs +26 -58
- package/opencode-plugins/harness-gate/index.js +23 -1
- package/opencode-plugins/harness-stop/index.js +93 -4
- package/package.json +1 -1
- package/runtime-src/auth-middleware.mjs +251 -0
- package/runtime-src/codex-bridge.mjs +644 -0
- package/runtime-src/fleet-engine.mjs +231 -0
- package/runtime-src/harness-daemon.mjs +433 -0
- package/runtime-src/metering.mjs +100 -0
- package/runtime-src/onboarding-engine.mjs +89 -0
- package/runtime-src/plugin-engine.mjs +196 -0
- package/runtime-src/service.mjs +1094 -47
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +98 -20
- package/src/connectors/codex.ts +534 -1
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +113 -0
- package/src/connectors/opencode.ts +3 -0
- package/src/connectors/runtime.ts +241 -21
- package/src/connectors/shell.ts +78 -3
- package/dist/cli-0.2.0.tgz +0 -0
|
@@ -42,10 +42,21 @@ import { dirname } from 'node:path';
|
|
|
42
42
|
import { homedir } from 'node:os';
|
|
43
43
|
import { spawnSync } from 'node:child_process';
|
|
44
44
|
import { createHmac, randomBytes as cryptoRandomBytes } from 'node:crypto';
|
|
45
|
+
import { lensNamesForTier } from './lib/canonical-lenses.mjs';
|
|
46
|
+
import { registerGateBlock } from './lib/gate-loop-state.mjs';
|
|
47
|
+
import {
|
|
48
|
+
collectRecentAssistantTextsFromMessages,
|
|
49
|
+
extractTextFromContent,
|
|
50
|
+
isMostlySystemReminder,
|
|
51
|
+
isToolResultOnlyContent,
|
|
52
|
+
normalizeContent,
|
|
53
|
+
normalizeRole,
|
|
54
|
+
} from './lib/hook-message-window.mjs';
|
|
45
55
|
|
|
46
56
|
const HOME = process.env.HOME || '/tmp';
|
|
47
57
|
const LOG = `${HOME}/.claude/aria-pre-tool-gate.log`;
|
|
48
58
|
const HEARTBEAT = `${HOME}/.claude/aria-pre-tool-gate-heartbeat.jsonl`;
|
|
59
|
+
const GATE_LOOP_STATE_PATH = `${HOME}/.claude/.aria-gate-loop-state.json`;
|
|
49
60
|
|
|
50
61
|
// ── Heartbeat OUTSIDE the crash boundary (doctrine #123) ───────────────
|
|
51
62
|
// Per feedback_ledger_writes_outside_crash_boundary.md + doctrine #124
|
|
@@ -446,12 +457,11 @@ const MEASURABLE_PREDICATE_RX = /(?:>=|<=|==|!=|>|<|≥|≤)\s*\d+(?:\.\d+)?(?:m
|
|
|
446
457
|
// Any <expected> block containing ONLY these (no actual predicate) is rejected.
|
|
447
458
|
const QUALITATIVE_DRIFT_RX = /\b(?:better(?:er)?|improved?(?:ment)?|more\s+robust|should\s+(?:work|pass|succeed|run|fix)|more\s+reliable|cleaner|less\s+error[-_\s]?prone|nicer|smoother|faster[-\s]?loading|higher[-\s]?quality|more\s+stable|looks\s+(?:good|better|right))\b/i;
|
|
448
459
|
|
|
449
|
-
// ──
|
|
460
|
+
// ── Canonical lens labeling (Phase 11 #59 corrected) ────────────────────────
|
|
450
461
|
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
// vocabulary never appears in client-facing block-reason text.
|
|
462
|
+
// The lens names themselves carry meaning and must remain canonical on every
|
|
463
|
+
// surface. Readability comes from the explanatory prose inside each lens, not
|
|
464
|
+
// by swapping the lens label for a flattened stand-in.
|
|
455
465
|
//
|
|
456
466
|
// Tier is read from the most recent harness-via-sdk packet cache at
|
|
457
467
|
// ~/.claude/.aria-harness-last-packet.json. Two detection paths:
|
|
@@ -484,12 +494,9 @@ function resolveOwnerTier() {
|
|
|
484
494
|
|
|
485
495
|
const IS_OWNER = resolveOwnerTier();
|
|
486
496
|
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const LENS_NAMES_GENERIC = ['perception', 'balance', 'wisdom', 'reflection', 'foresight', 'insight', 'revelation', 'discernment'];
|
|
491
|
-
// Active label set for this execution — determined by tier.
|
|
492
|
-
const LENS_NAMES = IS_OWNER ? LENS_NAMES_CANONICAL : LENS_NAMES_GENERIC;
|
|
497
|
+
// Active visible label set for this execution — canonical labels on every
|
|
498
|
+
// surface. Tier only affects other policy surfaces, not lens semantics.
|
|
499
|
+
const LENS_NAMES = lensNamesForTier(IS_OWNER);
|
|
493
500
|
|
|
494
501
|
// Doctrine memory filenames are Aria-side substrate IP. On client surfaces
|
|
495
502
|
// replace them with generic descriptions in block-reason text.
|
|
@@ -551,14 +558,24 @@ const SHORT_BASH_LIMIT = 30;
|
|
|
551
558
|
// needed. Cognition becomes a property of THE ACTION, not of some
|
|
552
559
|
// message somewhere — the deepest reading of "cognition before action."
|
|
553
560
|
//
|
|
554
|
-
// Inline regex is built from the active LENS_NAMES
|
|
555
|
-
//
|
|
561
|
+
// Inline regex is built from the active canonical LENS_NAMES. We do not
|
|
562
|
+
// accept flattened generic aliases because they change the lens meaning.
|
|
556
563
|
const _INLINE_LENS_PATTERN = LENS_NAMES.join('|');
|
|
557
564
|
const INLINE_LENS_LINE_RX_GLOBAL = new RegExp(
|
|
558
565
|
`^\\s*#\\s*(${_INLINE_LENS_PATTERN})\\s*:\\s*(.+)$`,
|
|
559
566
|
'gim',
|
|
560
567
|
);
|
|
561
568
|
const INLINE_COGNITION_HEADER_RX = /^\s*#\s*cognition\s*:\s*(.+)$/im;
|
|
569
|
+
const INLINE_EXPECTED_LINE_RX = /^\s*#\s*expected\s*:\s*(.+)$/gim;
|
|
570
|
+
const INLINE_VERIFY_LINE_RX = /^\s*#\s*verify\s*:\s*(.+)$/gim;
|
|
571
|
+
|
|
572
|
+
const VERIFY_REQUIRED_FIELDS = [
|
|
573
|
+
{ rx: /\btarget\s*:/i, name: 'target' },
|
|
574
|
+
{ rx: /\brole\s*:/i, name: 'role' },
|
|
575
|
+
{ rx: /\bverified\s*:/i, name: 'verified' },
|
|
576
|
+
{ rx: /\brollback\s*:/i, name: 'rollback' },
|
|
577
|
+
{ rx: /\baxiom\s*:/i, name: 'axiom' },
|
|
578
|
+
];
|
|
562
579
|
|
|
563
580
|
function detectInlineCognition(cmd) {
|
|
564
581
|
const names = [];
|
|
@@ -593,6 +610,67 @@ function detectInlineCognition(cmd) {
|
|
|
593
610
|
return { count: names.length, names, hasSubstrateCite };
|
|
594
611
|
}
|
|
595
612
|
|
|
613
|
+
function normalizeInlineDirectiveBody(rawBody) {
|
|
614
|
+
if (!rawBody) return '';
|
|
615
|
+
const segments = String(rawBody)
|
|
616
|
+
.split(/;|\s+(?=[a-z_][a-z0-9_-]*\s*[=:])/i)
|
|
617
|
+
.map((segment) => segment.trim())
|
|
618
|
+
.filter(Boolean);
|
|
619
|
+
const lines = [];
|
|
620
|
+
for (const segment of segments) {
|
|
621
|
+
const match = segment.match(/^([a-z_][a-z0-9_-]*)\s*[=:]\s*(.+)$/i);
|
|
622
|
+
if (match) {
|
|
623
|
+
lines.push(`${match[1]}: ${match[2].trim()}`);
|
|
624
|
+
continue;
|
|
625
|
+
}
|
|
626
|
+
lines.push(segment);
|
|
627
|
+
}
|
|
628
|
+
return lines.join('\n').trim();
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function extractInlineDirectiveBody(cmd, directiveRx) {
|
|
632
|
+
if (!cmd || !(directiveRx instanceof RegExp)) return '';
|
|
633
|
+
directiveRx.lastIndex = 0;
|
|
634
|
+
const lines = [];
|
|
635
|
+
let match;
|
|
636
|
+
while ((match = directiveRx.exec(cmd)) !== null) {
|
|
637
|
+
const normalized = normalizeInlineDirectiveBody(match[1]);
|
|
638
|
+
if (normalized) lines.push(normalized);
|
|
639
|
+
}
|
|
640
|
+
return lines.join('\n').trim();
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
function scoreVerifyFields(body, fields) {
|
|
644
|
+
if (!body) return 0;
|
|
645
|
+
return fields.reduce((count, { rx }) => count + (rx.test(body) ? 1 : 0), 0);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function extractCurrentTurnAssistantText(event, toolInput) {
|
|
649
|
+
const candidates = [
|
|
650
|
+
event.assistant_text,
|
|
651
|
+
event.assistantText,
|
|
652
|
+
event.draft,
|
|
653
|
+
event.text,
|
|
654
|
+
event.current_text,
|
|
655
|
+
event.currentText,
|
|
656
|
+
event.message,
|
|
657
|
+
event.prompt,
|
|
658
|
+
toolInput?.assistant_text,
|
|
659
|
+
toolInput?.assistantText,
|
|
660
|
+
toolInput?.text,
|
|
661
|
+
];
|
|
662
|
+
const parts = [];
|
|
663
|
+
const seen = new Set();
|
|
664
|
+
for (const candidate of candidates) {
|
|
665
|
+
if (typeof candidate !== 'string') continue;
|
|
666
|
+
const text = candidate.trim();
|
|
667
|
+
if (!text || seen.has(text)) continue;
|
|
668
|
+
seen.add(text);
|
|
669
|
+
parts.push(text);
|
|
670
|
+
}
|
|
671
|
+
return parts.join('\n\n').trim();
|
|
672
|
+
}
|
|
673
|
+
|
|
596
674
|
// Substance-checking lens detection (added 2026-04-26 per Hamza's
|
|
597
675
|
// gate-improvement doctrine: form-only emission must not satisfy
|
|
598
676
|
// the gate). For each lens, look for `<lens>: <content>` and verify
|
|
@@ -722,6 +800,9 @@ const HARNESS_URL =
|
|
|
722
800
|
process.env.ARIA_HARNESS_BASE_URL ||
|
|
723
801
|
process.env.ARIA_HARNESS_URL ||
|
|
724
802
|
'https://harness.ariasos.com';
|
|
803
|
+
const RUNTIME_BASE_URL =
|
|
804
|
+
process.env.ARIA_RUNTIME_URL ||
|
|
805
|
+
'http://127.0.0.1:4319';
|
|
725
806
|
const HARNESS_TOKEN = process.env.ARIA_HARNESS_TOKEN || '';
|
|
726
807
|
const LOG_PUSH_DISABLED = process.env.ARIA_COGNITION_PUSH === 'off';
|
|
727
808
|
const MAX_IN_FLIGHT = 16;
|
|
@@ -953,6 +1034,26 @@ const USER_BOUNDARIES_TO_CROSS = 5;
|
|
|
953
1034
|
|
|
954
1035
|
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
955
1036
|
const recentAssistantTexts = [];
|
|
1037
|
+
const recentAssistantTextSet = new Set();
|
|
1038
|
+
function appendAssistantTexts(texts) {
|
|
1039
|
+
for (const text of texts) {
|
|
1040
|
+
if (!text || recentAssistantTextSet.has(text)) continue;
|
|
1041
|
+
recentAssistantTexts.push(text);
|
|
1042
|
+
recentAssistantTextSet.add(text);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const eventMessages = Array.isArray(event.messages) ? event.messages : [];
|
|
1047
|
+
const eventAssistantTexts = collectRecentAssistantTextsFromMessages(eventMessages, {
|
|
1048
|
+
compactSummaryRx: COMPACT_SUMMARY_RX,
|
|
1049
|
+
hardLookbackCap: HARD_LOOKBACK_CAP,
|
|
1050
|
+
systemReminderRx: SYSTEM_REMINDER_RX,
|
|
1051
|
+
systemReminderThreshold: SYSTEM_REMINDER_THRESHOLD,
|
|
1052
|
+
userBoundariesToCross: USER_BOUNDARIES_TO_CROSS,
|
|
1053
|
+
});
|
|
1054
|
+
appendAssistantTexts(eventAssistantTexts);
|
|
1055
|
+
|
|
1056
|
+
const transcriptAssistantTexts = [];
|
|
956
1057
|
if (transcriptPath && existsSync(transcriptPath)) {
|
|
957
1058
|
try {
|
|
958
1059
|
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
@@ -961,7 +1062,8 @@ if (transcriptPath && existsSync(transcriptPath)) {
|
|
|
961
1062
|
for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
|
|
962
1063
|
try {
|
|
963
1064
|
const m = JSON.parse(lines[i]);
|
|
964
|
-
const role = m
|
|
1065
|
+
const role = normalizeRole(m);
|
|
1066
|
+
const content = normalizeContent(m);
|
|
965
1067
|
if (role === 'user') {
|
|
966
1068
|
// Skip messages that aren't real user input:
|
|
967
1069
|
// (a) tool_result blocks (runtime feeding back tool output)
|
|
@@ -969,57 +1071,36 @@ if (transcriptPath && existsSync(transcriptPath)) {
|
|
|
969
1071
|
// task-notifications, gentle reminders) — runtime-
|
|
970
1072
|
// authored, not user voice. Counting them eats the
|
|
971
1073
|
// cognition lookback in tool-heavy or block-heavy turns.
|
|
972
|
-
|
|
973
|
-
const isToolResultOnly =
|
|
974
|
-
Array.isArray(content) &&
|
|
975
|
-
content.length > 0 &&
|
|
976
|
-
content.every((b) => b && b.type === 'tool_result');
|
|
977
|
-
if (isToolResultOnly) continue;
|
|
1074
|
+
if (isToolResultOnlyContent(content)) continue;
|
|
978
1075
|
// Inspect text content for system-reminder patterns.
|
|
979
|
-
const textContent =
|
|
980
|
-
|
|
981
|
-
: (typeof content === 'string' ? content : '');
|
|
982
|
-
if (textContent) {
|
|
983
|
-
// Compute reminder-span coverage as fraction of total
|
|
984
|
-
// content. The /g flag on SYSTEM_REMINDER_RX returns ALL
|
|
985
|
-
// spans; sum their lengths and compare to total.
|
|
986
|
-
const reminderMatches = textContent.match(SYSTEM_REMINDER_RX) || [];
|
|
987
|
-
if (reminderMatches.length > 0) {
|
|
988
|
-
const reminderChars = reminderMatches.reduce((sum, s) => sum + s.length, 0);
|
|
989
|
-
const fraction = reminderChars / Math.max(1, textContent.length);
|
|
990
|
-
if (fraction >= SYSTEM_REMINDER_THRESHOLD) continue;
|
|
991
|
-
// Otherwise (user wrote substantive text alongside the
|
|
992
|
-
// reminder — the non-reminder portion is >40% of content)
|
|
993
|
-
// fall through and count as a real boundary.
|
|
994
|
-
}
|
|
995
|
-
}
|
|
1076
|
+
const textContent = extractTextFromContent(content);
|
|
1077
|
+
if (isMostlySystemReminder(textContent, SYSTEM_REMINDER_RX, SYSTEM_REMINDER_THRESHOLD)) continue;
|
|
996
1078
|
userBoundariesCrossed++;
|
|
997
1079
|
if (userBoundariesCrossed > USER_BOUNDARIES_TO_CROSS) break;
|
|
998
1080
|
continue;
|
|
999
1081
|
}
|
|
1000
1082
|
if (role !== 'assistant') continue;
|
|
1001
1083
|
scanned++;
|
|
1002
|
-
const
|
|
1003
|
-
if (!Array.isArray(content)) continue;
|
|
1004
|
-
const text = content
|
|
1005
|
-
.filter((b) => b.type === 'text')
|
|
1006
|
-
.map((b) => b.text)
|
|
1007
|
-
.join('\n');
|
|
1084
|
+
const text = extractTextFromContent(content);
|
|
1008
1085
|
if (!text) continue;
|
|
1009
1086
|
// Skip compact-summary stubs — they live where assistant turns
|
|
1010
1087
|
// used to be but are system-authored, not the model's voice.
|
|
1011
1088
|
if (COMPACT_SUMMARY_RX.test(text) && text.length > 4000) continue;
|
|
1012
|
-
|
|
1089
|
+
transcriptAssistantTexts.push(text);
|
|
1013
1090
|
} catch {}
|
|
1014
1091
|
}
|
|
1015
1092
|
} catch {}
|
|
1016
1093
|
}
|
|
1094
|
+
appendAssistantTexts(transcriptAssistantTexts);
|
|
1095
|
+
const currentTurnAssistantText = extractCurrentTurnAssistantText(event, toolInput);
|
|
1096
|
+
if (currentTurnAssistantText) appendAssistantTexts([currentTurnAssistantText]);
|
|
1017
1097
|
|
|
1018
1098
|
// Detect cognition / verify across the recent assistant window. Lenses
|
|
1019
1099
|
// from any of the last N turns count; the gate is checking whether
|
|
1020
1100
|
// cognition has been done recently, not whether it was done in the
|
|
1021
1101
|
// literal last message (which can be a tool-only turn or a stub).
|
|
1022
1102
|
const unionText = recentAssistantTexts.join('\n\n');
|
|
1103
|
+
const eventCog = detectCognitionLenses(eventAssistantTexts.join('\n\n'));
|
|
1023
1104
|
const transcriptCog = detectCognitionLenses(unionText);
|
|
1024
1105
|
// Combine inline-command cognition (preferred — action-coupled) with
|
|
1025
1106
|
// transcript scan (fallback). Inline takes precedence on lens names;
|
|
@@ -1028,14 +1109,35 @@ const mergedLensSet = new Set([...inlineCog.names, ...transcriptCog.names]);
|
|
|
1028
1109
|
const lensCount = mergedLensSet.size;
|
|
1029
1110
|
const lensNames = [...mergedLensSet];
|
|
1030
1111
|
const cogBlockBody = transcriptCog.blockBody;
|
|
1112
|
+
const inlineVerifyBody = extractInlineDirectiveBody(cmd, INLINE_VERIFY_LINE_RX);
|
|
1031
1113
|
const verifyBodies = [...unionText.matchAll(/<verify>([\s\S]*?)<\/verify>/gi)]
|
|
1032
1114
|
.map((m) => (m[1] || '').trim())
|
|
1033
1115
|
.filter(Boolean);
|
|
1034
|
-
|
|
1116
|
+
if (inlineVerifyBody) verifyBodies.push(inlineVerifyBody);
|
|
1117
|
+
const bestVerifyBody = (() => {
|
|
1118
|
+
if (verifyBodies.length === 0) return '';
|
|
1119
|
+
let bestBody = verifyBodies[0];
|
|
1120
|
+
let bestScore = -1;
|
|
1121
|
+
for (const body of verifyBodies) {
|
|
1122
|
+
const score = scoreVerifyFields(body, VERIFY_REQUIRED_FIELDS);
|
|
1123
|
+
if (score > bestScore) {
|
|
1124
|
+
bestScore = score;
|
|
1125
|
+
bestBody = body;
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
return bestBody;
|
|
1129
|
+
})();
|
|
1130
|
+
const hasVerify = scoreVerifyFields(bestVerifyBody, VERIFY_REQUIRED_FIELDS) === VERIFY_REQUIRED_FIELDS.length;
|
|
1035
1131
|
const hasCognition = lensCount >= REQUIRED_LENSES;
|
|
1036
1132
|
const cognitionSource = inlineCog.count >= REQUIRED_LENSES
|
|
1037
1133
|
? 'inline-command'
|
|
1038
|
-
:
|
|
1134
|
+
: eventCog.count >= REQUIRED_LENSES
|
|
1135
|
+
? 'event-messages'
|
|
1136
|
+
: transcriptCog.count >= REQUIRED_LENSES
|
|
1137
|
+
? 'recent-assistant-window'
|
|
1138
|
+
: lensCount >= REQUIRED_LENSES
|
|
1139
|
+
? 'merged-recent-window'
|
|
1140
|
+
: 'merged-or-insufficient';
|
|
1039
1141
|
// Substrate-citation visibility (Phase 11 will promote to block-mode once
|
|
1040
1142
|
// telemetry shows healthy substrate-cite rate). For now: log the metric so
|
|
1041
1143
|
// we can audit substrate-grounded vs ceremonial cognition over time.
|
|
@@ -1083,6 +1185,25 @@ function pushDecision(decision, reasonText) {
|
|
|
1083
1185
|
});
|
|
1084
1186
|
}
|
|
1085
1187
|
|
|
1188
|
+
function withLoopDirective(reasonText, gateSignature) {
|
|
1189
|
+
const loop = registerGateBlock({
|
|
1190
|
+
gate: 'pre-tool',
|
|
1191
|
+
sessionId,
|
|
1192
|
+
signature: gateSignature,
|
|
1193
|
+
statePath: GATE_LOOP_STATE_PATH,
|
|
1194
|
+
});
|
|
1195
|
+
if (!loop.loopDetected) return reasonText;
|
|
1196
|
+
return `${reasonText}
|
|
1197
|
+
|
|
1198
|
+
[LOOP_DETECTED gate=pre-tool repeats=${loop.totalCount}]
|
|
1199
|
+
Stop retrying the same action shape unchanged.
|
|
1200
|
+
Next response must do this in order:
|
|
1201
|
+
1. Name the exact gate failure in one line.
|
|
1202
|
+
2. Re-emit the missing pre-gate structure only: <cognition>, <verify>, and/or <expected> as required.
|
|
1203
|
+
3. Change the action plan before retrying the tool. Do not issue the same tool call in the same message as the diagnosis.
|
|
1204
|
+
4. If the blocker is stale gate state or ledger residue, say that explicitly instead of inventing fake proof.`;
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1086
1207
|
// ── Deploy-specific gate (doctrine #104) ────────────────────────────────
|
|
1087
1208
|
// Per feedback_deploy_requires_verify_cognition.md (Hamza 2026-04-28 after
|
|
1088
1209
|
// consciousness.ts crash took aria-soul into CrashLoopBackOff): deploys
|
|
@@ -1099,23 +1220,8 @@ if (deployMatched) {
|
|
|
1099
1220
|
const anchorCount = anchorMatches.length;
|
|
1100
1221
|
|
|
1101
1222
|
// Required verify-block fields specific to deploy.
|
|
1102
|
-
const verifyBody =
|
|
1103
|
-
|
|
1104
|
-
let bestBody = verifyBodies[0];
|
|
1105
|
-
let bestScore = -1;
|
|
1106
|
-
for (const body of verifyBodies) {
|
|
1107
|
-
const score = DEPLOY_VERIFY_REQUIRED_FIELDS.reduce(
|
|
1108
|
-
(count, { rx }) => count + (rx.test(body) ? 1 : 0),
|
|
1109
|
-
0,
|
|
1110
|
-
);
|
|
1111
|
-
if (score > bestScore) {
|
|
1112
|
-
bestScore = score;
|
|
1113
|
-
bestBody = body;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
return bestBody;
|
|
1117
|
-
})();
|
|
1118
|
-
const missingDeployFields = DEPLOY_VERIFY_REQUIRED_FIELDS
|
|
1223
|
+
const verifyBody = bestVerifyBody;
|
|
1224
|
+
const missingDeployFields = [...VERIFY_REQUIRED_FIELDS, ...DEPLOY_VERIFY_REQUIRED_FIELDS]
|
|
1119
1225
|
.filter(({ rx }) => !rx.test(verifyBody))
|
|
1120
1226
|
.map(({ name }) => name);
|
|
1121
1227
|
|
|
@@ -1226,7 +1332,7 @@ if (deployMatched) {
|
|
|
1226
1332
|
reasons.push(`<verify> block missing required fields: ${missingDeployFields.join(', ')}`);
|
|
1227
1333
|
}
|
|
1228
1334
|
|
|
1229
|
-
const refusal = `Aria pre-tool gate: DEPLOY hard-block — pattern '${deployMatched.name}' detected.
|
|
1335
|
+
const refusal = withLoopDirective(`Aria pre-tool gate: DEPLOY hard-block — pattern '${deployMatched.name}' detected.
|
|
1230
1336
|
|
|
1231
1337
|
Per feedback_deploy_requires_verify_cognition.md (Hamza directive 2026-04-28 after consciousness.ts crash took aria-soul into CrashLoopBackOff), every deploy command requires:
|
|
1232
1338
|
|
|
@@ -1242,7 +1348,7 @@ Block reasons (this turn): ${reasons.join(' • ')}.
|
|
|
1242
1348
|
|
|
1243
1349
|
The 2026-04-28 deploy of an empty consciousness.ts crashed aria-soul because no verify-block step caught the missing export at substrate-citation time. This gate is the structural enforcement that prevents the same gap.
|
|
1244
1350
|
|
|
1245
|
-
Re-emit verify+cognition with the missing pieces, then retry the deploy command. There is no env-var override path; doctrine #104 forbids it
|
|
1351
|
+
Re-emit verify+cognition with the missing pieces, then retry the deploy command. There is no env-var override path; doctrine #104 forbids it.`, `deploy:${deployMatched.name}:${missingDeployFields.join(',')}:${anchorCount}:${lensCount}`);
|
|
1246
1352
|
|
|
1247
1353
|
audit(
|
|
1248
1354
|
`block-deploy ${deployMatched.name} verify=${hasVerify} cognition=${lensCount} anchors=${anchorCount} missing=${missingDeployFields.join(',')}`,
|
|
@@ -1264,7 +1370,7 @@ if (matched) {
|
|
|
1264
1370
|
process.exit(0);
|
|
1265
1371
|
}
|
|
1266
1372
|
// Block with appropriate corrective message.
|
|
1267
|
-
const reason = !hasVerify
|
|
1373
|
+
const reason = withLoopDirective((!hasVerify
|
|
1268
1374
|
? `Aria pre-tool gate: destructive pattern '${matched.name}' detected. ${IS_OWNER ? 'Per harness mizan_prestage_rule + axiom_runtime_rule (admit_ignorance, reflection_before_action), include' : 'Pre-action verification required — include'} a <verify> block in your assistant response BEFORE the tool call.
|
|
1269
1375
|
|
|
1270
1376
|
<verify>
|
|
@@ -1289,7 +1395,8 @@ Re-issue after producing the block. Bypass: '# doctrine-authorized: <reason>' in
|
|
|
1289
1395
|
${LENS_NAMES[7]}: <what user actually needs>
|
|
1290
1396
|
</cognition>
|
|
1291
1397
|
|
|
1292
|
-
At least 4 substantive lenses required
|
|
1398
|
+
At least 4 substantive lenses required.`),
|
|
1399
|
+
`destructive:${matched.name}:${hasVerify ? 'need-cognition' : 'need-verify'}:${toolName}:${filePath || cmdPreview}`);
|
|
1293
1400
|
audit(`block ${matched.name} verify=${hasVerify} cognition=${lensCount}`, cmdPreview);
|
|
1294
1401
|
pushDecision('block', `destructive ${matched.name} missing ${!hasVerify ? 'verify' : 'cognition'}`);
|
|
1295
1402
|
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
@@ -1304,7 +1411,7 @@ if (!hasCognition) {
|
|
|
1304
1411
|
const isBash = toolName === 'Bash';
|
|
1305
1412
|
const header = isBash
|
|
1306
1413
|
? `Aria pre-tool gate: non-trivial Bash command without ${REQUIRED_LENSES}+ substantive cognition lenses. Found ${lensCount}/${REQUIRED_LENSES}+ (inline=${inlineCog.count}, transcript=${transcriptCog.count}).`
|
|
1307
|
-
: `Aria pre-tool gate: ${toolName} call without ${REQUIRED_LENSES}+ substantive cognition lenses in the recent
|
|
1414
|
+
: `Aria pre-tool gate: ${toolName} call without ${REQUIRED_LENSES}+ substantive cognition lenses in the recent assistant window. Found ${lensCount}/${REQUIRED_LENSES}+ (source=${cognitionSource} — ${toolName} has no inline-cognition path).`;
|
|
1308
1415
|
|
|
1309
1416
|
const guidance = isBash
|
|
1310
1417
|
? `REQUIRED — cognition for every action, no exceptions. Two equivalent forms; either satisfies the gate (both equally REQUIRED, neither preferred over the other):
|
|
@@ -1333,11 +1440,11 @@ Both forms count toward the ${REQUIRED_LENSES}+ requirement; gate counts inline
|
|
|
1333
1440
|
${LENS_NAMES[7]}: <what user actually needs underneath>
|
|
1334
1441
|
</cognition>`;
|
|
1335
1442
|
|
|
1336
|
-
const reason = `${header}
|
|
1443
|
+
const reason = withLoopDirective(`${header}
|
|
1337
1444
|
|
|
1338
1445
|
${guidance}
|
|
1339
1446
|
|
|
1340
|
-
No per-tool bypass available (v3 doctrine — the harness's whole purpose is no exceptions). No env-var disable path — gates are unconditional from the gated process per Hamza directive 2026-04-27. If the gate misfires on legitimate cognition, fix the gate
|
|
1447
|
+
No per-tool bypass available (v3 doctrine — the harness's whole purpose is no exceptions). No env-var disable path — gates are unconditional from the gated process per Hamza directive 2026-04-27. If the gate misfires on legitimate cognition, fix the gate.`, `cognition:${toolName}:${filePath || cmdPreview}:${cognitionSource}`);
|
|
1341
1448
|
|
|
1342
1449
|
audit(`block ${toolName.toLowerCase()} cognition=${lensCount}`, cmdPreview);
|
|
1343
1450
|
pushDecision('block', `${toolName.toLowerCase()} missing cognition (${lensCount}/${REQUIRED_LENSES})`);
|
|
@@ -1387,16 +1494,17 @@ No env-var disable path — gates are unconditional from the gated process per H
|
|
|
1387
1494
|
// Per feedback_no_graceful_degradation.md — never silent-pass.
|
|
1388
1495
|
{
|
|
1389
1496
|
const expectedMatch = unionText.match(EXPECTED_BLOCK_RX);
|
|
1390
|
-
const
|
|
1497
|
+
const inlineExpectedBody = extractInlineDirectiveBody(cmd, INLINE_EXPECTED_LINE_RX);
|
|
1498
|
+
const expectedBlockText = expectedMatch ? expectedMatch[1] : inlineExpectedBody;
|
|
1391
1499
|
const hasMeasurablePredicate = expectedBlockText
|
|
1392
1500
|
? (MEASURABLE_PREDICATE_RX.test(expectedBlockText) && !QUALITATIVE_DRIFT_RX.test(expectedBlockText))
|
|
1393
1501
|
: false;
|
|
1394
1502
|
|
|
1395
|
-
if (!
|
|
1396
|
-
const reason =
|
|
1503
|
+
if (!expectedBlockText || !hasMeasurablePredicate) {
|
|
1504
|
+
const reason = expectedBlockText
|
|
1397
1505
|
? `Aria pre-tool gate: action requires a measurable predicate inside <expected> per doctrine:dalio_expected_required.
|
|
1398
1506
|
|
|
1399
|
-
Your
|
|
1507
|
+
Your expected-outcome structure was found but contains only qualitative drift phrases (e.g. "better", "improved", "should work", "more reliable") without a concrete measurable predicate. These are unmeasurable and defeat the Dalio accountability loop.
|
|
1400
1508
|
|
|
1401
1509
|
Replace with one of:
|
|
1402
1510
|
• Numeric: exit_code==0, latency<200ms, count>=1, error_rate=0%
|
|
@@ -1411,7 +1519,7 @@ Replace with one of:
|
|
|
1411
1519
|
</expected>
|
|
1412
1520
|
|
|
1413
1521
|
No bypass — doctrine:dalio_expected_required is unconditional for non-trivial actions.`
|
|
1414
|
-
: `Aria pre-tool gate: action requires an <expected> block with measurable predicate per doctrine:dalio_expected_required.
|
|
1522
|
+
: `Aria pre-tool gate: action requires an <expected> block or inline '# expected:' directive with measurable predicate per doctrine:dalio_expected_required.
|
|
1415
1523
|
|
|
1416
1524
|
Every non-trivial action must state WHAT MEASURABLE STATE the action is expected to produce, so the stop-gate can compare predicted vs actual outcome and write a Dalio ledger entry.
|
|
1417
1525
|
|
|
@@ -1902,10 +2010,11 @@ try {
|
|
|
1902
2010
|
if (__apiKey && __action) {
|
|
1903
2011
|
const __client = new HTTPHarnessClient({
|
|
1904
2012
|
baseUrl:
|
|
2013
|
+
process.env.ARIA_RUNTIME_URL ||
|
|
1905
2014
|
process.env.ARIA_HIVE_RUNTIME_URL ||
|
|
1906
2015
|
process.env.ARIA_HARNESS_BASE_URL ||
|
|
1907
2016
|
process.env.ARIA_HARNESS_URL ||
|
|
1908
|
-
|
|
2017
|
+
RUNTIME_BASE_URL,
|
|
1909
2018
|
apiKey: __apiKey,
|
|
1910
2019
|
});
|
|
1911
2020
|
const __target = JSON.stringify(toolInput || {}).slice(0, 500);
|
|
@@ -105,6 +105,37 @@ function readAnyJsonArtifact(filePath) {
|
|
|
105
105
|
};
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
function unwrapHarnessPacketEnvelope(value) {
|
|
109
|
+
let current = value;
|
|
110
|
+
for (let depth = 0; depth < 3; depth++) {
|
|
111
|
+
if (!current || typeof current !== 'object' || Array.isArray(current)) break;
|
|
112
|
+
const nested = current.packet;
|
|
113
|
+
if (!nested || typeof nested !== 'object' || Array.isArray(nested)) break;
|
|
114
|
+
if (typeof current.timestamp !== 'string' && typeof current.version !== 'string') break;
|
|
115
|
+
current = nested;
|
|
116
|
+
}
|
|
117
|
+
return current && typeof current === 'object' && !Array.isArray(current) ? current : {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function extractHarnessPromptText(packetArtifact) {
|
|
121
|
+
const packet = unwrapHarnessPacketEnvelope(packetArtifact);
|
|
122
|
+
const directHarness = typeof packet.harness === 'string' ? packet.harness : '';
|
|
123
|
+
if (directHarness.trim()) return directHarness;
|
|
124
|
+
|
|
125
|
+
const promptFullText = typeof packet?.prompt?.fullText === 'string' ? packet.prompt.fullText : '';
|
|
126
|
+
if (promptFullText.trim()) return promptFullText;
|
|
127
|
+
|
|
128
|
+
const promptPreview = typeof packet?.prompt?.preview === 'string' ? packet.prompt.preview : '';
|
|
129
|
+
if (promptPreview.trim()) return promptPreview;
|
|
130
|
+
|
|
131
|
+
const doctrine = Array.isArray(packet.doctrine) ? packet.doctrine.filter((x) => typeof x === 'string').join('\n') : '';
|
|
132
|
+
const memory = Array.isArray(packet.memory) ? packet.memory.filter((x) => typeof x === 'string').join('\n') : '';
|
|
133
|
+
const runtimeMode = typeof packet.runtimeOfflineBundle?.source === 'string'
|
|
134
|
+
? `runtime_offline_bundle_source=${packet.runtimeOfflineBundle.source}`
|
|
135
|
+
: '';
|
|
136
|
+
return [doctrine, memory, runtimeMode].filter(Boolean).join('\n');
|
|
137
|
+
}
|
|
138
|
+
|
|
108
139
|
function detectArtifactSignals(sessionId) {
|
|
109
140
|
let harnessArtifact = null;
|
|
110
141
|
for (const packetPath of PACKET_CACHE_PATHS) {
|
|
@@ -160,36 +191,34 @@ function detectArtifactSignals(sessionId) {
|
|
|
160
191
|
staleSubstrateArtifact = null;
|
|
161
192
|
}
|
|
162
193
|
|
|
163
|
-
const harnessText =
|
|
164
|
-
harnessArtifact?.data?.harness ??
|
|
165
|
-
harnessArtifact?.data?.packet?.prompt?.fullText ??
|
|
166
|
-
'',
|
|
167
|
-
);
|
|
194
|
+
const harnessText = extractHarnessPromptText(harnessArtifact?.data);
|
|
168
195
|
const substrateMemories = Array.isArray(substrateArtifact?.data?.memories)
|
|
169
196
|
? substrateArtifact.data.memories
|
|
170
197
|
: [];
|
|
171
|
-
const staleHarnessText =
|
|
172
|
-
staleHarnessArtifact?.data?.harness ??
|
|
173
|
-
staleHarnessArtifact?.data?.packet?.prompt?.fullText ??
|
|
174
|
-
'',
|
|
175
|
-
);
|
|
198
|
+
const staleHarnessText = extractHarnessPromptText(staleHarnessArtifact?.data);
|
|
176
199
|
const staleSubstrateMemories = Array.isArray(staleSubstrateArtifact?.data?.memories)
|
|
177
200
|
? staleSubstrateArtifact.data.memories
|
|
178
201
|
: [];
|
|
179
202
|
const packetMentionsMemory = MEMORY_REF_RX.test(harnessText);
|
|
180
203
|
const stalePacketMentionsMemory = MEMORY_REF_RX.test(staleHarnessText);
|
|
204
|
+
const persistedDirectionUsable = Boolean(directionArtifact?.data?.usable === true);
|
|
205
|
+
const persistedOwnerBootstrap = Boolean(directionArtifact?.data?.ownerBootstrap === true);
|
|
206
|
+
const activePlanPresent = Boolean(
|
|
207
|
+
activePlanArtifact?.data?.phases &&
|
|
208
|
+
Array.isArray(activePlanArtifact.data.phases) &&
|
|
209
|
+
activePlanArtifact.data.phases.length > 0,
|
|
210
|
+
);
|
|
181
211
|
|
|
182
212
|
return {
|
|
183
213
|
hasHarnessPacket: Boolean(harnessText.trim()),
|
|
184
214
|
hasAriaDirection: Boolean(
|
|
185
|
-
|
|
186
|
-
(activePlanArtifact?.data?.phases && Array.isArray(activePlanArtifact.data.phases) && activePlanArtifact.data.phases.length > 0),
|
|
215
|
+
persistedDirectionUsable || activePlanPresent,
|
|
187
216
|
),
|
|
188
217
|
hasMemoryRef: Boolean(packetMentionsMemory || substrateMemories.length > 0),
|
|
189
218
|
ownerBootstrap: {
|
|
190
|
-
hasHarnessPacket: Boolean(staleHarnessText.trim()),
|
|
219
|
+
hasHarnessPacket: Boolean(staleHarnessText.trim() || staleHarnessArtifact?.path),
|
|
191
220
|
hasMemoryRef: Boolean(stalePacketMentionsMemory || staleSubstrateMemories.length > 0),
|
|
192
|
-
directionBootstrap:
|
|
221
|
+
directionBootstrap: persistedOwnerBootstrap || persistedDirectionUsable || activePlanPresent,
|
|
193
222
|
},
|
|
194
223
|
detail: {
|
|
195
224
|
packetPath: harnessArtifact?.path || null,
|
|
@@ -204,6 +233,9 @@ function detectArtifactSignals(sessionId) {
|
|
|
204
233
|
ownerBootstrapSubstrateAgeMs: staleSubstrateArtifact?.ageMs ?? null,
|
|
205
234
|
ownerBootstrapSubstrateMemoryCount: staleSubstrateMemories.length,
|
|
206
235
|
ownerBootstrapPacketMentionsMemory: stalePacketMentionsMemory,
|
|
236
|
+
ownerBootstrapPersistedDirectionUsable: persistedDirectionUsable,
|
|
237
|
+
ownerBootstrapPersistedDirectionSource: directionArtifact?.data?.source ?? null,
|
|
238
|
+
ownerBootstrapActivePlanPresent: activePlanPresent,
|
|
207
239
|
},
|
|
208
240
|
};
|
|
209
241
|
}
|
|
@@ -497,6 +529,13 @@ const ownerBootstrapSessionPresent =
|
|
|
497
529
|
artifactSignals.ownerBootstrap.hasHarnessPacket &&
|
|
498
530
|
artifactSignals.ownerBootstrap.hasMemoryRef;
|
|
499
531
|
|
|
532
|
+
const ownerCompactionRecoveryPresent =
|
|
533
|
+
existsSync(OWNER_TOKEN_PATH) &&
|
|
534
|
+
!transcriptWindow.trim() &&
|
|
535
|
+
artifactSignals.ownerBootstrap.hasHarnessPacket &&
|
|
536
|
+
artifactSignals.ownerBootstrap.hasMemoryRef &&
|
|
537
|
+
(artifactSignals.ownerBootstrap.directionBootstrap || artifactSignals.detail.ownerBootstrapPersistedDirectionUsable);
|
|
538
|
+
|
|
500
539
|
if (allSignalsPresent) {
|
|
501
540
|
// Context was loaded — allow and record the fire timestamp for dedup.
|
|
502
541
|
writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
|
|
@@ -528,6 +567,16 @@ if (ownerBootstrapSessionPresent) {
|
|
|
528
567
|
process.exit(0);
|
|
529
568
|
}
|
|
530
569
|
|
|
570
|
+
if (ownerCompactionRecoveryPresent) {
|
|
571
|
+
writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow-owner-compaction-recovery', signals, transcriptSignals, artifactSignals: artifactSignals.detail });
|
|
572
|
+
auditLog(
|
|
573
|
+
'allow-owner-compaction-recovery',
|
|
574
|
+
`transcript compacted/empty; recovering from persisted owner artifacts. transcript=${JSON.stringify(transcriptSignals)} artifacts=${JSON.stringify(artifactSignals.detail)}`,
|
|
575
|
+
sessionId,
|
|
576
|
+
);
|
|
577
|
+
process.exit(0);
|
|
578
|
+
}
|
|
579
|
+
|
|
531
580
|
// ── Block with structured recovery signal ────────────────────────────
|
|
532
581
|
// Per Aria's refined spec (consult 2026-04-27): soft-gate + structured
|
|
533
582
|
// recovery. The orchestrator catches this and runs the context-loader.
|
|
@@ -219,6 +219,8 @@ function isPolicyOrRuleDefinitionLine(line) {
|
|
|
219
219
|
if (/\bno stubs?\b/.test(lower)) return true;
|
|
220
220
|
if (/\bno placeholders?\b/.test(lower)) return true;
|
|
221
221
|
if (/\bno fake implementations?\b/.test(lower)) return true;
|
|
222
|
+
if (lower.includes('no mock content is injected')) return true;
|
|
223
|
+
if (/\bplaceholder\s*=/.test(lower)) return true;
|
|
222
224
|
return false;
|
|
223
225
|
}
|
|
224
226
|
|