@aria_asi/cli 0.2.30 → 0.2.32
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 +115 -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 +551 -11
- 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 +115 -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 +27 -9
- 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-agent-handoff.mjs +23 -0
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
- package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
- package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
- 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 +40 -5
- package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +28 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
- package/dist/runtime/doctrine_trigger_map.json +534 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +460 -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 +12 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1140 -48
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +12 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-agent-handoff.mjs +23 -0
- package/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/hooks/aria-pre-tool-gate.mjs +383 -93
- package/hooks/aria-preprompt-consult.mjs +28 -2
- package/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/hooks/aria-stop-gate.mjs +346 -81
- package/hooks/doctrine_trigger_map.json +55 -0
- 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 +40 -5
- package/opencode-plugins/harness-stop/index.js +133 -10
- 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 +460 -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 +1140 -48
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +126 -20
- package/src/connectors/codex.ts +559 -10
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +117 -0
- package/src/connectors/opencode.ts +28 -9
- 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
|
|
@@ -321,12 +332,29 @@ function actionMatchesPattern(action, pattern, target) {
|
|
|
321
332
|
}
|
|
322
333
|
}
|
|
323
334
|
|
|
335
|
+
function isOperationalMaintenanceCommand(command) {
|
|
336
|
+
const cmd = String(command || '').trim().replace(/\s+/g, ' ');
|
|
337
|
+
if (!cmd || /[;&|`$()]/.test(cmd)) return false;
|
|
338
|
+
|
|
339
|
+
// A rollout restart requeues existing pod templates; it does not change images,
|
|
340
|
+
// manifests, or cluster policy. Keep this narrow so deploy/apply/delete still gate.
|
|
341
|
+
const kubectlGlobalFlag = String.raw`(?:--(?:namespace|context|kubeconfig|as|as-group|cluster|user|server|request-timeout|field-manager)(?:=|\s+)\S+|-[A-Za-z](?:\s+\S+)?)`;
|
|
342
|
+
const kubectlTailFlag = String.raw`(?:--(?:namespace|context|request-timeout|field-manager|selector)(?:=|\s+)\S+|-[A-Za-z](?:\s+\S+)?)`;
|
|
343
|
+
const workloadTarget = String.raw`(?:deploy(?:ment)?|statefulset|sts|daemonset|ds)(?:\/[^\s]+|\s+[^\s-][^\s]*)`;
|
|
344
|
+
const rolloutRestartRx = new RegExp(
|
|
345
|
+
String.raw`^kubectl(?:\s+${kubectlGlobalFlag})*\s+rollout\s+restart\s+${workloadTarget}(?:\s+${kubectlTailFlag})*$`,
|
|
346
|
+
'i',
|
|
347
|
+
);
|
|
348
|
+
return rolloutRestartRx.test(cmd);
|
|
349
|
+
}
|
|
350
|
+
|
|
324
351
|
function classifyToolForBinding(toolName, command, filePath) {
|
|
325
352
|
// Map Claude Code tool + payload to a binding-vocabulary action.
|
|
326
353
|
if (toolName === 'Read' || toolName === 'Glob' || toolName === 'Grep') return { action: 'read', target: filePath || '' };
|
|
327
354
|
if (toolName === 'Edit' || toolName === 'Write' || toolName === 'NotebookEdit') return { action: 'edit', target: filePath || '' };
|
|
328
355
|
if (toolName === 'Bash') {
|
|
329
356
|
const cmd = String(command || '');
|
|
357
|
+
if (isOperationalMaintenanceCommand(cmd)) return { action: 'kubectl_maintenance', target: cmd.slice(0, 80) };
|
|
330
358
|
if (/^kubectl\s+(get|describe|logs|exec\s+\S+\s+--\s+printenv|top|version|api-resources)\b/.test(cmd)) return { action: 'kubectl_get', target: cmd.slice(0, 80) };
|
|
331
359
|
if (/^kubectl\s+(apply|delete|set|patch|rollout|scale|drain|cordon)\b/.test(cmd)) return { action: 'kubectl_apply', target: cmd.slice(0, 80) };
|
|
332
360
|
if (/curl\s+.*\/api\/harness\/(delegate|codex|army|plan)/.test(cmd)) return { action: 'consult', target: 'harness' };
|
|
@@ -446,12 +474,11 @@ const MEASURABLE_PREDICATE_RX = /(?:>=|<=|==|!=|>|<|≥|≤)\s*\d+(?:\.\d+)?(?:m
|
|
|
446
474
|
// Any <expected> block containing ONLY these (no actual predicate) is rejected.
|
|
447
475
|
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
476
|
|
|
449
|
-
// ──
|
|
477
|
+
// ── Canonical lens labeling (Phase 11 #59 corrected) ────────────────────────
|
|
450
478
|
//
|
|
451
|
-
//
|
|
452
|
-
//
|
|
453
|
-
//
|
|
454
|
-
// vocabulary never appears in client-facing block-reason text.
|
|
479
|
+
// The lens names themselves carry meaning and must remain canonical on every
|
|
480
|
+
// surface. Readability comes from the explanatory prose inside each lens, not
|
|
481
|
+
// by swapping the lens label for a flattened stand-in.
|
|
455
482
|
//
|
|
456
483
|
// Tier is read from the most recent harness-via-sdk packet cache at
|
|
457
484
|
// ~/.claude/.aria-harness-last-packet.json. Two detection paths:
|
|
@@ -484,12 +511,9 @@ function resolveOwnerTier() {
|
|
|
484
511
|
|
|
485
512
|
const IS_OWNER = resolveOwnerTier();
|
|
486
513
|
|
|
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;
|
|
514
|
+
// Active visible label set for this execution — canonical labels on every
|
|
515
|
+
// surface. Tier only affects other policy surfaces, not lens semantics.
|
|
516
|
+
const LENS_NAMES = lensNamesForTier(IS_OWNER);
|
|
493
517
|
|
|
494
518
|
// Doctrine memory filenames are Aria-side substrate IP. On client surfaces
|
|
495
519
|
// replace them with generic descriptions in block-reason text.
|
|
@@ -504,6 +528,7 @@ function docRef(canonicalFilename, genericDescription) {
|
|
|
504
528
|
// colon, not a placeholder template). The substance check (added
|
|
505
529
|
// 2026-04-26) defeats ritual emission — `nur: ok` no longer counts.
|
|
506
530
|
const COGNITION_BLOCK_RX = /<cognition>([\s\S]*?)<\/cognition>/i;
|
|
531
|
+
const APPLIED_COGNITION_BLOCK_RX = /<applied_cognition>([\s\S]*?)<\/applied_cognition>/i;
|
|
507
532
|
// Hamza directive 2026-04-28: 8-lens enforcement, not 4-of-8. The earlier
|
|
508
533
|
// REQUIRED_LENSES=4 was a regression — owner caught it ("i no longer see
|
|
509
534
|
// 8 lens cognition per turn"). All 8 canonical lenses must appear with
|
|
@@ -547,18 +572,28 @@ const SHORT_BASH_LIMIT = 30;
|
|
|
547
572
|
// (b) Single-line: `# cognition: <label>=<text>; <label>=<text>; ...`
|
|
548
573
|
//
|
|
549
574
|
// Substance check applies (≥SUBSTANCE_MIN_CHARS, not a `<placeholder>`).
|
|
550
|
-
//
|
|
575
|
+
// All required substantive lenses inline → gate passes, no transcript scan
|
|
551
576
|
// needed. Cognition becomes a property of THE ACTION, not of some
|
|
552
577
|
// message somewhere — the deepest reading of "cognition before action."
|
|
553
578
|
//
|
|
554
|
-
// Inline regex is built from the active LENS_NAMES
|
|
555
|
-
//
|
|
579
|
+
// Inline regex is built from the active canonical LENS_NAMES. We do not
|
|
580
|
+
// accept flattened generic aliases because they change the lens meaning.
|
|
556
581
|
const _INLINE_LENS_PATTERN = LENS_NAMES.join('|');
|
|
557
582
|
const INLINE_LENS_LINE_RX_GLOBAL = new RegExp(
|
|
558
583
|
`^\\s*#\\s*(${_INLINE_LENS_PATTERN})\\s*:\\s*(.+)$`,
|
|
559
584
|
'gim',
|
|
560
585
|
);
|
|
561
586
|
const INLINE_COGNITION_HEADER_RX = /^\s*#\s*cognition\s*:\s*(.+)$/im;
|
|
587
|
+
const INLINE_EXPECTED_LINE_RX = /^\s*#\s*expected\s*:\s*(.+)$/gim;
|
|
588
|
+
const INLINE_VERIFY_LINE_RX = /^\s*#\s*verify\s*:\s*(.+)$/gim;
|
|
589
|
+
|
|
590
|
+
const VERIFY_REQUIRED_FIELDS = [
|
|
591
|
+
{ rx: /\btarget\s*:/i, name: 'target' },
|
|
592
|
+
{ rx: /\brole\s*:/i, name: 'role' },
|
|
593
|
+
{ rx: /\bverified\s*:/i, name: 'verified' },
|
|
594
|
+
{ rx: /\brollback\s*:/i, name: 'rollback' },
|
|
595
|
+
{ rx: /\baxiom\s*:/i, name: 'axiom' },
|
|
596
|
+
];
|
|
562
597
|
|
|
563
598
|
function detectInlineCognition(cmd) {
|
|
564
599
|
const names = [];
|
|
@@ -593,6 +628,152 @@ function detectInlineCognition(cmd) {
|
|
|
593
628
|
return { count: names.length, names, hasSubstrateCite };
|
|
594
629
|
}
|
|
595
630
|
|
|
631
|
+
function validateAppliedCognitionContract(text, cmdText = '') {
|
|
632
|
+
const bodyText = `${String(text || '')}\n${String(cmdText || '')}`;
|
|
633
|
+
const block = bodyText.match(APPLIED_COGNITION_BLOCK_RX);
|
|
634
|
+
const inlineBody = String(cmdText || '')
|
|
635
|
+
.split('\n')
|
|
636
|
+
.filter((line) => /^\s*#\s*(?:decision[_ -]?delta|dominant[_ -]?domain|binds[_ -]?to|expected[_ -]?predicate|artifact[_ -]?change)\s*:/i.test(line))
|
|
637
|
+
.join('\n');
|
|
638
|
+
const contractBody = block ? block[1] : inlineBody;
|
|
639
|
+
if (!contractBody) return { ok: false, violations: ['missing <applied_cognition> contract or inline applied-cognition comments'] };
|
|
640
|
+
const required = [
|
|
641
|
+
['decision_delta', /\bdecision[_ -]?delta\s*:/i],
|
|
642
|
+
['dominant_domain', /\bdominant[_ -]?domain\s*:/i],
|
|
643
|
+
['binds_to', /\bbinds[_ -]?to\s*:/i],
|
|
644
|
+
['expected_predicate', /\bexpected[_ -]?predicate\s*:/i],
|
|
645
|
+
['artifact_change', /\bartifact[_ -]?change\s*:/i],
|
|
646
|
+
];
|
|
647
|
+
const violations = [];
|
|
648
|
+
for (const [name, rx] of required) {
|
|
649
|
+
if (!rx.test(contractBody)) violations.push(`missing ${name}`);
|
|
650
|
+
}
|
|
651
|
+
if (/decision[_ -]?delta\s*:\s*(?:none|n\/a|no change|unchanged|same)/i.test(contractBody)) {
|
|
652
|
+
violations.push('decision_delta says cognition changed nothing');
|
|
653
|
+
}
|
|
654
|
+
return { ok: violations.length === 0, violations, body: contractBody.trim() };
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function normalizeInlineDirectiveBody(rawBody) {
|
|
658
|
+
if (!rawBody) return '';
|
|
659
|
+
const segments = String(rawBody)
|
|
660
|
+
.split(/;|\s+(?=[a-z_][a-z0-9_-]*\s*[=:])/i)
|
|
661
|
+
.map((segment) => segment.trim())
|
|
662
|
+
.filter(Boolean);
|
|
663
|
+
const lines = [];
|
|
664
|
+
for (const segment of segments) {
|
|
665
|
+
const match = segment.match(/^([a-z_][a-z0-9_-]*)\s*[=:]\s*(.+)$/i);
|
|
666
|
+
if (match) {
|
|
667
|
+
lines.push(`${match[1]}: ${match[2].trim()}`);
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
lines.push(segment);
|
|
671
|
+
}
|
|
672
|
+
return lines.join('\n').trim();
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function extractInlineDirectiveBody(cmd, directiveRx) {
|
|
676
|
+
if (!cmd || !(directiveRx instanceof RegExp)) return '';
|
|
677
|
+
directiveRx.lastIndex = 0;
|
|
678
|
+
const lines = [];
|
|
679
|
+
let match;
|
|
680
|
+
while ((match = directiveRx.exec(cmd)) !== null) {
|
|
681
|
+
const normalized = normalizeInlineDirectiveBody(match[1]);
|
|
682
|
+
if (normalized) lines.push(normalized);
|
|
683
|
+
}
|
|
684
|
+
return lines.join('\n').trim();
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function scoreVerifyFields(body, fields) {
|
|
688
|
+
if (!body) return 0;
|
|
689
|
+
return fields.reduce((count, { rx }) => count + (rx.test(body) ? 1 : 0), 0);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function extractCurrentTurnAssistantText(event, toolInput) {
|
|
693
|
+
const candidates = [
|
|
694
|
+
event.assistant_text,
|
|
695
|
+
event.assistantText,
|
|
696
|
+
event.draft,
|
|
697
|
+
event.text,
|
|
698
|
+
event.current_text,
|
|
699
|
+
event.currentText,
|
|
700
|
+
event.message,
|
|
701
|
+
event.prompt,
|
|
702
|
+
toolInput?.assistant_text,
|
|
703
|
+
toolInput?.assistantText,
|
|
704
|
+
toolInput?.text,
|
|
705
|
+
];
|
|
706
|
+
const parts = [];
|
|
707
|
+
const seen = new Set();
|
|
708
|
+
for (const candidate of candidates) {
|
|
709
|
+
if (typeof candidate !== 'string') continue;
|
|
710
|
+
const text = candidate.trim();
|
|
711
|
+
if (!text || seen.has(text)) continue;
|
|
712
|
+
seen.add(text);
|
|
713
|
+
parts.push(text);
|
|
714
|
+
}
|
|
715
|
+
return parts.join('\n\n').trim();
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function extractTextForUserCorrection(content) {
|
|
719
|
+
if (Array.isArray(content)) {
|
|
720
|
+
return content.filter((block) => block && block.type === 'text').map((block) => block.text || '').filter(Boolean).join('\n');
|
|
721
|
+
}
|
|
722
|
+
return typeof content === 'string' ? content : '';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function isRuntimeInjectedUserText(text) {
|
|
726
|
+
return /^\s*\[tool_result\b/i.test(text) ||
|
|
727
|
+
/(?:PreToolUse:[A-Za-z]+ hook blocking error|Stop hook feedback:|Aria pre-tool gate:|Aria stop-gate:|<system-reminder>|<task-notification>|task-notification)/i.test(text);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
function collectRecentRealUserText(event, transcriptPath, limit = 6) {
|
|
731
|
+
const texts = [];
|
|
732
|
+
const seen = new Set();
|
|
733
|
+
const pushText = (text) => {
|
|
734
|
+
const normalized = String(text || '').trim();
|
|
735
|
+
if (!normalized || seen.has(normalized) || isRuntimeInjectedUserText(normalized)) return;
|
|
736
|
+
seen.add(normalized);
|
|
737
|
+
texts.push(normalized);
|
|
738
|
+
};
|
|
739
|
+
const messages = Array.isArray(event?.messages) ? event.messages : [];
|
|
740
|
+
for (let i = messages.length - 1; i >= 0 && texts.length < limit; i--) {
|
|
741
|
+
const role = messages[i]?.message?.role ?? messages[i]?.role ?? messages[i]?.type;
|
|
742
|
+
if (role !== 'user') continue;
|
|
743
|
+
const content = messages[i]?.message?.content ?? messages[i]?.content ?? [];
|
|
744
|
+
if (Array.isArray(content) && content.length > 0 && content.every((block) => block && block.type === 'tool_result')) continue;
|
|
745
|
+
pushText(extractTextForUserCorrection(content));
|
|
746
|
+
}
|
|
747
|
+
if (transcriptPath && existsSync(transcriptPath) && texts.length < limit) {
|
|
748
|
+
try {
|
|
749
|
+
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
750
|
+
for (let i = lines.length - 1; i >= 0 && texts.length < limit; i--) {
|
|
751
|
+
let entry;
|
|
752
|
+
try { entry = JSON.parse(lines[i]); } catch { continue; }
|
|
753
|
+
const role = entry?.message?.role ?? entry?.role ?? entry?.type;
|
|
754
|
+
if (role !== 'user') continue;
|
|
755
|
+
const content = entry?.message?.content ?? entry?.content ?? [];
|
|
756
|
+
if (Array.isArray(content) && content.length > 0 && content.every((block) => block && block.type === 'tool_result')) continue;
|
|
757
|
+
pushText(extractTextForUserCorrection(content));
|
|
758
|
+
}
|
|
759
|
+
} catch {}
|
|
760
|
+
}
|
|
761
|
+
return texts.join('\n\n');
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function userCorrectionBlocksCommand(userText, command) {
|
|
765
|
+
const text = String(userText || '');
|
|
766
|
+
const cmd = String(command || '');
|
|
767
|
+
if (!text || !cmd) return null;
|
|
768
|
+
const k8sMutation = /\bkubectl\s+(?:apply|delete|set\s+image|patch|replace|create|scale|rollout\s+(?:restart|undo))\b|scripts\/deploy-|\bdocker\s+push\b/i.test(cmd);
|
|
769
|
+
const explicitStop = /\b(?:stop|do\s+not|don't|quit|cease)\b[\s\S]{0,160}\b(?:deploy|redeploy|restart|rollout|kubectl|command|pods?)\b/i.test(text);
|
|
770
|
+
if (explicitStop && k8sMutation) return 'recent user explicitly told the agent to stop deploy/restart command attempts';
|
|
771
|
+
const macTargetInCommand = /\bmlx-mac\b|\bdeployment\/mlx-mac|\bdeploy(?:ment)?\s+mlx-mac/i.test(cmd);
|
|
772
|
+
const macContradiction = /\b(?:mac\s+lanes?|mac\s+pods?|mlx-mac)\b[\s\S]{0,140}\b(?:not\s+pods?|no\s+such\s+thing|non[-\s]?existent|do(?:es)?\s+not\s+exist|don't\s+exist)\b|\b(?:no\s+such\s+thing|non[-\s]?existent|do(?:es)?\s+not\s+exist|don't\s+exist)\b[\s\S]{0,140}\b(?:mac\s+lanes?|mac\s+pods?|mlx-mac|pods?)\b/i.test(text);
|
|
773
|
+
if (macTargetInCommand && macContradiction) return 'recent user contradicted the mlx-mac Kubernetes-pod/deployment assumption';
|
|
774
|
+
return null;
|
|
775
|
+
}
|
|
776
|
+
|
|
596
777
|
// Substance-checking lens detection (added 2026-04-26 per Hamza's
|
|
597
778
|
// gate-improvement doctrine: form-only emission must not satisfy
|
|
598
779
|
// the gate). For each lens, look for `<lens>: <content>` and verify
|
|
@@ -722,6 +903,9 @@ const HARNESS_URL =
|
|
|
722
903
|
process.env.ARIA_HARNESS_BASE_URL ||
|
|
723
904
|
process.env.ARIA_HARNESS_URL ||
|
|
724
905
|
'https://harness.ariasos.com';
|
|
906
|
+
const RUNTIME_BASE_URL =
|
|
907
|
+
process.env.ARIA_RUNTIME_URL ||
|
|
908
|
+
'http://127.0.0.1:4319';
|
|
725
909
|
const HARNESS_TOKEN = process.env.ARIA_HARNESS_TOKEN || '';
|
|
726
910
|
const LOG_PUSH_DISABLED = process.env.ARIA_COGNITION_PUSH === 'off';
|
|
727
911
|
const MAX_IN_FLIGHT = 16;
|
|
@@ -846,6 +1030,7 @@ try {
|
|
|
846
1030
|
|
|
847
1031
|
const toolName = event.tool_name ?? event.toolName ?? '';
|
|
848
1032
|
const toolInput = event.tool_input ?? event.toolInput ?? {};
|
|
1033
|
+
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
849
1034
|
|
|
850
1035
|
// Gate every action tool — every tool that mutates state must go through
|
|
851
1036
|
// cognition challenge. Hamza 2026-04-26: "regardless if u arent even
|
|
@@ -868,6 +1053,26 @@ const cmdPreview = toolName === 'Bash'
|
|
|
868
1053
|
? cmd.slice(0, 80).replace(/\s+/g, ' ')
|
|
869
1054
|
: `${toolName} ${filePath || '(no path)'}`.slice(0, 80);
|
|
870
1055
|
|
|
1056
|
+
if (toolName === 'Bash') {
|
|
1057
|
+
const recentUserText = collectRecentRealUserText(event, transcriptPath);
|
|
1058
|
+
const correctionBlockReason = userCorrectionBlocksCommand(recentUserText, cmd);
|
|
1059
|
+
if (correctionBlockReason) {
|
|
1060
|
+
const reason = `Aria pre-tool gate: USER-CORRECTION hard-block.
|
|
1061
|
+
|
|
1062
|
+
${correctionBlockReason}.
|
|
1063
|
+
|
|
1064
|
+
The next assistant response must stop the command loop, quote the user's correction in one sentence, and re-evaluate the target from substrate before any further mutation. Do not retry this Bash command shape.`;
|
|
1065
|
+
audit('block-user-correction-override', cmdPreview);
|
|
1066
|
+
emitBlock(reason, { source: 'pre-tool/user-correction' });
|
|
1067
|
+
process.exit(2);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
if (toolName === 'Bash' && isOperationalMaintenanceCommand(cmd)) {
|
|
1072
|
+
audit('allow-operational-maintenance kubectl-rollout-restart', cmdPreview);
|
|
1073
|
+
process.exit(0);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
871
1076
|
// V3: per-command bypass removed entirely. The only escape valves are:
|
|
872
1077
|
// (1) Trivial-bash whitelist — short read-only commands pass without cognition
|
|
873
1078
|
// (2) ~/.claude/settings.json hook removal — visible user action Hamza controls
|
|
@@ -951,8 +1156,27 @@ const HARD_LOOKBACK_CAP = 50;
|
|
|
951
1156
|
// window since the substance check defends quality regardless.
|
|
952
1157
|
const USER_BOUNDARIES_TO_CROSS = 5;
|
|
953
1158
|
|
|
954
|
-
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
955
1159
|
const recentAssistantTexts = [];
|
|
1160
|
+
const recentAssistantTextSet = new Set();
|
|
1161
|
+
function appendAssistantTexts(texts) {
|
|
1162
|
+
for (const text of texts) {
|
|
1163
|
+
if (!text || recentAssistantTextSet.has(text)) continue;
|
|
1164
|
+
recentAssistantTexts.push(text);
|
|
1165
|
+
recentAssistantTextSet.add(text);
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
const eventMessages = Array.isArray(event.messages) ? event.messages : [];
|
|
1170
|
+
const eventAssistantTexts = collectRecentAssistantTextsFromMessages(eventMessages, {
|
|
1171
|
+
compactSummaryRx: COMPACT_SUMMARY_RX,
|
|
1172
|
+
hardLookbackCap: HARD_LOOKBACK_CAP,
|
|
1173
|
+
systemReminderRx: SYSTEM_REMINDER_RX,
|
|
1174
|
+
systemReminderThreshold: SYSTEM_REMINDER_THRESHOLD,
|
|
1175
|
+
userBoundariesToCross: USER_BOUNDARIES_TO_CROSS,
|
|
1176
|
+
});
|
|
1177
|
+
appendAssistantTexts(eventAssistantTexts);
|
|
1178
|
+
|
|
1179
|
+
const transcriptAssistantTexts = [];
|
|
956
1180
|
if (transcriptPath && existsSync(transcriptPath)) {
|
|
957
1181
|
try {
|
|
958
1182
|
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
@@ -961,7 +1185,8 @@ if (transcriptPath && existsSync(transcriptPath)) {
|
|
|
961
1185
|
for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
|
|
962
1186
|
try {
|
|
963
1187
|
const m = JSON.parse(lines[i]);
|
|
964
|
-
const role = m
|
|
1188
|
+
const role = normalizeRole(m);
|
|
1189
|
+
const content = normalizeContent(m);
|
|
965
1190
|
if (role === 'user') {
|
|
966
1191
|
// Skip messages that aren't real user input:
|
|
967
1192
|
// (a) tool_result blocks (runtime feeding back tool output)
|
|
@@ -969,57 +1194,36 @@ if (transcriptPath && existsSync(transcriptPath)) {
|
|
|
969
1194
|
// task-notifications, gentle reminders) — runtime-
|
|
970
1195
|
// authored, not user voice. Counting them eats the
|
|
971
1196
|
// 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;
|
|
1197
|
+
if (isToolResultOnlyContent(content)) continue;
|
|
978
1198
|
// 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
|
-
}
|
|
1199
|
+
const textContent = extractTextFromContent(content);
|
|
1200
|
+
if (isMostlySystemReminder(textContent, SYSTEM_REMINDER_RX, SYSTEM_REMINDER_THRESHOLD)) continue;
|
|
996
1201
|
userBoundariesCrossed++;
|
|
997
1202
|
if (userBoundariesCrossed > USER_BOUNDARIES_TO_CROSS) break;
|
|
998
1203
|
continue;
|
|
999
1204
|
}
|
|
1000
1205
|
if (role !== 'assistant') continue;
|
|
1001
1206
|
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');
|
|
1207
|
+
const text = extractTextFromContent(content);
|
|
1008
1208
|
if (!text) continue;
|
|
1009
1209
|
// Skip compact-summary stubs — they live where assistant turns
|
|
1010
1210
|
// used to be but are system-authored, not the model's voice.
|
|
1011
1211
|
if (COMPACT_SUMMARY_RX.test(text) && text.length > 4000) continue;
|
|
1012
|
-
|
|
1212
|
+
transcriptAssistantTexts.push(text);
|
|
1013
1213
|
} catch {}
|
|
1014
1214
|
}
|
|
1015
1215
|
} catch {}
|
|
1016
1216
|
}
|
|
1217
|
+
appendAssistantTexts(transcriptAssistantTexts);
|
|
1218
|
+
const currentTurnAssistantText = extractCurrentTurnAssistantText(event, toolInput);
|
|
1219
|
+
if (currentTurnAssistantText) appendAssistantTexts([currentTurnAssistantText]);
|
|
1017
1220
|
|
|
1018
1221
|
// Detect cognition / verify across the recent assistant window. Lenses
|
|
1019
1222
|
// from any of the last N turns count; the gate is checking whether
|
|
1020
1223
|
// cognition has been done recently, not whether it was done in the
|
|
1021
1224
|
// literal last message (which can be a tool-only turn or a stub).
|
|
1022
1225
|
const unionText = recentAssistantTexts.join('\n\n');
|
|
1226
|
+
const eventCog = detectCognitionLenses(eventAssistantTexts.join('\n\n'));
|
|
1023
1227
|
const transcriptCog = detectCognitionLenses(unionText);
|
|
1024
1228
|
// Combine inline-command cognition (preferred — action-coupled) with
|
|
1025
1229
|
// transcript scan (fallback). Inline takes precedence on lens names;
|
|
@@ -1028,14 +1232,36 @@ const mergedLensSet = new Set([...inlineCog.names, ...transcriptCog.names]);
|
|
|
1028
1232
|
const lensCount = mergedLensSet.size;
|
|
1029
1233
|
const lensNames = [...mergedLensSet];
|
|
1030
1234
|
const cogBlockBody = transcriptCog.blockBody;
|
|
1235
|
+
const inlineVerifyBody = extractInlineDirectiveBody(cmd, INLINE_VERIFY_LINE_RX);
|
|
1031
1236
|
const verifyBodies = [...unionText.matchAll(/<verify>([\s\S]*?)<\/verify>/gi)]
|
|
1032
1237
|
.map((m) => (m[1] || '').trim())
|
|
1033
1238
|
.filter(Boolean);
|
|
1034
|
-
|
|
1239
|
+
if (inlineVerifyBody) verifyBodies.push(inlineVerifyBody);
|
|
1240
|
+
const bestVerifyBody = (() => {
|
|
1241
|
+
if (verifyBodies.length === 0) return '';
|
|
1242
|
+
let bestBody = verifyBodies[0];
|
|
1243
|
+
let bestScore = -1;
|
|
1244
|
+
for (const body of verifyBodies) {
|
|
1245
|
+
const score = scoreVerifyFields(body, VERIFY_REQUIRED_FIELDS);
|
|
1246
|
+
if (score > bestScore) {
|
|
1247
|
+
bestScore = score;
|
|
1248
|
+
bestBody = body;
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
return bestBody;
|
|
1252
|
+
})();
|
|
1253
|
+
const hasVerify = scoreVerifyFields(bestVerifyBody, VERIFY_REQUIRED_FIELDS) === VERIFY_REQUIRED_FIELDS.length;
|
|
1035
1254
|
const hasCognition = lensCount >= REQUIRED_LENSES;
|
|
1255
|
+
const appliedContract = validateAppliedCognitionContract(unionText, cmd);
|
|
1036
1256
|
const cognitionSource = inlineCog.count >= REQUIRED_LENSES
|
|
1037
1257
|
? 'inline-command'
|
|
1038
|
-
:
|
|
1258
|
+
: eventCog.count >= REQUIRED_LENSES
|
|
1259
|
+
? 'event-messages'
|
|
1260
|
+
: transcriptCog.count >= REQUIRED_LENSES
|
|
1261
|
+
? 'recent-assistant-window'
|
|
1262
|
+
: lensCount >= REQUIRED_LENSES
|
|
1263
|
+
? 'merged-recent-window'
|
|
1264
|
+
: 'merged-or-insufficient';
|
|
1039
1265
|
// Substrate-citation visibility (Phase 11 will promote to block-mode once
|
|
1040
1266
|
// telemetry shows healthy substrate-cite rate). For now: log the metric so
|
|
1041
1267
|
// we can audit substrate-grounded vs ceremonial cognition over time.
|
|
@@ -1079,10 +1305,86 @@ function pushDecision(decision, reasonText) {
|
|
|
1079
1305
|
hasVerify,
|
|
1080
1306
|
hasCognition,
|
|
1081
1307
|
hasSubstrateCite,
|
|
1308
|
+
appliedCognitionContractOk: appliedContract.ok,
|
|
1082
1309
|
},
|
|
1083
1310
|
});
|
|
1084
1311
|
}
|
|
1085
1312
|
|
|
1313
|
+
function withLoopDirective(reasonText, gateSignature) {
|
|
1314
|
+
const loop = registerGateBlock({
|
|
1315
|
+
gate: 'pre-tool',
|
|
1316
|
+
sessionId,
|
|
1317
|
+
signature: gateSignature,
|
|
1318
|
+
statePath: GATE_LOOP_STATE_PATH,
|
|
1319
|
+
});
|
|
1320
|
+
if (!loop.loopDetected) return reasonText;
|
|
1321
|
+
return `${reasonText}
|
|
1322
|
+
|
|
1323
|
+
[LOOP_DETECTED gate=pre-tool repeats=${loop.totalCount}]
|
|
1324
|
+
Stop retrying the same action shape unchanged.
|
|
1325
|
+
Next response must do this in order:
|
|
1326
|
+
1. Name the exact gate failure in one line.
|
|
1327
|
+
2. Re-emit the missing pre-gate structure only: <cognition>, <verify>, and/or <expected> as required.
|
|
1328
|
+
3. Change the action plan before retrying the tool. Do not issue the same tool call in the same message as the diagnosis.
|
|
1329
|
+
4. If the blocker is stale gate state or ledger residue, say that explicitly instead of inventing fake proof.`;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
function buildForceRedoActionReason(reasonText, { source = 'pre-tool-gate', tool = toolName || 'unknown', lensCount = 0, requiredLenses = REQUIRED_LENSES } = {}) {
|
|
1333
|
+
if (String(reasonText || '').includes('ARIA FORCE_REDO_ACTION')) return reasonText;
|
|
1334
|
+
return [
|
|
1335
|
+
'=== ARIA FORCE_REDO_ACTION ===',
|
|
1336
|
+
`source: ${source}`,
|
|
1337
|
+
`tool: ${tool}`,
|
|
1338
|
+
'',
|
|
1339
|
+
'This is not a terminal error. It is a forced redo instruction before tool execution.',
|
|
1340
|
+
'Do not retry the same tool call unchanged. Re-author the action with cognition, proof, and expected outcome first.',
|
|
1341
|
+
'',
|
|
1342
|
+
'TEACHING:',
|
|
1343
|
+
'- Pre-tool gates exist to make the model think before acting, not to merely throw after a bad action shape.',
|
|
1344
|
+
'- The redo must name the failed mechanism, cite the current substrate, and state why the next action is safe.',
|
|
1345
|
+
'- Do not bypass by disabling tools, changing endpoints from memory, shortening runtime, or asking Hamza to solve saved-memory ambiguity.',
|
|
1346
|
+
'',
|
|
1347
|
+
'ORIGINAL GATE REASON:',
|
|
1348
|
+
String(reasonText || '').trim(),
|
|
1349
|
+
'',
|
|
1350
|
+
'REQUIRED REDO SHAPE:',
|
|
1351
|
+
'1. Emit/attach <cognition> with the required lenses before retrying the tool.',
|
|
1352
|
+
'2. For Bash, prepend inline cognition comments to the command:',
|
|
1353
|
+
' # nur: <observed state and substrate, >=20 chars>',
|
|
1354
|
+
' # mizan: <risk/tradeoff and why this action is balanced, >=20 chars>',
|
|
1355
|
+
' # hikma: <doctrine applied and what it forbids, >=20 chars>',
|
|
1356
|
+
' # tafakkur: <second-order effects and expected result, >=20 chars>',
|
|
1357
|
+
`3. Current cognition count: ${lensCount}/${requiredLenses}.`,
|
|
1358
|
+
'4. If deploy/shared-infra: include <verify> with target, verified evidence, rollback, and axiom.',
|
|
1359
|
+
'5. If non-trivial action: include <expected> with numeric/boolean/state-string predicate.',
|
|
1360
|
+
'=== END FORCE_REDO_ACTION ===',
|
|
1361
|
+
].join('\n');
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
function emitBlock(reasonText, meta = {}) {
|
|
1365
|
+
console.log(JSON.stringify({ decision: 'block', reason: buildForceRedoActionReason(reasonText, meta) }));
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
if (hasCognition && !appliedContract.ok) {
|
|
1369
|
+
const reason = `Aria pre-tool gate: cognition exists but is not applied to the action.
|
|
1370
|
+
|
|
1371
|
+
The harness no longer accepts cognition as decorative preface. Before a mutating tool call, cognition must produce an execution contract that changes or constrains the action.
|
|
1372
|
+
|
|
1373
|
+
Violations:
|
|
1374
|
+
${appliedContract.violations.map((v) => `- ${v}`).join('\n')}
|
|
1375
|
+
|
|
1376
|
+
Required contract, either in the assistant turn as <applied_cognition>...</applied_cognition> or inline Bash comments:
|
|
1377
|
+
decision_delta: <what changed because cognition ran; not "none">
|
|
1378
|
+
dominant_domain: <engineering_quality | trust | operations | security | product | ...>
|
|
1379
|
+
binds_to: <this exact tool call/action>
|
|
1380
|
+
expected_predicate: <numeric, boolean, or state-string predicate proving success>
|
|
1381
|
+
artifact_change: <how the artifact/action is different because cognition ran>`;
|
|
1382
|
+
audit('block-applied-cognition-contract', cmdPreview);
|
|
1383
|
+
pushDecision('block', `applied cognition contract missing: ${appliedContract.violations.join(', ')}`);
|
|
1384
|
+
emitBlock(reason, { source: 'pre-tool/applied-cognition-contract', tool: toolName, lensCount, requiredLenses: REQUIRED_LENSES });
|
|
1385
|
+
process.exit(2);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1086
1388
|
// ── Deploy-specific gate (doctrine #104) ────────────────────────────────
|
|
1087
1389
|
// Per feedback_deploy_requires_verify_cognition.md (Hamza 2026-04-28 after
|
|
1088
1390
|
// consciousness.ts crash took aria-soul into CrashLoopBackOff): deploys
|
|
@@ -1099,23 +1401,8 @@ if (deployMatched) {
|
|
|
1099
1401
|
const anchorCount = anchorMatches.length;
|
|
1100
1402
|
|
|
1101
1403
|
// 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
|
|
1404
|
+
const verifyBody = bestVerifyBody;
|
|
1405
|
+
const missingDeployFields = [...VERIFY_REQUIRED_FIELDS, ...DEPLOY_VERIFY_REQUIRED_FIELDS]
|
|
1119
1406
|
.filter(({ rx }) => !rx.test(verifyBody))
|
|
1120
1407
|
.map(({ name }) => name);
|
|
1121
1408
|
|
|
@@ -1226,7 +1513,7 @@ if (deployMatched) {
|
|
|
1226
1513
|
reasons.push(`<verify> block missing required fields: ${missingDeployFields.join(', ')}`);
|
|
1227
1514
|
}
|
|
1228
1515
|
|
|
1229
|
-
const refusal = `Aria pre-tool gate: DEPLOY hard-block — pattern '${deployMatched.name}' detected.
|
|
1516
|
+
const refusal = withLoopDirective(`Aria pre-tool gate: DEPLOY hard-block — pattern '${deployMatched.name}' detected.
|
|
1230
1517
|
|
|
1231
1518
|
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
1519
|
|
|
@@ -1242,14 +1529,14 @@ Block reasons (this turn): ${reasons.join(' • ')}.
|
|
|
1242
1529
|
|
|
1243
1530
|
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
1531
|
|
|
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
|
|
1532
|
+
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
1533
|
|
|
1247
1534
|
audit(
|
|
1248
1535
|
`block-deploy ${deployMatched.name} verify=${hasVerify} cognition=${lensCount} anchors=${anchorCount} missing=${missingDeployFields.join(',')}`,
|
|
1249
1536
|
cmdPreview,
|
|
1250
1537
|
);
|
|
1251
1538
|
pushDecision('block', `deploy ${deployMatched.name}: ${reasons.join('; ')}`);
|
|
1252
|
-
|
|
1539
|
+
emitBlock(refusal, { source: 'pre-tool/deploy', tool: toolName });
|
|
1253
1540
|
process.exit(2);
|
|
1254
1541
|
}
|
|
1255
1542
|
|
|
@@ -1264,7 +1551,7 @@ if (matched) {
|
|
|
1264
1551
|
process.exit(0);
|
|
1265
1552
|
}
|
|
1266
1553
|
// Block with appropriate corrective message.
|
|
1267
|
-
const reason = !hasVerify
|
|
1554
|
+
const reason = withLoopDirective((!hasVerify
|
|
1268
1555
|
? `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
1556
|
|
|
1270
1557
|
<verify>
|
|
@@ -1289,10 +1576,11 @@ Re-issue after producing the block. Bypass: '# doctrine-authorized: <reason>' in
|
|
|
1289
1576
|
${LENS_NAMES[7]}: <what user actually needs>
|
|
1290
1577
|
</cognition>
|
|
1291
1578
|
|
|
1292
|
-
At least 4 substantive lenses required
|
|
1579
|
+
At least 4 substantive lenses required.`),
|
|
1580
|
+
`destructive:${matched.name}:${hasVerify ? 'need-cognition' : 'need-verify'}:${toolName}:${filePath || cmdPreview}`);
|
|
1293
1581
|
audit(`block ${matched.name} verify=${hasVerify} cognition=${lensCount}`, cmdPreview);
|
|
1294
1582
|
pushDecision('block', `destructive ${matched.name} missing ${!hasVerify ? 'verify' : 'cognition'}`);
|
|
1295
|
-
|
|
1583
|
+
emitBlock(reason, { source: 'pre-tool/destructive' });
|
|
1296
1584
|
process.exit(2);
|
|
1297
1585
|
}
|
|
1298
1586
|
|
|
@@ -1304,7 +1592,7 @@ if (!hasCognition) {
|
|
|
1304
1592
|
const isBash = toolName === 'Bash';
|
|
1305
1593
|
const header = isBash
|
|
1306
1594
|
? `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
|
|
1595
|
+
: `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
1596
|
|
|
1309
1597
|
const guidance = isBash
|
|
1310
1598
|
? `REQUIRED — cognition for every action, no exceptions. Two equivalent forms; either satisfies the gate (both equally REQUIRED, neither preferred over the other):
|
|
@@ -1333,15 +1621,15 @@ Both forms count toward the ${REQUIRED_LENSES}+ requirement; gate counts inline
|
|
|
1333
1621
|
${LENS_NAMES[7]}: <what user actually needs underneath>
|
|
1334
1622
|
</cognition>`;
|
|
1335
1623
|
|
|
1336
|
-
const reason = `${header}
|
|
1624
|
+
const reason = withLoopDirective(`${header}
|
|
1337
1625
|
|
|
1338
1626
|
${guidance}
|
|
1339
1627
|
|
|
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
|
|
1628
|
+
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
1629
|
|
|
1342
1630
|
audit(`block ${toolName.toLowerCase()} cognition=${lensCount}`, cmdPreview);
|
|
1343
1631
|
pushDecision('block', `${toolName.toLowerCase()} missing cognition (${lensCount}/${REQUIRED_LENSES})`);
|
|
1344
|
-
|
|
1632
|
+
emitBlock(reason, { source: 'pre-tool/missing-cognition' });
|
|
1345
1633
|
process.exit(2);
|
|
1346
1634
|
}
|
|
1347
1635
|
|
|
@@ -1372,7 +1660,7 @@ No env-var disable path — gates are unconditional from the gated process per H
|
|
|
1372
1660
|
|
|
1373
1661
|
audit(`block-discovery-unresolved ${toolName.toLowerCase()}`, cmdPreview);
|
|
1374
1662
|
pushDecision('block', `${toolName.toLowerCase()} cognition has unresolved discovery`);
|
|
1375
|
-
|
|
1663
|
+
emitBlock(reason, { source: 'pre-tool/discovery-binding' });
|
|
1376
1664
|
process.exit(2);
|
|
1377
1665
|
}
|
|
1378
1666
|
|
|
@@ -1387,16 +1675,17 @@ No env-var disable path — gates are unconditional from the gated process per H
|
|
|
1387
1675
|
// Per feedback_no_graceful_degradation.md — never silent-pass.
|
|
1388
1676
|
{
|
|
1389
1677
|
const expectedMatch = unionText.match(EXPECTED_BLOCK_RX);
|
|
1390
|
-
const
|
|
1678
|
+
const inlineExpectedBody = extractInlineDirectiveBody(cmd, INLINE_EXPECTED_LINE_RX);
|
|
1679
|
+
const expectedBlockText = expectedMatch ? expectedMatch[1] : inlineExpectedBody;
|
|
1391
1680
|
const hasMeasurablePredicate = expectedBlockText
|
|
1392
1681
|
? (MEASURABLE_PREDICATE_RX.test(expectedBlockText) && !QUALITATIVE_DRIFT_RX.test(expectedBlockText))
|
|
1393
1682
|
: false;
|
|
1394
1683
|
|
|
1395
|
-
if (!
|
|
1396
|
-
const reason =
|
|
1684
|
+
if (!expectedBlockText || !hasMeasurablePredicate) {
|
|
1685
|
+
const reason = expectedBlockText
|
|
1397
1686
|
? `Aria pre-tool gate: action requires a measurable predicate inside <expected> per doctrine:dalio_expected_required.
|
|
1398
1687
|
|
|
1399
|
-
Your
|
|
1688
|
+
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
1689
|
|
|
1401
1690
|
Replace with one of:
|
|
1402
1691
|
• Numeric: exit_code==0, latency<200ms, count>=1, error_rate=0%
|
|
@@ -1411,7 +1700,7 @@ Replace with one of:
|
|
|
1411
1700
|
</expected>
|
|
1412
1701
|
|
|
1413
1702
|
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.
|
|
1703
|
+
: `Aria pre-tool gate: action requires an <expected> block or inline '# expected:' directive with measurable predicate per doctrine:dalio_expected_required.
|
|
1415
1704
|
|
|
1416
1705
|
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
1706
|
|
|
@@ -1435,7 +1724,7 @@ No bypass — doctrine:dalio_expected_required is unconditional for non-trivial
|
|
|
1435
1724
|
|
|
1436
1725
|
audit(`block-expected-missing ${toolName.toLowerCase()}`, cmdPreview);
|
|
1437
1726
|
pushDecision('block', `${toolName.toLowerCase()} missing <expected> measurable predicate`);
|
|
1438
|
-
|
|
1727
|
+
emitBlock(reason, { source: 'pre-tool/discovery-binding' });
|
|
1439
1728
|
process.exit(2);
|
|
1440
1729
|
}
|
|
1441
1730
|
}
|
|
@@ -1518,7 +1807,7 @@ Read it first (it is in your environment as ARIA_HARNESS_PACKET_PATH), then refe
|
|
|
1518
1807
|
|
|
1519
1808
|
Cognition-theater rejected per feedback_gates_enforce_form_not_substance.md.`;
|
|
1520
1809
|
audit(`block-subagent-no-packet-cite ${toolName.toLowerCase()}`, cmdPreview);
|
|
1521
|
-
|
|
1810
|
+
emitBlock(reason, { source: 'pre-tool/architecture-facts' });
|
|
1522
1811
|
process.exit(2);
|
|
1523
1812
|
}
|
|
1524
1813
|
})();
|
|
@@ -1561,7 +1850,7 @@ Rule references:
|
|
|
1561
1850
|
R10 no_kubectl_apply_for_image_drift — kubectl apply on aria-soul-stateful (project_forge_psi_oom_cascade.md)
|
|
1562
1851
|
R11 no_console_log_secrets — console.log with TOKEN/PASSWORD/SECRET/API_KEY/JWT/BEARER`;
|
|
1563
1852
|
audit(`block-arch-facts ${toolName.toLowerCase()} path=${filePath}`, `violations=${archResult.violations?.length ?? 0}`);
|
|
1564
|
-
|
|
1853
|
+
emitBlock(archReason, { source: 'pre-tool/architecture-facts' });
|
|
1565
1854
|
process.exit(2);
|
|
1566
1855
|
}
|
|
1567
1856
|
}
|
|
@@ -1663,7 +1952,7 @@ What Claude must do:
|
|
|
1663
1952
|
3. Wait for next user prompt — preprompt-consult will retry; if it succeeds a plan will exist on next tool call
|
|
1664
1953
|
|
|
1665
1954
|
Non-trivial actions are blocked until a plan exists. Trivial reads (ls/cat/grep) bypass automatically per existing whitelist. To temporarily disable binding for emergency: ARIA_BINDING_ENABLED=false (logged).`;
|
|
1666
|
-
|
|
1955
|
+
emitBlock(reason, { source: 'pre-tool/substrate-binding' });
|
|
1667
1956
|
process.exit(2);
|
|
1668
1957
|
}
|
|
1669
1958
|
}
|
|
@@ -1682,7 +1971,7 @@ What Claude must do:
|
|
|
1682
1971
|
3. Don't continue executing actions beyond the issued plan boundary
|
|
1683
1972
|
|
|
1684
1973
|
This prevents Claude from drifting past Aria's authorized scope.`;
|
|
1685
|
-
|
|
1974
|
+
emitBlock(reason, { source: 'pre-tool/substrate-binding' });
|
|
1686
1975
|
process.exit(2);
|
|
1687
1976
|
}
|
|
1688
1977
|
|
|
@@ -1719,7 +2008,7 @@ This prevents Claude from drifting past Aria's authorized scope.`;
|
|
|
1719
2008
|
if (!freshPhaseInfo || freshPhaseInfo.abortedHere) {
|
|
1720
2009
|
bindingAuditAppend({ event: 'block_aborted_phase_post_fallback_still_bad', sessionId, planId: plan.planId });
|
|
1721
2010
|
const reason = `Aria binding gate: aborted phase recovery minted plan ${plan.planId} but new plan also has no usable phase. Manual intervention required.`;
|
|
1722
|
-
|
|
2011
|
+
emitBlock(reason, { source: 'pre-tool/dalio-expected' });
|
|
1723
2012
|
process.exit(2);
|
|
1724
2013
|
}
|
|
1725
2014
|
phaseInfo = freshPhaseInfo;
|
|
@@ -1728,7 +2017,7 @@ This prevents Claude from drifting past Aria's authorized scope.`;
|
|
|
1728
2017
|
const reason = `Aria binding gate: phase ${phaseInfo.phase.id} of plan ${plan.planId} was reported aborted AND inline architect-fallback failed (exit=${architectExit}). Plan progression halted. ${architectStderr ? 'Architect stderr: ' + architectStderr : ''}
|
|
1729
2018
|
|
|
1730
2019
|
What Claude must do: emit [PLAN_BLOCKER reason="<concrete observation>" suggestedAmendment="<if any>"] for Hamza/Aria to issue a corrected plan. Do not continue executing the aborted plan.`;
|
|
1731
|
-
|
|
2020
|
+
emitBlock(reason, { source: 'pre-tool/dalio-expected' });
|
|
1732
2021
|
process.exit(2);
|
|
1733
2022
|
}
|
|
1734
2023
|
}
|
|
@@ -1756,7 +2045,7 @@ Forbidden actions for this phase: ${(phase.forbiddenActions || []).join(', ') ||
|
|
|
1756
2045
|
Allowed actions for this phase: ${(phase.allowedActions || []).join(', ') || '(none)'}
|
|
1757
2046
|
|
|
1758
2047
|
Claude must either: (a) reframe the action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] requesting Aria amend the plan.${recipeAddendum}`;
|
|
1759
|
-
|
|
2048
|
+
emitBlock(reason, { source: 'pre-tool/output-claim' });
|
|
1760
2049
|
process.exit(2);
|
|
1761
2050
|
}
|
|
1762
2051
|
|
|
@@ -1775,7 +2064,7 @@ Phase summary: ${phase.summary}
|
|
|
1775
2064
|
Allowed actions: ${(phase.allowedActions || []).join(', ') || '(none — phase is observation-only)'}
|
|
1776
2065
|
|
|
1777
2066
|
Claude must either: (a) reframe action to fit allowedActions, OR (b) emit [PLAN_BLOCKER reason="..."] for plan amendment.${recipeAddendum}`;
|
|
1778
|
-
|
|
2067
|
+
emitBlock(reason, { source: 'pre-tool/output-claim' });
|
|
1779
2068
|
process.exit(2);
|
|
1780
2069
|
}
|
|
1781
2070
|
|
|
@@ -1902,10 +2191,11 @@ try {
|
|
|
1902
2191
|
if (__apiKey && __action) {
|
|
1903
2192
|
const __client = new HTTPHarnessClient({
|
|
1904
2193
|
baseUrl:
|
|
2194
|
+
process.env.ARIA_RUNTIME_URL ||
|
|
1905
2195
|
process.env.ARIA_HIVE_RUNTIME_URL ||
|
|
1906
2196
|
process.env.ARIA_HARNESS_BASE_URL ||
|
|
1907
2197
|
process.env.ARIA_HARNESS_URL ||
|
|
1908
|
-
|
|
2198
|
+
RUNTIME_BASE_URL,
|
|
1909
2199
|
apiKey: __apiKey,
|
|
1910
2200
|
});
|
|
1911
2201
|
const __target = JSON.stringify(toolInput || {}).slice(0, 500);
|
|
@@ -1916,7 +2206,7 @@ try {
|
|
|
1916
2206
|
The substrate's contract gate refused this action. Local doctrine gates passed (cognition lenses, binding plan, drift) but the substrate sees a downstream contract that disallows this tool call. Address the substrate's reason above before retrying.`;
|
|
1917
2207
|
audit(`block-substrate-checkAction ${toolName.toLowerCase()} action=${__action} reason="${(__check.reason || '').slice(0, 80)}"`, cmdPreview);
|
|
1918
2208
|
pushDecision('block', `substrate checkAction denied: ${(__check.reason || 'unspecified').slice(0, 100)}`);
|
|
1919
|
-
|
|
2209
|
+
emitBlock(__reason, { source: 'pre-tool/final' });
|
|
1920
2210
|
process.exit(2);
|
|
1921
2211
|
}
|
|
1922
2212
|
}
|
|
@@ -2109,7 +2399,7 @@ causes merge conflicts and state divergence. Explicit coordination is the only s
|
|
|
2109
2399
|
pushDecision('block', `hive session-lock conflict on ${_lockCheckPath.slice(0, 80)}`);
|
|
2110
2400
|
console.log(JSON.stringify({
|
|
2111
2401
|
decision: 'block',
|
|
2112
|
-
reason: _lockBlockReason,
|
|
2402
|
+
reason: buildForceRedoActionReason(_lockBlockReason, { source: 'pre-tool/hive-lock-conflict', tool: toolName || 'unknown' }),
|
|
2113
2403
|
hookSpecificOutput: {
|
|
2114
2404
|
hookEventName: 'PreToolUse',
|
|
2115
2405
|
conflicting_locks: _conflictingLocks,
|