@aria_asi/cli 0.2.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/bin/aria.js +168 -0
- package/dist/aria-connector/src/auth-commands.d.ts +28 -0
- package/dist/aria-connector/src/auth-commands.d.ts.map +1 -0
- package/dist/aria-connector/src/auth-commands.js +129 -0
- package/dist/aria-connector/src/auth-commands.js.map +1 -0
- package/dist/aria-connector/src/auth.d.ts +12 -0
- package/dist/aria-connector/src/auth.d.ts.map +1 -0
- package/dist/aria-connector/src/auth.js +31 -0
- package/dist/aria-connector/src/auth.js.map +1 -0
- package/dist/aria-connector/src/auto-mcp.d.ts +23 -0
- package/dist/aria-connector/src/auto-mcp.d.ts.map +1 -0
- package/dist/aria-connector/src/auto-mcp.js +994 -0
- package/dist/aria-connector/src/auto-mcp.js.map +1 -0
- package/dist/aria-connector/src/chat.d.ts +21 -0
- package/dist/aria-connector/src/chat.d.ts.map +1 -0
- package/dist/aria-connector/src/chat.js +332 -0
- package/dist/aria-connector/src/chat.js.map +1 -0
- package/dist/aria-connector/src/codebase-scanner.d.ts +7 -0
- package/dist/aria-connector/src/codebase-scanner.d.ts.map +1 -0
- package/dist/aria-connector/src/codebase-scanner.js +6 -0
- package/dist/aria-connector/src/codebase-scanner.js.map +1 -0
- package/dist/aria-connector/src/cognition-log.d.ts +17 -0
- package/dist/aria-connector/src/cognition-log.d.ts.map +1 -0
- package/dist/aria-connector/src/cognition-log.js +19 -0
- package/dist/aria-connector/src/cognition-log.js.map +1 -0
- package/dist/aria-connector/src/config.d.ts +41 -0
- package/dist/aria-connector/src/config.d.ts.map +1 -0
- package/dist/aria-connector/src/config.js +50 -0
- package/dist/aria-connector/src/config.js.map +1 -0
- package/dist/aria-connector/src/connectors/claude-code.d.ts +4 -0
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/claude-code.js +204 -0
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -0
- package/dist/aria-connector/src/connectors/cursor.d.ts +4 -0
- package/dist/aria-connector/src/connectors/cursor.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/cursor.js +63 -0
- package/dist/aria-connector/src/connectors/cursor.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts +4 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.js +102 -0
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -0
- package/dist/aria-connector/src/connectors/shell.d.ts +4 -0
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/shell.js +58 -0
- package/dist/aria-connector/src/connectors/shell.js.map +1 -0
- package/dist/aria-connector/src/garden-client.d.ts +19 -0
- package/dist/aria-connector/src/garden-client.d.ts.map +1 -0
- package/dist/aria-connector/src/garden-client.js +85 -0
- package/dist/aria-connector/src/garden-client.js.map +1 -0
- package/dist/aria-connector/src/garden-control-plane.d.ts +22 -0
- package/dist/aria-connector/src/garden-control-plane.d.ts.map +1 -0
- package/dist/aria-connector/src/garden-control-plane.js +43 -0
- package/dist/aria-connector/src/garden-control-plane.js.map +1 -0
- package/dist/aria-connector/src/harness-client.d.ts +166 -0
- package/dist/aria-connector/src/harness-client.d.ts.map +1 -0
- package/dist/aria-connector/src/harness-client.js +344 -0
- package/dist/aria-connector/src/harness-client.js.map +1 -0
- package/dist/aria-connector/src/hive-client.d.ts +32 -0
- package/dist/aria-connector/src/hive-client.d.ts.map +1 -0
- package/dist/aria-connector/src/hive-client.js +69 -0
- package/dist/aria-connector/src/hive-client.js.map +1 -0
- package/dist/aria-connector/src/index.d.ts +19 -0
- package/dist/aria-connector/src/index.d.ts.map +1 -0
- package/dist/aria-connector/src/index.js +13 -0
- package/dist/aria-connector/src/index.js.map +1 -0
- package/dist/aria-connector/src/install-hooks.d.ts +18 -0
- package/dist/aria-connector/src/install-hooks.d.ts.map +1 -0
- package/dist/aria-connector/src/install-hooks.js +224 -0
- package/dist/aria-connector/src/install-hooks.js.map +1 -0
- package/dist/aria-connector/src/model-context.d.ts +8 -0
- package/dist/aria-connector/src/model-context.d.ts.map +1 -0
- package/dist/aria-connector/src/model-context.js +83 -0
- package/dist/aria-connector/src/model-context.js.map +1 -0
- package/dist/aria-connector/src/persona.d.ts +27 -0
- package/dist/aria-connector/src/persona.d.ts.map +1 -0
- package/dist/aria-connector/src/persona.js +86 -0
- package/dist/aria-connector/src/persona.js.map +1 -0
- package/dist/aria-connector/src/providers/anthropic.d.ts +4 -0
- package/dist/aria-connector/src/providers/anthropic.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/anthropic.js +92 -0
- package/dist/aria-connector/src/providers/anthropic.js.map +1 -0
- package/dist/aria-connector/src/providers/deepseek.d.ts +3 -0
- package/dist/aria-connector/src/providers/deepseek.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/deepseek.js +28 -0
- package/dist/aria-connector/src/providers/deepseek.js.map +1 -0
- package/dist/aria-connector/src/providers/google.d.ts +3 -0
- package/dist/aria-connector/src/providers/google.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/google.js +38 -0
- package/dist/aria-connector/src/providers/google.js.map +1 -0
- package/dist/aria-connector/src/providers/ollama.d.ts +3 -0
- package/dist/aria-connector/src/providers/ollama.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/ollama.js +28 -0
- package/dist/aria-connector/src/providers/ollama.js.map +1 -0
- package/dist/aria-connector/src/providers/openai.d.ts +4 -0
- package/dist/aria-connector/src/providers/openai.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/openai.js +84 -0
- package/dist/aria-connector/src/providers/openai.js.map +1 -0
- package/dist/aria-connector/src/providers/openrouter.d.ts +3 -0
- package/dist/aria-connector/src/providers/openrouter.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/openrouter.js +30 -0
- package/dist/aria-connector/src/providers/openrouter.js.map +1 -0
- package/dist/aria-connector/src/providers/types.d.ts +20 -0
- package/dist/aria-connector/src/providers/types.d.ts.map +1 -0
- package/dist/aria-connector/src/providers/types.js +2 -0
- package/dist/aria-connector/src/providers/types.js.map +1 -0
- package/dist/aria-connector/src/setup-wizard.d.ts +2 -0
- package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -0
- package/dist/aria-connector/src/setup-wizard.js +140 -0
- package/dist/aria-connector/src/setup-wizard.js.map +1 -0
- package/dist/aria-connector/src/types.d.ts +30 -0
- package/dist/aria-connector/src/types.d.ts.map +1 -0
- package/dist/aria-connector/src/types.js +5 -0
- package/dist/aria-connector/src/types.js.map +1 -0
- package/dist/aria-web/src/lib/codebase-scanner.d.ts +127 -0
- package/dist/aria-web/src/lib/codebase-scanner.d.ts.map +1 -0
- package/dist/aria-web/src/lib/codebase-scanner.js +1730 -0
- package/dist/aria-web/src/lib/codebase-scanner.js.map +1 -0
- package/dist/cli-0.2.0.tgz +0 -0
- package/dist/install.sh +13 -0
- package/hooks/aria-harness-via-sdk.mjs +317 -0
- package/hooks/aria-pre-tool-gate.mjs +596 -0
- package/hooks/aria-preprompt-consult.mjs +175 -0
- package/hooks/aria-stop-gate.mjs +222 -0
- package/package.json +47 -0
- package/src/__tests__/auth-commands.test.ts +132 -0
- package/src/auth-commands.ts +175 -0
- package/src/auth.ts +33 -0
- package/src/auto-mcp.ts +1172 -0
- package/src/chat.ts +387 -0
- package/src/codebase-scanner.ts +18 -0
- package/src/cognition-log.ts +30 -0
- package/src/config.ts +94 -0
- package/src/connectors/claude-code.ts +213 -0
- package/src/connectors/cursor.ts +75 -0
- package/src/connectors/opencode.ts +115 -0
- package/src/connectors/shell.ts +72 -0
- package/src/garden-client.ts +98 -0
- package/src/garden-control-plane.ts +108 -0
- package/src/harness-client.ts +454 -0
- package/src/hive-client.ts +104 -0
- package/src/index.ts +26 -0
- package/src/install-hooks.ts +259 -0
- package/src/model-context.ts +88 -0
- package/src/persona.ts +113 -0
- package/src/providers/anthropic.ts +120 -0
- package/src/providers/deepseek.ts +40 -0
- package/src/providers/google.ts +57 -0
- package/src/providers/ollama.ts +43 -0
- package/src/providers/openai.ts +108 -0
- package/src/providers/openrouter.ts +42 -0
- package/src/providers/types.ts +35 -0
- package/src/setup-wizard.ts +177 -0
- package/src/types.ts +32 -0
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Aria pre-tool-use gate — enforces cognition use before destructive tool calls.
|
|
3
|
+
//
|
|
4
|
+
// Runs as a Claude Code PreToolUse hook on every Bash invocation. For
|
|
5
|
+
// commands matching destructive-verb patterns, requires the most recent
|
|
6
|
+
// assistant turn to contain a structured <verify> block. Without it, the
|
|
7
|
+
// tool call is blocked with a corrective message that names the missing
|
|
8
|
+
// fields.
|
|
9
|
+
//
|
|
10
|
+
// Doctrine being enforced (from /tmp/aria-harness-full.txt):
|
|
11
|
+
// - mizan_prestage_rule: "Before drafting, check niyyah/pre-state: am I
|
|
12
|
+
// present, truthful, specific, grounded in verified substrate?"
|
|
13
|
+
// - drift_guard_rule: "Before answering, restate internally: current
|
|
14
|
+
// goal, current blocker, current stage, and next committed action."
|
|
15
|
+
// - axiom_runtime_rule.admit_ignorance + reflection_before_action
|
|
16
|
+
// - llm_worker_rule: "External LLMs are hands/renderers/reviewers.
|
|
17
|
+
// Aria memory, cognition, frames, Mizan, and Garden are the shared
|
|
18
|
+
// brain substrate."
|
|
19
|
+
//
|
|
20
|
+
// The gate is the *forcing function* that converts those rules from text
|
|
21
|
+
// instructions into actual enforcement. The harness already declares
|
|
22
|
+
// them; this hook is what makes them gate-real for Claude Code.
|
|
23
|
+
//
|
|
24
|
+
// Escape valves (v3 — Hamza 2026-04-26: "why is there an ability to
|
|
25
|
+
// bypass? doesnt that fundamentally void the purpose of the harness?"
|
|
26
|
+
// Per-command bypass was removed entirely — every prior bypass was
|
|
27
|
+
// traceable to a gate bug, not a legitimate exception.):
|
|
28
|
+
// - Trivial-bash whitelist: short read-only commands (ls/cat/grep/etc.)
|
|
29
|
+
// pass without cognition.
|
|
30
|
+
// - Set env ARIA_PRE_TOOL_GATE=off to disable globally (kill-switch).
|
|
31
|
+
// Setting an env var is a deliberate, audit-trailed override —
|
|
32
|
+
// deliberate enough that it's not the model's reflex.
|
|
33
|
+
// - When the gate misfires on legitimate work: fix the gate. The
|
|
34
|
+
// misfire IS the bug. Don't route around it.
|
|
35
|
+
//
|
|
36
|
+
// Audit log: every gate decision (allow / block / kill-switch) is
|
|
37
|
+
// appended to ~/.claude/aria-pre-tool-gate.log.
|
|
38
|
+
|
|
39
|
+
import { readFileSync, appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
40
|
+
import { dirname } from 'node:path';
|
|
41
|
+
|
|
42
|
+
const HOME = process.env.HOME || '/tmp';
|
|
43
|
+
const LOG = `${HOME}/.claude/aria-pre-tool-gate.log`;
|
|
44
|
+
|
|
45
|
+
// Bypass-counter (kept for historical visibility — past audit-log
|
|
46
|
+
// entries are still useful even though no new bypass entries can be
|
|
47
|
+
// created in v3 since the per-command bypass code path was removed).
|
|
48
|
+
// V1 added the WARN line at 5+/hour as a drift instrument; that line
|
|
49
|
+
// will only appear if old log entries spanning before v3 still fall
|
|
50
|
+
// in the rolling window.
|
|
51
|
+
const BYPASS_WARN_THRESHOLD_PER_HOUR = 5;
|
|
52
|
+
|
|
53
|
+
function readRecentBypassCount() {
|
|
54
|
+
try {
|
|
55
|
+
if (!existsSync(LOG)) return 0;
|
|
56
|
+
const cutoffMs = Date.now() - 60 * 60 * 1000;
|
|
57
|
+
const lines = readFileSync(LOG, 'utf-8').split('\n').filter(Boolean);
|
|
58
|
+
let n = 0;
|
|
59
|
+
// Walk backward — bypasses are recent if at all.
|
|
60
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
61
|
+
const line = lines[i];
|
|
62
|
+
if (!line.includes('bypass')) continue;
|
|
63
|
+
const tsEnd = line.indexOf(' ');
|
|
64
|
+
if (tsEnd < 0) continue;
|
|
65
|
+
const ts = Date.parse(line.slice(0, tsEnd));
|
|
66
|
+
if (!Number.isFinite(ts)) continue;
|
|
67
|
+
if (ts < cutoffMs) break;
|
|
68
|
+
n++;
|
|
69
|
+
}
|
|
70
|
+
return n;
|
|
71
|
+
} catch {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function audit(decision, summary) {
|
|
77
|
+
try {
|
|
78
|
+
if (!existsSync(dirname(LOG))) mkdirSync(dirname(LOG), { recursive: true });
|
|
79
|
+
appendFileSync(LOG, `${new Date().toISOString()} ${decision} ${summary}\n`);
|
|
80
|
+
// If this audit is itself a bypass, check the bypass-rate. A
|
|
81
|
+
// separate WARN line gets emitted (and surfaces via tail) when
|
|
82
|
+
// we exceed the per-hour threshold. The agent can't quietly
|
|
83
|
+
// drift back to bypass-as-default — drift is visible.
|
|
84
|
+
if (decision.startsWith('bypass')) {
|
|
85
|
+
const count = readRecentBypassCount();
|
|
86
|
+
if (count > BYPASS_WARN_THRESHOLD_PER_HOUR) {
|
|
87
|
+
appendFileSync(
|
|
88
|
+
LOG,
|
|
89
|
+
`${new Date().toISOString()} WARN-BYPASS-RATE ${count} bypasses in last hour (threshold ${BYPASS_WARN_THRESHOLD_PER_HOUR}) — drift check\n`,
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch {}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Kill-switch
|
|
97
|
+
if (process.env.ARIA_PRE_TOOL_GATE === 'off') {
|
|
98
|
+
audit('bypass-killswitch', 'env ARIA_PRE_TOOL_GATE=off');
|
|
99
|
+
process.exit(0);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Destructive-verb patterns. Tuned for tonight's failure modes; extend
|
|
103
|
+
// over time as new patterns surface.
|
|
104
|
+
const DESTRUCTIVE_PATTERNS = [
|
|
105
|
+
// sudo only when it's a command verb (start of line or after shell
|
|
106
|
+
// separator), not when it appears as an argument to another command
|
|
107
|
+
// (e.g. `echo "sudo …"` or `grep sudo`). The specific verb patterns
|
|
108
|
+
// below catch the actual destructive operations regardless of sudo
|
|
109
|
+
// prefix; this rule is the catch-all for arbitrary privilege elevation.
|
|
110
|
+
{ rx: /(?:^|[;&|]\s*|\$\(\s*|`\s*)sudo\s+\S/, name: 'sudo' },
|
|
111
|
+
{ rx: /systemctl\s+(disable|stop|mask|reset-failed|kill)/, name: 'systemctl-state-change' },
|
|
112
|
+
{ rx: /\brm\s+-[rRfF]+/, name: 'rm-recursive-or-force' },
|
|
113
|
+
{ rx: /\bgit\s+push\b.*\b--force\b/, name: 'git-push-force' },
|
|
114
|
+
{ rx: /\bgit\s+reset\s+--hard\b/, name: 'git-reset-hard' },
|
|
115
|
+
{ rx: /\bgit\s+checkout\s+--\b/, name: 'git-checkout-discard' },
|
|
116
|
+
{ rx: /\b--no-verify\b/, name: 'commit-no-verify' },
|
|
117
|
+
{ rx: /\b--no-gpg-sign\b/, name: 'commit-no-gpg-sign' },
|
|
118
|
+
{ rx: /\bkill\s+-(9|KILL|TERM|HUP|INT)\b/, name: 'kill-signal' },
|
|
119
|
+
{ rx: /\bpkill\b/, name: 'pkill' },
|
|
120
|
+
{ rx: /\b(DROP|TRUNCATE)\s+(TABLE|DATABASE|SCHEMA|INDEX)\b/i, name: 'sql-drop-or-truncate' },
|
|
121
|
+
{ rx: /\bkubectl\s+delete\b/, name: 'kubectl-delete' },
|
|
122
|
+
{ rx: /\bkubectl\s+(scale|rollout)\s+(undo|restart)\b/, name: 'kubectl-rollback' },
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
// The verify-block contract. All five fields required.
|
|
126
|
+
const VERIFY_BLOCK_RX =
|
|
127
|
+
/<verify>[\s\S]*?target\s*:[\s\S]*?role\s*:[\s\S]*?verified\s*:[\s\S]*?rollback\s*:[\s\S]*?axiom\s*:[\s\S]*?<\/verify>/i;
|
|
128
|
+
|
|
129
|
+
// 8-lens cognition block per EIGHT_LENS_DOCTRINE.md. Required on
|
|
130
|
+
// non-trivial Bash regardless of whether the command is destructive.
|
|
131
|
+
// Threshold: at least 4 of the 8 lens names must appear inside the
|
|
132
|
+
// block AND each must have substantive content (≥20 chars after the
|
|
133
|
+
// colon, not a placeholder template). The substance check (added
|
|
134
|
+
// 2026-04-26) defeats ritual emission — `nur: ok` no longer counts.
|
|
135
|
+
const COGNITION_BLOCK_RX = /<cognition>([\s\S]*?)<\/cognition>/i;
|
|
136
|
+
const LENS_NAMES = ['nur', 'mizan', 'hikma', 'tafakkur', 'tadabbur', 'ilham', 'wahi', 'firasah'];
|
|
137
|
+
const REQUIRED_LENSES = 4;
|
|
138
|
+
const SUBSTANCE_MIN_CHARS = 20;
|
|
139
|
+
// Placeholder patterns from the gate's own correction message + the
|
|
140
|
+
// COMPACT_CONTINUITY_DOCTRINE template — content matching these is
|
|
141
|
+
// not "thinking," it's a copy-paste of the prompt template.
|
|
142
|
+
const PLACEHOLDER_RX = /^\s*<[^<>]+>\s*$/;
|
|
143
|
+
|
|
144
|
+
// Trivial reads that don't require a cognition block.
|
|
145
|
+
const TRIVIAL_BASH_RX = /^\s*(?:ls|cat|head|tail|grep|find|echo|wc|stat|which|pwd|date|file|du|df|ss|ps)\s/;
|
|
146
|
+
const SHORT_BASH_LIMIT = 30;
|
|
147
|
+
|
|
148
|
+
// V3 doctrine (2026-04-26, Hamza): "why is there an ability to bypass?
|
|
149
|
+
// doesnt that fundamentally void the purpose of the harness?" — yes.
|
|
150
|
+
// Per-command bypass is removed entirely. Every bypass this session
|
|
151
|
+
// was traceable to a gate bug, not a legitimate exception. Compliance
|
|
152
|
+
// is the only path for non-trivial work; the kill-switch env remains
|
|
153
|
+
// as the explicit emergency override. When the gate misfires, fix
|
|
154
|
+
// the gate.
|
|
155
|
+
//
|
|
156
|
+
// (DOCTRINE_BYPASS_RX, findBypassDirective, validateStructuredBypass,
|
|
157
|
+
// REQUIRED_BYPASS_FIELDS, BYPASS_FIELD_MIN_CHARS, BYPASS_HARD_LIMIT_PER_HOUR
|
|
158
|
+
// were removed in this commit. The bypass-rate-counter +
|
|
159
|
+
// readRecentBypassCount stay for historical visibility — past audit-log
|
|
160
|
+
// entries are still useful — but no new bypass entries can be created
|
|
161
|
+
// because the bypass code path is gone.)
|
|
162
|
+
|
|
163
|
+
// Inline command cognition (v2 — added 2026-04-26): cognition
|
|
164
|
+
// embedded in the bash command itself as structured comments.
|
|
165
|
+
// Solves the same-message visibility problem (the transcript-flush
|
|
166
|
+
// race that made same-message cognition invisible to PreToolUse).
|
|
167
|
+
//
|
|
168
|
+
// Two accepted formats:
|
|
169
|
+
// (a) Per-lens lines: `# nur: <substantive thought>\n# mizan: ...`
|
|
170
|
+
// (b) Single-line: `# cognition: nur=<text>; mizan=<text>; ...`
|
|
171
|
+
//
|
|
172
|
+
// Substance check applies (≥SUBSTANCE_MIN_CHARS, not a `<placeholder>`).
|
|
173
|
+
// 4+ substantive lenses inline → gate passes, no transcript scan
|
|
174
|
+
// needed. Cognition becomes a property of THE ACTION, not of some
|
|
175
|
+
// message somewhere — the deepest reading of "cognition before action."
|
|
176
|
+
const INLINE_LENS_LINE_RX_GLOBAL = /^\s*#\s*(nur|mizan|hikma|tafakkur|tadabbur|ilham|wahi|firasah)\s*:\s*(.+)$/gim;
|
|
177
|
+
const INLINE_COGNITION_HEADER_RX = /^\s*#\s*cognition\s*:\s*(.+)$/im;
|
|
178
|
+
|
|
179
|
+
function detectInlineCognition(cmd) {
|
|
180
|
+
const names = [];
|
|
181
|
+
// Per-lens line form
|
|
182
|
+
const lineMatches = [...cmd.matchAll(INLINE_LENS_LINE_RX_GLOBAL)];
|
|
183
|
+
for (const m of lineMatches) {
|
|
184
|
+
const lens = m[1].toLowerCase();
|
|
185
|
+
const content = (m[2] || '').trim();
|
|
186
|
+
if (content.length < SUBSTANCE_MIN_CHARS) continue;
|
|
187
|
+
if (PLACEHOLDER_RX.test(content)) continue;
|
|
188
|
+
if (!names.includes(lens)) names.push(lens);
|
|
189
|
+
}
|
|
190
|
+
// Single-line `# cognition: nur=...; mizan=...` form
|
|
191
|
+
const headerMatch = cmd.match(INLINE_COGNITION_HEADER_RX);
|
|
192
|
+
if (headerMatch) {
|
|
193
|
+
const inlineBody = headerMatch[1];
|
|
194
|
+
for (const lens of LENS_NAMES) {
|
|
195
|
+
const lensRx = new RegExp(`\\b${lens}\\s*=\\s*([^;\\n]+)`, 'i');
|
|
196
|
+
const m = inlineBody.match(lensRx);
|
|
197
|
+
if (!m) continue;
|
|
198
|
+
const content = (m[1] || '').trim();
|
|
199
|
+
if (content.length < SUBSTANCE_MIN_CHARS) continue;
|
|
200
|
+
if (PLACEHOLDER_RX.test(content)) continue;
|
|
201
|
+
if (!names.includes(lens)) names.push(lens);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { count: names.length, names };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Substance-checking lens detection (added 2026-04-26 per Hamza's
|
|
208
|
+
// gate-improvement doctrine: form-only emission must not satisfy
|
|
209
|
+
// the gate). For each lens, look for `<lens>: <content>` and verify
|
|
210
|
+
// content is ≥SUBSTANCE_MIN_CHARS of non-placeholder text. Bare
|
|
211
|
+
// `nur` mentions in prose, or `nur: ok`, no longer count — must be
|
|
212
|
+
// substantive thought.
|
|
213
|
+
function detectCognitionLenses(text) {
|
|
214
|
+
if (!text) return { count: 0, names: [], blockBody: '' };
|
|
215
|
+
const block = text.match(COGNITION_BLOCK_RX);
|
|
216
|
+
const searchSpace = block ? block[1] : text;
|
|
217
|
+
const blockBody = block ? block[1] : '';
|
|
218
|
+
const names = [];
|
|
219
|
+
for (const lens of LENS_NAMES) {
|
|
220
|
+
// Match `lens: <content>` where content extends until the next
|
|
221
|
+
// lens label, the closing </cognition> tag, or a blank-line break.
|
|
222
|
+
const lensRx = new RegExp(
|
|
223
|
+
`\\b${lens}\\s*:\\s*([^\\n]*(?:\\n(?!\\s*(?:${LENS_NAMES.join('|')})\\s*:|<\\/cognition>)[^\\n]*)*)`,
|
|
224
|
+
'i',
|
|
225
|
+
);
|
|
226
|
+
const m = searchSpace.match(lensRx);
|
|
227
|
+
if (!m) continue;
|
|
228
|
+
const content = (m[1] || '').trim();
|
|
229
|
+
if (content.length < SUBSTANCE_MIN_CHARS) continue;
|
|
230
|
+
if (PLACEHOLDER_RX.test(content)) continue;
|
|
231
|
+
names.push(lens);
|
|
232
|
+
}
|
|
233
|
+
return { count: names.length, names, blockBody };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Backwards-compat shim — count-only path used by older callers.
|
|
237
|
+
function countCognitionLenses(text) {
|
|
238
|
+
return detectCognitionLenses(text).count;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Hive cognition-logging v1.2 — fire-and-forget HTTP push to
|
|
242
|
+
// /api/cognition/log so every gate decision joins the corpus.
|
|
243
|
+
// Failures are silent: the local audit log is the durable record;
|
|
244
|
+
// the network push is additive signal for compounding evolution.
|
|
245
|
+
//
|
|
246
|
+
// Per no-timeouts doctrine (feedback_no_timeouts_doctrine.md): no
|
|
247
|
+
// AbortController deadline. Pile-up protection is structural —
|
|
248
|
+
// in-flight counter caps concurrent pushes. Real network errors
|
|
249
|
+
// drive the catch path; slow responses complete naturally.
|
|
250
|
+
const HARNESS_URL = process.env.ARIA_HARNESS_URL || 'http://192.168.4.25:30080';
|
|
251
|
+
const HARNESS_TOKEN = process.env.ARIA_HARNESS_TOKEN || '';
|
|
252
|
+
const LOG_PUSH_DISABLED = process.env.ARIA_COGNITION_PUSH === 'off';
|
|
253
|
+
const MAX_IN_FLIGHT = 16;
|
|
254
|
+
let inFlight = 0;
|
|
255
|
+
|
|
256
|
+
function pushCognitionEvent(payload) {
|
|
257
|
+
if (LOG_PUSH_DISABLED) return;
|
|
258
|
+
if (inFlight >= MAX_IN_FLIGHT) return; // structural backpressure, not a deadline
|
|
259
|
+
try {
|
|
260
|
+
const body = JSON.stringify(payload);
|
|
261
|
+
const url = `${HARNESS_URL}/api/cognition/log`;
|
|
262
|
+
inFlight++;
|
|
263
|
+
fetch(url, {
|
|
264
|
+
method: 'POST',
|
|
265
|
+
headers: {
|
|
266
|
+
'Content-Type': 'application/json',
|
|
267
|
+
...(HARNESS_TOKEN ? { Authorization: `Bearer ${HARNESS_TOKEN}` } : {}),
|
|
268
|
+
},
|
|
269
|
+
body,
|
|
270
|
+
})
|
|
271
|
+
.catch(() => {/* real transport error — silent, audit log is durable */})
|
|
272
|
+
.finally(() => { inFlight--; });
|
|
273
|
+
} catch {
|
|
274
|
+
// pre-fetch errors (malformed JSON, env issue) — silent
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Read event JSON from stdin (Claude Code spec).
|
|
279
|
+
let input = '';
|
|
280
|
+
for await (const chunk of process.stdin) input += chunk;
|
|
281
|
+
|
|
282
|
+
let event;
|
|
283
|
+
try {
|
|
284
|
+
event = JSON.parse(input);
|
|
285
|
+
} catch {
|
|
286
|
+
audit('allow-parse-error', 'stdin not JSON');
|
|
287
|
+
process.exit(0); // fail-open on malformed input
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const toolName = event.tool_name ?? event.toolName ?? '';
|
|
291
|
+
const toolInput = event.tool_input ?? event.toolInput ?? {};
|
|
292
|
+
|
|
293
|
+
// Gate every action tool — every tool that mutates state must go through
|
|
294
|
+
// cognition challenge. Hamza 2026-04-26: "regardless if u arent even
|
|
295
|
+
// compliant with it how do we expect workers to be? i truly nees to
|
|
296
|
+
// ship this and ur non compliance is blocking." Per
|
|
297
|
+
// feedback_full_harness_binding_must_be_structural.md the dispatcher
|
|
298
|
+
// (the orchestrator wielding the tool) owns compliance, not the worker
|
|
299
|
+
// that receives a dispatched task. Read/Glob/Grep observe state without
|
|
300
|
+
// changing it, so they remain ungated.
|
|
301
|
+
const ACTION_TOOLS = new Set(['Bash', 'Edit', 'Write', 'NotebookEdit']);
|
|
302
|
+
if (!ACTION_TOOLS.has(toolName)) {
|
|
303
|
+
process.exit(0);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const cmd = toolName === 'Bash' ? String(toolInput.command ?? '') : '';
|
|
307
|
+
const filePath = toolName !== 'Bash'
|
|
308
|
+
? String(toolInput.file_path ?? toolInput.notebook_path ?? '')
|
|
309
|
+
: '';
|
|
310
|
+
const cmdPreview = toolName === 'Bash'
|
|
311
|
+
? cmd.slice(0, 80).replace(/\s+/g, ' ')
|
|
312
|
+
: `${toolName} ${filePath || '(no path)'}`.slice(0, 80);
|
|
313
|
+
|
|
314
|
+
// V3: per-command bypass removed entirely. The only escape valves are:
|
|
315
|
+
// (1) Kill-switch env ARIA_PRE_TOOL_GATE=off — handled at top of file
|
|
316
|
+
// (2) Trivial-bash whitelist — short read-only commands pass without cognition
|
|
317
|
+
// Compliance is the only path for non-trivial work.
|
|
318
|
+
|
|
319
|
+
// V2 primary path — inline command cognition. If the command carries
|
|
320
|
+
// 4+ substantive lenses inline, gate passes immediately (still need
|
|
321
|
+
// verify block from transcript for destructive ops, see below).
|
|
322
|
+
const inlineCog = detectInlineCognition(cmd);
|
|
323
|
+
if (inlineCog.count >= REQUIRED_LENSES) {
|
|
324
|
+
// For destructive commands, still require a verify block in the
|
|
325
|
+
// recent transcript (verify is fundamentally about pre-action
|
|
326
|
+
// verification of state, not about the command's own thought).
|
|
327
|
+
// We'll do that check below alongside the existing flow but skip
|
|
328
|
+
// the cognition-from-transcript check entirely since we have it
|
|
329
|
+
// inline.
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Destructive-pattern + trivial-read + short-command shortcuts apply
|
|
333
|
+
// only to Bash. Edit / Write / NotebookEdit always require cognition —
|
|
334
|
+
// they have no triviality concept (you don't "trivially" mutate a file)
|
|
335
|
+
// and no command-text to inspect for destructive verbs (the destructive
|
|
336
|
+
// shape is "this Edit might overwrite something important," which is
|
|
337
|
+
// exactly what cognition is for).
|
|
338
|
+
const matched = toolName === 'Bash'
|
|
339
|
+
? DESTRUCTIVE_PATTERNS.find(({ rx }) => rx.test(cmd))
|
|
340
|
+
: null;
|
|
341
|
+
const isTrivialRead = toolName === 'Bash' && TRIVIAL_BASH_RX.test(cmd) && cmd.length < 200;
|
|
342
|
+
const isShort = toolName === 'Bash' && cmd.length < SHORT_BASH_LIMIT;
|
|
343
|
+
|
|
344
|
+
if (!matched && (isTrivialRead || isShort)) {
|
|
345
|
+
// Not destructive AND trivial — allow without further checks. Only
|
|
346
|
+
// reachable for Bash because both flags are forced false otherwise.
|
|
347
|
+
process.exit(0);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Read recent assistant turns for both verify + cognition blocks.
|
|
351
|
+
//
|
|
352
|
+
// Doctrine being implemented: cognition is *turn-scoped*, not
|
|
353
|
+
// message-scoped. A model that emits a <cognition> block once at the
|
|
354
|
+
// top of a turn and then executes 20 tool_use round-trips has done
|
|
355
|
+
// its 8-lens application — gating each individual message would be
|
|
356
|
+
// performative, not enforcement.
|
|
357
|
+
//
|
|
358
|
+
// Algorithm: walk back from the most recent transcript entry,
|
|
359
|
+
// accumulating assistant text. Stop after crossing exactly ONE user
|
|
360
|
+
// message — that captures "everything the model said this turn" plus
|
|
361
|
+
// the immediately-prior assistant turn's tail (so cognition that
|
|
362
|
+
// carried over a turn boundary still counts). Hard cap at 30
|
|
363
|
+
// messages to bound work on huge transcripts.
|
|
364
|
+
//
|
|
365
|
+
// Compact robustness: Claude Code rewrites the transcript with a
|
|
366
|
+
// summary stub as the most recent assistant entry. We recognize and
|
|
367
|
+
// skip those by content heuristic so they don't poison the search.
|
|
368
|
+
const COMPACT_SUMMARY_RX = /(this session is being continued|conversation that ran out of context|primary request and intent|all user messages)/i;
|
|
369
|
+
// System-reminder messages are stored with role=user but are runtime-
|
|
370
|
+
// injected (PreToolUse blocks for any tool, Stop blocks, task-
|
|
371
|
+
// notifications, gentle reminders, harness packet preview chunks).
|
|
372
|
+
// Counting them as turn boundaries evaporated the cognition lookback
|
|
373
|
+
// in client-tonight sessions where the harness packet preview alone
|
|
374
|
+
// is 36k chars per turn — old `length < 4000` skip threshold trapped
|
|
375
|
+
// every reminder as a boundary. Now: PERCENTAGE-OF-CONTENT skip via
|
|
376
|
+
// total reminder-span coverage as a fraction of total content length.
|
|
377
|
+
// ≥60% reminder coverage → skip as runtime-injected.
|
|
378
|
+
const SYSTEM_REMINDER_RX = /<system-reminder>[\s\S]*?<\/system-reminder>|<task-notification>[\s\S]*?<\/task-notification>|🔐 Aria Harness|task-notification|PreToolUse:[A-Z][A-Za-z]* hook blocking error|Stop hook blocking error/g;
|
|
379
|
+
const SYSTEM_REMINDER_THRESHOLD = 0.6;
|
|
380
|
+
const HARD_LOOKBACK_CAP = 50;
|
|
381
|
+
// Bumped from 2 → 5 (2026-04-26): client-tonight session has many
|
|
382
|
+
// system-reminder injections per turn (block errors, task-list
|
|
383
|
+
// reminders, harness packet preview). Tight boundary count plus
|
|
384
|
+
// the broken length-based reminder skip evaporated cognition lookback
|
|
385
|
+
// even when cognition was emitted in the prior assistant text. Per
|
|
386
|
+
// Hamza directive "fix the gate, don't route around it" — widen the
|
|
387
|
+
// window since the substance check defends quality regardless.
|
|
388
|
+
const USER_BOUNDARIES_TO_CROSS = 5;
|
|
389
|
+
|
|
390
|
+
const transcriptPath = event.transcript_path ?? event.transcriptPath;
|
|
391
|
+
const recentAssistantTexts = [];
|
|
392
|
+
if (transcriptPath && existsSync(transcriptPath)) {
|
|
393
|
+
try {
|
|
394
|
+
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
395
|
+
let userBoundariesCrossed = 0;
|
|
396
|
+
let scanned = 0;
|
|
397
|
+
for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
|
|
398
|
+
try {
|
|
399
|
+
const m = JSON.parse(lines[i]);
|
|
400
|
+
const role = m.message?.role ?? m.role;
|
|
401
|
+
if (role === 'user') {
|
|
402
|
+
// Skip messages that aren't real user input:
|
|
403
|
+
// (a) tool_result blocks (runtime feeding back tool output)
|
|
404
|
+
// (b) system-reminder injections (PreToolUse blocks,
|
|
405
|
+
// task-notifications, gentle reminders) — runtime-
|
|
406
|
+
// authored, not user voice. Counting them eats the
|
|
407
|
+
// cognition lookback in tool-heavy or block-heavy turns.
|
|
408
|
+
const content = m.message?.content ?? m.content ?? [];
|
|
409
|
+
const isToolResultOnly =
|
|
410
|
+
Array.isArray(content) &&
|
|
411
|
+
content.length > 0 &&
|
|
412
|
+
content.every((b) => b && b.type === 'tool_result');
|
|
413
|
+
if (isToolResultOnly) continue;
|
|
414
|
+
// Inspect text content for system-reminder patterns.
|
|
415
|
+
const textContent = Array.isArray(content)
|
|
416
|
+
? content.filter((b) => b && b.type === 'text').map((b) => b.text || '').join('\n')
|
|
417
|
+
: (typeof content === 'string' ? content : '');
|
|
418
|
+
if (textContent) {
|
|
419
|
+
// Compute reminder-span coverage as fraction of total
|
|
420
|
+
// content. The /g flag on SYSTEM_REMINDER_RX returns ALL
|
|
421
|
+
// spans; sum their lengths and compare to total.
|
|
422
|
+
const reminderMatches = textContent.match(SYSTEM_REMINDER_RX) || [];
|
|
423
|
+
if (reminderMatches.length > 0) {
|
|
424
|
+
const reminderChars = reminderMatches.reduce((sum, s) => sum + s.length, 0);
|
|
425
|
+
const fraction = reminderChars / Math.max(1, textContent.length);
|
|
426
|
+
if (fraction >= SYSTEM_REMINDER_THRESHOLD) continue;
|
|
427
|
+
// Otherwise (user wrote substantive text alongside the
|
|
428
|
+
// reminder — the non-reminder portion is >40% of content)
|
|
429
|
+
// fall through and count as a real boundary.
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
userBoundariesCrossed++;
|
|
433
|
+
if (userBoundariesCrossed > USER_BOUNDARIES_TO_CROSS) break;
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
if (role !== 'assistant') continue;
|
|
437
|
+
scanned++;
|
|
438
|
+
const content = m.message?.content ?? m.content ?? [];
|
|
439
|
+
if (!Array.isArray(content)) continue;
|
|
440
|
+
const text = content
|
|
441
|
+
.filter((b) => b.type === 'text')
|
|
442
|
+
.map((b) => b.text)
|
|
443
|
+
.join('\n');
|
|
444
|
+
if (!text) continue;
|
|
445
|
+
// Skip compact-summary stubs — they live where assistant turns
|
|
446
|
+
// used to be but are system-authored, not the model's voice.
|
|
447
|
+
if (COMPACT_SUMMARY_RX.test(text) && text.length > 4000) continue;
|
|
448
|
+
recentAssistantTexts.push(text);
|
|
449
|
+
} catch {}
|
|
450
|
+
}
|
|
451
|
+
} catch {}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Detect cognition / verify across the recent assistant window. Lenses
|
|
455
|
+
// from any of the last N turns count; the gate is checking whether
|
|
456
|
+
// cognition has been done recently, not whether it was done in the
|
|
457
|
+
// literal last message (which can be a tool-only turn or a stub).
|
|
458
|
+
const unionText = recentAssistantTexts.join('\n\n');
|
|
459
|
+
const transcriptCog = detectCognitionLenses(unionText);
|
|
460
|
+
// Combine inline-command cognition (preferred — action-coupled) with
|
|
461
|
+
// transcript scan (fallback). Inline takes precedence on lens names;
|
|
462
|
+
// counts merge for the threshold check.
|
|
463
|
+
const mergedLensSet = new Set([...inlineCog.names, ...transcriptCog.names]);
|
|
464
|
+
const lensCount = mergedLensSet.size;
|
|
465
|
+
const lensNames = [...mergedLensSet];
|
|
466
|
+
const cogBlockBody = transcriptCog.blockBody;
|
|
467
|
+
const hasVerify = VERIFY_BLOCK_RX.test(unionText);
|
|
468
|
+
const hasCognition = lensCount >= REQUIRED_LENSES;
|
|
469
|
+
const cognitionSource = inlineCog.count >= REQUIRED_LENSES
|
|
470
|
+
? 'inline-command'
|
|
471
|
+
: (transcriptCog.count >= REQUIRED_LENSES ? 'transcript-scan' : 'merged-or-insufficient');
|
|
472
|
+
|
|
473
|
+
// Best-effort session id for the corpus push. Claude Code passes
|
|
474
|
+
// session_id in the event payload; fall back to transcript file
|
|
475
|
+
// basename so events from the same session cluster.
|
|
476
|
+
const sessionId =
|
|
477
|
+
event.session_id ??
|
|
478
|
+
event.sessionId ??
|
|
479
|
+
(transcriptPath ? transcriptPath.split('/').pop()?.replace(/\.[^.]+$/, '') : null) ??
|
|
480
|
+
'claude-code-unknown';
|
|
481
|
+
|
|
482
|
+
function pushDecision(decision, reasonText) {
|
|
483
|
+
pushCognitionEvent({
|
|
484
|
+
sessionId,
|
|
485
|
+
client: 'claude-code',
|
|
486
|
+
surface: 'pre-tool-gate',
|
|
487
|
+
rawBlock: cogBlockBody ? `<cognition>${cogBlockBody}</cognition>` : null,
|
|
488
|
+
lensesApplied: lensNames,
|
|
489
|
+
blockChars: unionText.length,
|
|
490
|
+
nextActionKind: toolName === 'Bash' ? 'bash' : toolName.toLowerCase(),
|
|
491
|
+
nextActionSummary: cmdPreview,
|
|
492
|
+
gateName: 'aria-pre-tool-gate',
|
|
493
|
+
gateDecision: decision,
|
|
494
|
+
gateReason: reasonText,
|
|
495
|
+
metadata: {
|
|
496
|
+
destructivePattern: matched?.name ?? null,
|
|
497
|
+
hasVerify,
|
|
498
|
+
hasCognition,
|
|
499
|
+
},
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (matched) {
|
|
504
|
+
// Destructive — require BOTH verify (from transcript) AND cognition
|
|
505
|
+
// (inline command preferred; transcript fallback). Verify stays
|
|
506
|
+
// transcript-only because it's about pre-action substrate
|
|
507
|
+
// verification, not about the command's own thought.
|
|
508
|
+
if (hasVerify && hasCognition) {
|
|
509
|
+
audit(`allow-verified-cognition ${matched.name} lenses=${lensCount} via=${cognitionSource}`, cmdPreview);
|
|
510
|
+
pushDecision('allow', `verified+cognition for ${matched.name} (${cognitionSource})`);
|
|
511
|
+
process.exit(0);
|
|
512
|
+
}
|
|
513
|
+
// Block with appropriate corrective message.
|
|
514
|
+
const reason = !hasVerify
|
|
515
|
+
? `Aria pre-tool gate: destructive pattern '${matched.name}' detected. Per harness mizan_prestage_rule + axiom_runtime_rule (admit_ignorance, reflection_before_action), include a <verify> block in your assistant response BEFORE the tool call.
|
|
516
|
+
|
|
517
|
+
<verify>
|
|
518
|
+
target: <exactly what is being changed>
|
|
519
|
+
role: <what that target does — verified, not assumed>
|
|
520
|
+
verified: <how you verified: tool output / file read / DB query / asked user>
|
|
521
|
+
rollback: <exact command to reverse this change>
|
|
522
|
+
axiom: <which harness rule applies>
|
|
523
|
+
</verify>
|
|
524
|
+
|
|
525
|
+
Re-issue after producing the block. Bypass: '# doctrine-authorized: <reason>' inline. Kill: ARIA_PRE_TOOL_GATE=off.`
|
|
526
|
+
: `Aria pre-tool gate: destructive pattern '${matched.name}' has its <verify> block, but the <cognition> block is missing or shows only ${lensCount}/${REQUIRED_LENSES}+ required lenses. Per EIGHT_LENS_DOCTRINE.md every non-trivial action requires visible 8-lens application.
|
|
527
|
+
|
|
528
|
+
<cognition>
|
|
529
|
+
nur: <what you see plainly, specific to this task>
|
|
530
|
+
mizan: <what's out of proportion / risk profile>
|
|
531
|
+
hikma: <what memory or principle applies>
|
|
532
|
+
tafakkur: <deep structural read>
|
|
533
|
+
tadabbur: <if-then chain>
|
|
534
|
+
ilham: <distant connection>
|
|
535
|
+
wahi: <what just landed in this turn>
|
|
536
|
+
firasah: <what user actually needs>
|
|
537
|
+
</cognition>
|
|
538
|
+
|
|
539
|
+
At least 4 substantive lenses required. Bypass: '# doctrine-authorized: <reason>' inline.`;
|
|
540
|
+
audit(`block ${matched.name} verify=${hasVerify} cognition=${lensCount}`, cmdPreview);
|
|
541
|
+
pushDecision('block', `destructive ${matched.name} missing ${!hasVerify ? 'verify' : 'cognition'}`);
|
|
542
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
543
|
+
process.exit(2);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Non-trivial but not destructive — require cognition. For Bash the
|
|
547
|
+
// inline-comment path is preferred (action-coupled). For Edit / Write /
|
|
548
|
+
// NotebookEdit there's no command field, so transcript-scan cognition
|
|
549
|
+
// is the only path.
|
|
550
|
+
if (!hasCognition) {
|
|
551
|
+
const isBash = toolName === 'Bash';
|
|
552
|
+
const header = isBash
|
|
553
|
+
? `Aria pre-tool gate: non-trivial Bash command without ${REQUIRED_LENSES}+ substantive cognition lenses. Found ${lensCount}/${REQUIRED_LENSES}+ (inline=${inlineCog.count}, transcript=${transcriptCog.count}).`
|
|
554
|
+
: `Aria pre-tool gate: ${toolName} call without ${REQUIRED_LENSES}+ substantive cognition lenses in the recent transcript. Found ${lensCount}/${REQUIRED_LENSES}+ (transcript-scan only — ${toolName} has no inline-cognition path).`;
|
|
555
|
+
|
|
556
|
+
const guidance = isBash
|
|
557
|
+
? `PREFERRED (v2 — action-coupled): inline cognition in the command itself —
|
|
558
|
+
# nur: <real perception, ≥${SUBSTANCE_MIN_CHARS} chars, no <placeholder>>
|
|
559
|
+
# mizan: <real risk read>
|
|
560
|
+
# hikma: <doctrine that applies>
|
|
561
|
+
# tafakkur: <deep structural read>
|
|
562
|
+
<your actual command here>
|
|
563
|
+
|
|
564
|
+
This solves the same-message visibility problem and couples cognition to THIS specific action.
|
|
565
|
+
|
|
566
|
+
FALLBACK: emit a <cognition>...</cognition> block in your assistant text with ${REQUIRED_LENSES}+ substantive lenses. Substance check applies: each lens must have ≥${SUBSTANCE_MIN_CHARS} chars of non-placeholder content.`
|
|
567
|
+
: `Emit a <cognition>...</cognition> block in your assistant text BEFORE this ${toolName} call, with ${REQUIRED_LENSES}+ substantive lenses. Substance check: each lens must have ≥${SUBSTANCE_MIN_CHARS} chars of non-placeholder content. Cognition is turn-scoped — one block at the start of a turn covers all ${toolName} calls in that turn.
|
|
568
|
+
|
|
569
|
+
<cognition>
|
|
570
|
+
nur: <what you see plainly, specific to this edit>
|
|
571
|
+
mizan: <what's out of proportion — what could this overwrite or break?>
|
|
572
|
+
hikma: <doctrine that applies (name the source)>
|
|
573
|
+
tafakkur: <deep structural read>
|
|
574
|
+
tadabbur: <if-then chain — what follows from this edit>
|
|
575
|
+
ilham: <distant connection a less-careful editor would miss>
|
|
576
|
+
wahi: <what just landed in this turn that justifies this edit>
|
|
577
|
+
firasah: <what user actually needs underneath>
|
|
578
|
+
</cognition>`;
|
|
579
|
+
|
|
580
|
+
const reason = `${header}
|
|
581
|
+
|
|
582
|
+
${guidance}
|
|
583
|
+
|
|
584
|
+
No per-tool bypass available (v3 doctrine — the harness's whole purpose is no exceptions). Kill-switch: ARIA_PRE_TOOL_GATE=off (logged, emergency only). If the gate misfires on legitimate cognition, fix the gate.`;
|
|
585
|
+
|
|
586
|
+
audit(`block ${toolName.toLowerCase()} cognition=${lensCount}`, cmdPreview);
|
|
587
|
+
pushDecision('block', `${toolName.toLowerCase()} missing cognition (${lensCount}/${REQUIRED_LENSES})`);
|
|
588
|
+
console.log(JSON.stringify({ decision: 'block', reason }));
|
|
589
|
+
process.exit(2);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Non-trivial action with cognition (inline for Bash, transcript for
|
|
593
|
+
// Edit/Write/NotebookEdit) — allow.
|
|
594
|
+
audit(`allow-cognition ${toolName.toLowerCase()} lenses=${lensCount} via=${cognitionSource}`, cmdPreview);
|
|
595
|
+
pushDecision('allow', `${toolName.toLowerCase()} with ${lensCount} lenses (${cognitionSource})`);
|
|
596
|
+
process.exit(0);
|