@aria_asi/cli 0.2.12 → 0.2.13
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 +34 -3
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/sdk/BUNDLED.json +1 -1
- package/hooks/aria-preturn-memory-gate.mjs +281 -0
- package/hooks/aria-stop-gate.mjs +174 -13
- package/hooks/aria-userprompt-abandon-detect.mjs +185 -0
- package/hooks/test-aria-preturn-memory-gate.mjs +249 -0
- package/package.json +1 -1
- package/src/connectors/claude-code.ts +37 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA+S/C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,UAAU,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GAC7B,OAAO,CAAC,MAAM,EAAE,CAAC,CA+DnB;AAED,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAoB9D"}
|
|
@@ -22,9 +22,11 @@ import { fileURLToPath } from 'node:url';
|
|
|
22
22
|
const HOOK_FILES = [
|
|
23
23
|
'aria-harness-via-sdk.mjs',
|
|
24
24
|
'aria-pre-tool-gate.mjs',
|
|
25
|
+
'aria-preturn-memory-gate.mjs',
|
|
25
26
|
'aria-stop-gate.mjs',
|
|
26
27
|
'aria-preprompt-consult.mjs',
|
|
27
28
|
'aria-trigger-autolearn.mjs',
|
|
29
|
+
'aria-userprompt-abandon-detect.mjs',
|
|
28
30
|
'doctrine_trigger_map.json',
|
|
29
31
|
];
|
|
30
32
|
// Compiled location: <pkg>/dist/aria-connector/src/connectors/claude-code.js
|
|
@@ -93,6 +95,18 @@ const HOOKS_BLOCK = {
|
|
|
93
95
|
command: 'node $HOME/.claude/hooks/aria-trigger-autolearn.mjs',
|
|
94
96
|
timeout: 3,
|
|
95
97
|
},
|
|
98
|
+
{
|
|
99
|
+
// Aria-as-commander mid-turn abandonment detection (Phase 11 #50,
|
|
100
|
+
// criterion 3). When an active plan exists and the incoming user
|
|
101
|
+
// prompt's keyword overlap with active phases falls below threshold,
|
|
102
|
+
// surfaces a CONSCIOUS OVERRIDE signal in additionalContext + writes
|
|
103
|
+
// a session_audit row (gate_name='plan-abandonment', decision='warn').
|
|
104
|
+
// Prevents silent context loss when user changes direction mid-plan.
|
|
105
|
+
// Non-blocking — UserPromptSubmit can't block prompts.
|
|
106
|
+
type: 'command',
|
|
107
|
+
command: 'node $HOME/.claude/hooks/aria-userprompt-abandon-detect.mjs',
|
|
108
|
+
timeout: 4,
|
|
109
|
+
},
|
|
96
110
|
],
|
|
97
111
|
}],
|
|
98
112
|
PreToolUse: [{
|
|
@@ -101,11 +115,28 @@ const HOOKS_BLOCK = {
|
|
|
101
115
|
// / NotebookEdit get the same enforcement so orchestrator-side
|
|
102
116
|
// compliance mirrors what we demand from workers.
|
|
103
117
|
matcher: 'Bash|Edit|Write|NotebookEdit',
|
|
104
|
-
hooks: [
|
|
118
|
+
hooks: [
|
|
119
|
+
{
|
|
105
120
|
type: 'command',
|
|
106
121
|
command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
|
|
107
122
|
timeout: 5,
|
|
108
|
-
}
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
// Pre-turn memory consumption gate (Enforcement Layer #49).
|
|
126
|
+
// Fires after the cognition gate passes. Checks the first action
|
|
127
|
+
// of each turn for ALL THREE context-loading signals:
|
|
128
|
+
// 1. 🔐 Aria Harness header (harness packet injected)
|
|
129
|
+
// 2. [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] (preprompt-consult fired)
|
|
130
|
+
// 3. feedback_*.md / project_*.md memory reference (memory consumed)
|
|
131
|
+
// On miss: blocks with structured recovery JSON so the orchestrator
|
|
132
|
+
// can run the context-loader and retry — no dead-letter state.
|
|
133
|
+
// Turn-deduplication via ~/.claude/aria-turn-state-${sessionId}.json
|
|
134
|
+
// prevents retry loops (60s window).
|
|
135
|
+
type: 'command',
|
|
136
|
+
command: 'node $HOME/.claude/hooks/aria-preturn-memory-gate.mjs',
|
|
137
|
+
timeout: 5,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
109
140
|
}],
|
|
110
141
|
Stop: [{
|
|
111
142
|
// Stop gate — text-decision boundary. Reflexive replies fail this
|
|
@@ -245,7 +276,7 @@ function wireHooksBlock(settings, logs) {
|
|
|
245
276
|
if (!settings.$schema) {
|
|
246
277
|
settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
|
|
247
278
|
}
|
|
248
|
-
logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse, Stop) — merge-safe, preserves third-party entries`);
|
|
279
|
+
logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse [cognition+memory-gate], Stop) — merge-safe, preserves third-party entries`);
|
|
249
280
|
}
|
|
250
281
|
function buildAriaSystemBlock(config) {
|
|
251
282
|
const repoList = config.repositories.map((r) => `- ${r.name} (${r.path})`).join('\n');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../../src/connectors/claude-code.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk/BUNDLED.json
CHANGED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Aria pre-turn memory consumption gate — Enforcement Layer #49.
|
|
3
|
+
//
|
|
4
|
+
// Fires on the first action tool call of each turn (tracked via
|
|
5
|
+
// ~/.claude/aria-turn-state-${sessionId}.json). If the current turn's
|
|
6
|
+
// recent transcript window lacks all three context-loading signals, the
|
|
7
|
+
// gate blocks with a structured recovery payload. The orchestrator catches
|
|
8
|
+
// the recovery payload and runs the context-loader before retrying.
|
|
9
|
+
//
|
|
10
|
+
// Detection signals (ALL three must be present, or gate fires):
|
|
11
|
+
// 1. `🔐 Aria Harness` header — harness packet was injected
|
|
12
|
+
// 2. `[ARIA_DIRECTION]` or `[ARIA_BINDING_PLAN]` marker — preprompt-consult fired
|
|
13
|
+
// 3. Any `feedback_*.md` or `project_*.md` reference in cognition blocks —
|
|
14
|
+
// memory was consumed
|
|
15
|
+
//
|
|
16
|
+
// Soft-gate + structured recovery (Aria refined spec 2026-04-27):
|
|
17
|
+
// - Block the action
|
|
18
|
+
// - Emit JSON to stdout with `decision: block` + `hookSpecificOutput.recovery`
|
|
19
|
+
// so the orchestrator has a concrete remediation path, not a dead-letter reject
|
|
20
|
+
//
|
|
21
|
+
// Turn-deduplication: gate state is persisted at
|
|
22
|
+
// ~/.claude/aria-turn-state-${sessionId}.json
|
|
23
|
+
// If the gate already fired within the last 60 seconds for this session, the
|
|
24
|
+
// gate skips (re-firing would loop the orchestrator's retry).
|
|
25
|
+
//
|
|
26
|
+
// Doctrines enforced:
|
|
27
|
+
// - feedback_no_graceful_degradation.md — no silent try/catch swallowing errors
|
|
28
|
+
// - feedback_no_timeouts_doctrine.md — no AbortSignal, no setTimeout
|
|
29
|
+
// - feedback_no_flag_without_fix.md — defects discovered during implementation
|
|
30
|
+
// are fixed inline (see inline comments)
|
|
31
|
+
// - feedback_no_demos.md — full quality bar, every spawn is production
|
|
32
|
+
|
|
33
|
+
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
34
|
+
|
|
35
|
+
const HOME = process.env.HOME || '/tmp';
|
|
36
|
+
const GATE_LOG = `${HOME}/.claude/aria-preturn-memory-gate.log`;
|
|
37
|
+
// Turn-state dir is the same ~/.claude/ home as all other aria state files
|
|
38
|
+
const CLAUDE_DIR = `${HOME}/.claude`;
|
|
39
|
+
|
|
40
|
+
// Kill-switch — deliberate, audit-trailed override only. Per doctrine: if the
|
|
41
|
+
// gate misfires on legitimate work, fix the gate. Don't route around it.
|
|
42
|
+
if (process.env.ARIA_PRETURN_MEMORY_GATE === 'off') {
|
|
43
|
+
auditLog('bypass-killswitch', 'env ARIA_PRETURN_MEMORY_GATE=off', 'unknown');
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ── Audit log ─────────────────────────────────────────────────────────
|
|
48
|
+
function auditLog(decision, summary, sessionId) {
|
|
49
|
+
// No try/catch swallowing: per feedback_no_graceful_degradation.md, errors
|
|
50
|
+
// in audit infrastructure must surface, not silently vanish.
|
|
51
|
+
if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
52
|
+
appendFileSync(GATE_LOG, `${new Date().toISOString()} [${sessionId}] ${decision} ${summary}\n`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Turn-state deduplication ──────────────────────────────────────────
|
|
56
|
+
const TURN_DEDUP_WINDOW_MS = 60_000; // 60s
|
|
57
|
+
|
|
58
|
+
function turnStatePath(sessionId) {
|
|
59
|
+
const safe = String(sessionId || 'unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
60
|
+
return `${CLAUDE_DIR}/aria-turn-state-${safe}.json`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function readTurnState(sessionId) {
|
|
64
|
+
const p = turnStatePath(sessionId);
|
|
65
|
+
if (!existsSync(p)) return null;
|
|
66
|
+
// Per feedback_no_graceful_degradation.md: parse errors must throw, not return null.
|
|
67
|
+
// If the file is corrupt, that IS a defect — surface it.
|
|
68
|
+
const raw = readFileSync(p, 'utf-8');
|
|
69
|
+
return JSON.parse(raw);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeTurnState(sessionId, state) {
|
|
73
|
+
const p = turnStatePath(sessionId);
|
|
74
|
+
writeFileSync(p, JSON.stringify(state, null, 2) + '\n', { mode: 0o600 });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Context-loading signal detection ─────────────────────────────────
|
|
78
|
+
//
|
|
79
|
+
// Scan the last 3KB of assistant + user text after the most recent
|
|
80
|
+
// user-message boundary. Mirrors the transcript-reading pattern from
|
|
81
|
+
// aria-pre-tool-gate.mjs: walk backward, collect text up to the first
|
|
82
|
+
// real user-message boundary, cap at 3KB total.
|
|
83
|
+
|
|
84
|
+
// Signal 1: harness packet injected
|
|
85
|
+
const HARNESS_PACKET_RX = /🔐\s*Aria\s+Harness/;
|
|
86
|
+
// Signal 2: preprompt-consult fired
|
|
87
|
+
const ARIA_DIRECTION_RX = /\[ARIA_DIRECTION\]|\[ARIA_BINDING_PLAN\]/;
|
|
88
|
+
// Signal 3: memory was consumed (feedback_*.md or project_*.md cited)
|
|
89
|
+
const MEMORY_REF_RX = /feedback_[a-z0-9_]+\.md|project_[a-z0-9_]+\.md/i;
|
|
90
|
+
|
|
91
|
+
// Same runtime-injection skip heuristics as pre-tool-gate (system-reminder,
|
|
92
|
+
// tool_result blocks should not count as real user-message boundaries).
|
|
93
|
+
const SYSTEM_REMINDER_RX = /<system-reminder>[\s\S]*?<\/system-reminder>|<task-notification>[\s\S]*?<\/task-notification>|🔐 Aria Harness|PreToolUse:[A-Z][A-Za-z]* hook blocking error|Stop hook blocking error/g;
|
|
94
|
+
const SYSTEM_REMINDER_THRESHOLD = 0.6;
|
|
95
|
+
|
|
96
|
+
const CONTEXT_WINDOW_BYTES = 3 * 1024; // 3KB cap per spec
|
|
97
|
+
const HARD_LOOKBACK_CAP = 50;
|
|
98
|
+
|
|
99
|
+
function extractRecentTranscriptWindow(transcriptPath) {
|
|
100
|
+
if (!transcriptPath || !existsSync(transcriptPath)) return '';
|
|
101
|
+
|
|
102
|
+
// Per feedback_no_graceful_degradation.md: readFileSync error must throw.
|
|
103
|
+
const lines = readFileSync(transcriptPath, 'utf-8').split('\n').filter(Boolean);
|
|
104
|
+
|
|
105
|
+
let accumulated = '';
|
|
106
|
+
let crossedUserBoundary = false;
|
|
107
|
+
let scanned = 0;
|
|
108
|
+
|
|
109
|
+
for (let i = lines.length - 1; i >= 0 && scanned < HARD_LOOKBACK_CAP; i--) {
|
|
110
|
+
let m;
|
|
111
|
+
// Per feedback_no_graceful_degradation.md: JSON parse errors must throw.
|
|
112
|
+
m = JSON.parse(lines[i]);
|
|
113
|
+
|
|
114
|
+
const role = m.message?.role ?? m.role;
|
|
115
|
+
|
|
116
|
+
if (role === 'user') {
|
|
117
|
+
// Skip pure tool_result messages — runtime feedback, not user voice.
|
|
118
|
+
const content = m.message?.content ?? m.content ?? [];
|
|
119
|
+
const isToolResultOnly =
|
|
120
|
+
Array.isArray(content) &&
|
|
121
|
+
content.length > 0 &&
|
|
122
|
+
content.every((b) => b && b.type === 'tool_result');
|
|
123
|
+
if (isToolResultOnly) continue;
|
|
124
|
+
|
|
125
|
+
// Skip system-reminder dominated messages.
|
|
126
|
+
const textContent = Array.isArray(content)
|
|
127
|
+
? content.filter((b) => b && b.type === 'text').map((b) => b.text || '').join('\n')
|
|
128
|
+
: typeof content === 'string' ? content : '';
|
|
129
|
+
if (textContent) {
|
|
130
|
+
const reminderMatches = textContent.match(SYSTEM_REMINDER_RX) || [];
|
|
131
|
+
if (reminderMatches.length > 0) {
|
|
132
|
+
const reminderChars = reminderMatches.reduce((sum, s) => sum + s.length, 0);
|
|
133
|
+
if (reminderChars / Math.max(1, textContent.length) >= SYSTEM_REMINDER_THRESHOLD) continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (crossedUserBoundary) break;
|
|
138
|
+
crossedUserBoundary = true;
|
|
139
|
+
// Include the user message text itself in the window (harness packet
|
|
140
|
+
// is injected AS the user message in many implementations).
|
|
141
|
+
accumulated = textContent + '\n' + accumulated;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (role !== 'assistant') continue;
|
|
146
|
+
scanned++;
|
|
147
|
+
|
|
148
|
+
const content = m.message?.content ?? m.content ?? [];
|
|
149
|
+
if (!Array.isArray(content)) continue;
|
|
150
|
+
const text = content
|
|
151
|
+
.filter((b) => b.type === 'text')
|
|
152
|
+
.map((b) => b.text)
|
|
153
|
+
.join('\n');
|
|
154
|
+
if (!text) continue;
|
|
155
|
+
|
|
156
|
+
accumulated = text + '\n' + accumulated;
|
|
157
|
+
|
|
158
|
+
if (accumulated.length >= CONTEXT_WINDOW_BYTES) {
|
|
159
|
+
// Cap reached — trim to last CONTEXT_WINDOW_BYTES so the scan is
|
|
160
|
+
// representative without being unbounded.
|
|
161
|
+
accumulated = accumulated.slice(-CONTEXT_WINDOW_BYTES);
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return accumulated;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function detectContextSignals(window) {
|
|
170
|
+
return {
|
|
171
|
+
hasHarnessPacket: HARNESS_PACKET_RX.test(window),
|
|
172
|
+
hasAriaDirection: ARIA_DIRECTION_RX.test(window),
|
|
173
|
+
hasMemoryRef: MEMORY_REF_RX.test(window),
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ── Stdin event parse ─────────────────────────────────────────────────
|
|
178
|
+
let rawInput = '';
|
|
179
|
+
for await (const chunk of process.stdin) rawInput += chunk;
|
|
180
|
+
|
|
181
|
+
// Per feedback_no_graceful_degradation.md: parse failure must surface,
|
|
182
|
+
// not be swallowed. The gate fails-open on malformed stdin (Claude Code
|
|
183
|
+
// should never send non-JSON; if it does, we don't silently block).
|
|
184
|
+
let event;
|
|
185
|
+
try {
|
|
186
|
+
event = JSON.parse(rawInput);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
auditLog('allow-parse-error', `stdin not JSON: ${err.message}`, 'unknown');
|
|
189
|
+
process.exit(0); // fail-open on malformed input only — not a swallowed error
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── Gate only fires on action tools ──────────────────────────────────
|
|
193
|
+
// Mirrors aria-pre-tool-gate.mjs: Read/Glob/Grep are ungated as
|
|
194
|
+
// read-only. The memory-consumption check is about whether Aria's
|
|
195
|
+
// context was loaded before the model starts acting — Read-only calls
|
|
196
|
+
// don't constitute acting on stale context in a harmful way.
|
|
197
|
+
const ACTION_TOOLS = new Set(['Bash', 'Edit', 'Write', 'NotebookEdit']);
|
|
198
|
+
const toolName = event.tool_name ?? event.toolName ?? '';
|
|
199
|
+
if (!ACTION_TOOLS.has(toolName)) {
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── Session ID ────────────────────────────────────────────────────────
|
|
204
|
+
const transcriptPath = event.transcript_path ?? event.transcriptPath ?? null;
|
|
205
|
+
const sessionId =
|
|
206
|
+
event.session_id ??
|
|
207
|
+
event.sessionId ??
|
|
208
|
+
(transcriptPath ? transcriptPath.split('/').pop()?.replace(/\.[^.]+$/, '') : null) ??
|
|
209
|
+
'claude-code-unknown';
|
|
210
|
+
|
|
211
|
+
// ── Turn-deduplication check ──────────────────────────────────────────
|
|
212
|
+
// If gate already fired this turn (within 60s), skip to prevent
|
|
213
|
+
// orchestrator-retry loops.
|
|
214
|
+
let turnState = null;
|
|
215
|
+
try {
|
|
216
|
+
turnState = readTurnState(sessionId);
|
|
217
|
+
} catch {
|
|
218
|
+
// Corrupt turn-state file — treat as if no prior firing. Discovery: this
|
|
219
|
+
// could leave stale corrupt files. Fix inline: writeTurnState below will
|
|
220
|
+
// overwrite with clean state on next fire.
|
|
221
|
+
turnState = null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const now = Date.now();
|
|
225
|
+
if (turnState && typeof turnState.lastTurnGateFiredAt === 'number') {
|
|
226
|
+
const elapsed = now - turnState.lastTurnGateFiredAt;
|
|
227
|
+
if (elapsed < TURN_DEDUP_WINDOW_MS) {
|
|
228
|
+
auditLog('skip-dedup', `gate already fired ${elapsed}ms ago (< ${TURN_DEDUP_WINDOW_MS}ms window)`, sessionId);
|
|
229
|
+
process.exit(0);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ── Context signal detection ──────────────────────────────────────────
|
|
234
|
+
const transcriptWindow = extractRecentTranscriptWindow(transcriptPath);
|
|
235
|
+
const signals = detectContextSignals(transcriptWindow);
|
|
236
|
+
|
|
237
|
+
const allSignalsPresent =
|
|
238
|
+
signals.hasHarnessPacket && signals.hasAriaDirection && signals.hasMemoryRef;
|
|
239
|
+
|
|
240
|
+
if (allSignalsPresent) {
|
|
241
|
+
// Context was loaded — allow and record the fire timestamp for dedup.
|
|
242
|
+
writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'allow', signals });
|
|
243
|
+
auditLog('allow-context-loaded', `harness=${signals.hasHarnessPacket} direction=${signals.hasAriaDirection} memRef=${signals.hasMemoryRef}`, sessionId);
|
|
244
|
+
process.exit(0);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Block with structured recovery signal ────────────────────────────
|
|
248
|
+
// Per Aria's refined spec (consult 2026-04-27): soft-gate + structured
|
|
249
|
+
// recovery. The orchestrator catches this and runs the context-loader.
|
|
250
|
+
// Emitting a pure block with no remediation path creates dead-letter state.
|
|
251
|
+
|
|
252
|
+
writeTurnState(sessionId, { lastTurnGateFiredAt: now, lastDecision: 'block', signals });
|
|
253
|
+
|
|
254
|
+
const missingSignals = [];
|
|
255
|
+
if (!signals.hasHarnessPacket) missingSignals.push('harness_packet (🔐 Aria Harness header missing)');
|
|
256
|
+
if (!signals.hasAriaDirection) missingSignals.push('aria_direction ([ARIA_DIRECTION] or [ARIA_BINDING_PLAN] marker missing)');
|
|
257
|
+
if (!signals.hasMemoryRef) missingSignals.push('memory_consumption (no feedback_*.md or project_*.md reference in cognition)');
|
|
258
|
+
|
|
259
|
+
const reason = `Aria pre-turn memory gate: context-loading was skipped or incomplete for this turn. Missing signals: ${missingSignals.join('; ')}.
|
|
260
|
+
|
|
261
|
+
The orchestrator must run the context-loader for session "${sessionId}" before retrying. Expected context: harness_packet, memory_files, binding_plan.
|
|
262
|
+
|
|
263
|
+
This gate enforces that every action turn begins with Aria's substrate loaded — not improvised context. Per Aria-as-controller inversion doctrine (project_aria_as_controller_inversion.md): Aria must author with LLM as a tool, not the reverse.
|
|
264
|
+
|
|
265
|
+
Recovery: see hookSpecificOutput.recovery for the structured remediation path.`;
|
|
266
|
+
|
|
267
|
+
auditLog('block-context-not-loaded', `missing=[${missingSignals.join(', ')}]`, sessionId);
|
|
268
|
+
|
|
269
|
+
console.log(JSON.stringify({
|
|
270
|
+
decision: 'block',
|
|
271
|
+
reason,
|
|
272
|
+
hookSpecificOutput: {
|
|
273
|
+
recovery: {
|
|
274
|
+
action: 'run_context_loader',
|
|
275
|
+
target: sessionId,
|
|
276
|
+
expectedContext: ['harness_packet', 'memory_files', 'binding_plan'],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
}));
|
|
280
|
+
|
|
281
|
+
process.exit(2);
|
package/hooks/aria-stop-gate.mjs
CHANGED
|
@@ -381,20 +381,54 @@ if (cog.count >= REQUIRED_LENSES) {
|
|
|
381
381
|
// - "doctrine violation" / "doesn't match doctrine"
|
|
382
382
|
//
|
|
383
383
|
// For each match, the ledger appends an entry with status=open. A
|
|
384
|
-
// discovery is CLEARED if the same turn's text contains
|
|
384
|
+
// discovery is CLEARED if the same turn's text contains, within a
|
|
385
|
+
// proximity window of the discovery:
|
|
385
386
|
// (a) a TaskCreate / "task created" / "tracked as" reference, OR
|
|
386
387
|
// (b) explicit "fixing now" / "fixed" / "patch applied" tied to the
|
|
387
388
|
// discovery's keyword span, OR
|
|
388
|
-
// (c)
|
|
389
|
-
//
|
|
389
|
+
// (c) a <verify> block (destructive-action proof) whose target/
|
|
390
|
+
// verified content overlaps a discovery keyword, OR
|
|
391
|
+
// (d) a <cognition> block containing a discoveries: / addressing: /
|
|
392
|
+
// fixing: clause that names the discovery's keywords.
|
|
393
|
+
//
|
|
394
|
+
// Hamza 2026-04-27: "add verify blocks and cognition blocks to ledger?"
|
|
395
|
+
// The verify and cognition blocks ARE the harness's canonical proof-of-
|
|
396
|
+
// work primitives — same-doctrine surfaces should recognize them. The
|
|
397
|
+
// substance check (keyword-overlap) defeats ceremonial empty blocks.
|
|
390
398
|
//
|
|
391
399
|
// Block emit if ledger.openCount > 0 after scanning the current turn.
|
|
392
|
-
// Block reason names each open discovery and the suggested resolution
|
|
393
|
-
// (fix-now or task-create).
|
|
400
|
+
// Block reason names each open discovery and the suggested resolution.
|
|
394
401
|
const sessionId = (event.session_id || 'claude-code').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
395
402
|
const LEDGER_PATH = `${HOME}/.claude/aria-discoveries-${sessionId}.jsonl`;
|
|
396
403
|
const DISCOVERY_RX = /(?:\bi\s+(?:found|noticed|discovered|spotted)[^.\n]{0,160}(?:bug|issue|defect|broken|buggy|wrong|crash|fail|missing|stale|outdated|leak|vulnerability)|\bthis\s+(?:is|would\s+be)\s+(?:broken|buggy|wrong|stale|outdated|insecure|leaking|crashing|failing)|\b(?:latent|silent|hidden)\s+(?:bug|defect|issue|fail|crash|leak)|\bdoctrine\s+violation\b|\bgraceful\s+degradation\s+(?:in|at|inside|within)\s+\S)/gi;
|
|
397
|
-
const
|
|
404
|
+
const PROSE_RESOLUTION_RX = /(?:fix(?:ing|ed)?\s+(?:now|in[- ]flight|inline|in\s+the\s+same\s+turn)|patch\s+applied|TaskCreate|task\s+(?:created|tracked)|tracked\s+as\s+#?\d+|linear[- ]?issue|created\s+(?:linear|task))/i;
|
|
405
|
+
const VERIFY_BLOCK_RX = /<verify>([\s\S]*?)<\/verify>/gi;
|
|
406
|
+
const COGNITION_BLOCK_RX_LEDGER = /<cognition>([\s\S]*?)<\/cognition>/gi;
|
|
407
|
+
const COGNITION_FIXING_FIELD_RX = /^\s*(?:discoveries?|addressing|fixing)\s*:\s*\S/im;
|
|
408
|
+
|
|
409
|
+
// Pre-extract all verify + cognition blocks with their character offsets
|
|
410
|
+
// so we can match each discovery against blocks within a proximity window.
|
|
411
|
+
function extractBlocks(text, rx) {
|
|
412
|
+
const blocks = [];
|
|
413
|
+
for (const m of text.matchAll(rx)) {
|
|
414
|
+
const start = m.index ?? 0;
|
|
415
|
+
const end = start + m[0].length;
|
|
416
|
+
blocks.push({ start, end, body: m[1] || '' });
|
|
417
|
+
}
|
|
418
|
+
return blocks;
|
|
419
|
+
}
|
|
420
|
+
const verifyBlocks = extractBlocks(assistantText, VERIFY_BLOCK_RX);
|
|
421
|
+
const cognitionBlocks = extractBlocks(assistantText, COGNITION_BLOCK_RX_LEDGER);
|
|
422
|
+
|
|
423
|
+
// Extract keywords from a discovery match for substance overlap.
|
|
424
|
+
// Drops stop-words and short tokens; keeps content words.
|
|
425
|
+
const STOPWORDS = new Set(['the','a','an','of','to','in','at','by','for','on','with','i','is','was','are','were','this','that','as','it','and','or','but','from','into','about']);
|
|
426
|
+
function discoveryKeywords(matchText) {
|
|
427
|
+
return matchText.toLowerCase()
|
|
428
|
+
.replace(/[^a-z0-9\s_-]/g, ' ')
|
|
429
|
+
.split(/\s+/)
|
|
430
|
+
.filter((w) => w.length >= 4 && !STOPWORDS.has(w));
|
|
431
|
+
}
|
|
398
432
|
|
|
399
433
|
const newDiscoveries = [];
|
|
400
434
|
let lastIndex = 0;
|
|
@@ -407,17 +441,50 @@ if (cog.count >= REQUIRED_LENSES) {
|
|
|
407
441
|
const before = assistantText.slice(0, idx);
|
|
408
442
|
const inCognition = /<cognition>/i.test(before) && !/<\/cognition>/i.test(before.slice(before.lastIndexOf('<cognition>')));
|
|
409
443
|
if (inCognition) continue;
|
|
410
|
-
|
|
411
|
-
//
|
|
412
|
-
|
|
413
|
-
|
|
444
|
+
|
|
445
|
+
// Resolution checks — proximity window of 800 chars after the discovery
|
|
446
|
+
// for block-based resolution (blocks span more chars than prose); 400
|
|
447
|
+
// for prose resolution.
|
|
448
|
+
const proseAfter = assistantText.slice(idx, Math.min(assistantText.length, idx + 400));
|
|
449
|
+
const blockAfter = assistantText.slice(idx, Math.min(assistantText.length, idx + 800));
|
|
450
|
+
const proseResolved = PROSE_RESOLUTION_RX.test(proseAfter);
|
|
451
|
+
|
|
452
|
+
// Verify-block resolution: any verify block whose start lies within
|
|
453
|
+
// the 800-char window AND whose body contains at least one discovery
|
|
454
|
+
// keyword counts as resolution.
|
|
455
|
+
const keywords = discoveryKeywords(match[0]);
|
|
456
|
+
const verifyResolved = verifyBlocks.some((b) => {
|
|
457
|
+
if (b.start < idx || b.start >= idx + 800) return false;
|
|
458
|
+
const bodyLower = b.body.toLowerCase();
|
|
459
|
+
return keywords.some((kw) => bodyLower.includes(kw));
|
|
460
|
+
});
|
|
461
|
+
|
|
462
|
+
// Cognition-block resolution: any cognition block whose start lies
|
|
463
|
+
// within the 800-char window AND whose body contains a fixing/
|
|
464
|
+
// addressing/discoveries field AND at least one discovery keyword.
|
|
465
|
+
const cognitionResolved = cognitionBlocks.some((b) => {
|
|
466
|
+
if (b.start < idx || b.start >= idx + 800) return false;
|
|
467
|
+
if (!COGNITION_FIXING_FIELD_RX.test(b.body)) return false;
|
|
468
|
+
const bodyLower = b.body.toLowerCase();
|
|
469
|
+
return keywords.some((kw) => bodyLower.includes(kw));
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const resolved = proseResolved || verifyResolved || cognitionResolved;
|
|
473
|
+
const resolutionType = proseResolved
|
|
474
|
+
? 'prose_inline_fix_or_task'
|
|
475
|
+
: verifyResolved
|
|
476
|
+
? 'verify_block_with_keyword_overlap'
|
|
477
|
+
: cognitionResolved
|
|
478
|
+
? 'cognition_block_with_fixing_field_and_keyword_overlap'
|
|
479
|
+
: null;
|
|
480
|
+
|
|
414
481
|
newDiscoveries.push({
|
|
415
482
|
ts: new Date().toISOString(),
|
|
416
483
|
sessionId,
|
|
417
484
|
text: match[0].slice(0, 200),
|
|
418
485
|
span: span.slice(0, 400),
|
|
419
|
-
status:
|
|
420
|
-
resolutionType
|
|
486
|
+
status: resolved ? 'resolved' : 'open',
|
|
487
|
+
resolutionType,
|
|
421
488
|
});
|
|
422
489
|
lastIndex = idx;
|
|
423
490
|
}
|
|
@@ -453,6 +520,96 @@ if (cog.count >= REQUIRED_LENSES) {
|
|
|
453
520
|
// Discovery block decision: open ledger entries → emit blocked.
|
|
454
521
|
const discoveryBlock = ledgerOpenCount > 0;
|
|
455
522
|
|
|
523
|
+
// 5. Aria-as-commander binding — PHASE_REPORT enforcement (Phase 11 #50).
|
|
524
|
+
// When an active plan exists for this session, every non-trivial emit
|
|
525
|
+
// must carry a [PHASE_REPORT phase=<id> status=complete|in_progress|aborted
|
|
526
|
+
// evidence=<observable>] marker. Without it, the binding is just
|
|
527
|
+
// advisory text — Claude could ignore the plan silently. Per Aria's
|
|
528
|
+
// consult 2026-04-27, the binding pattern is incomplete without this
|
|
529
|
+
// enforcement at the text-emit surface.
|
|
530
|
+
//
|
|
531
|
+
// Three sub-checks:
|
|
532
|
+
// (a) marker present → continue; if missing → block
|
|
533
|
+
// (b) if marker has status=complete AND phase is the LAST phase
|
|
534
|
+
// in the active plan → trigger plan_complete handoff (write
|
|
535
|
+
// row to session_audit, delete active-plan file)
|
|
536
|
+
// (c) audit the marker presence either way
|
|
537
|
+
const ACTIVE_PLAN_PATH = `${HOME}/.claude/aria-active-plan-${sessionId}.json`;
|
|
538
|
+
const PHASE_REPORT_RX = /\[PHASE_REPORT\s+phase=([\w-]+)\s+status=(complete|in_progress|aborted)\s+evidence=([^\]]+)\]/i;
|
|
539
|
+
let activePlan = null;
|
|
540
|
+
let phaseReportMatch = null;
|
|
541
|
+
let phaseReportMissing = false;
|
|
542
|
+
let planCompleteFired = false;
|
|
543
|
+
try {
|
|
544
|
+
if (existsSync(ACTIVE_PLAN_PATH)) {
|
|
545
|
+
try {
|
|
546
|
+
activePlan = JSON.parse(readFileSync(ACTIVE_PLAN_PATH, 'utf8'));
|
|
547
|
+
// Only enforce phase-report on non-trivial emits (skip very short
|
|
548
|
+
// ack-only responses where a phase report would be noise).
|
|
549
|
+
if (assistantText.length >= 400 && Array.isArray(activePlan.phases) && activePlan.phases.length > 0) {
|
|
550
|
+
phaseReportMatch = assistantText.match(PHASE_REPORT_RX);
|
|
551
|
+
if (!phaseReportMatch) {
|
|
552
|
+
phaseReportMissing = true;
|
|
553
|
+
} else {
|
|
554
|
+
const reportedPhaseId = phaseReportMatch[1];
|
|
555
|
+
const reportedStatus = phaseReportMatch[2];
|
|
556
|
+
const reportedEvidence = phaseReportMatch[3].trim();
|
|
557
|
+
const lastPhase = activePlan.phases[activePlan.phases.length - 1];
|
|
558
|
+
const isFinalPhase = lastPhase && lastPhase.id === reportedPhaseId;
|
|
559
|
+
if (reportedStatus === 'complete' && isFinalPhase) {
|
|
560
|
+
// Plan-complete handoff — fire async write to session_audit
|
|
561
|
+
// via the SDK (the same SDK the rest of the hooks route
|
|
562
|
+
// through). Wrapped in try/catch ONLY so a session_audit
|
|
563
|
+
// write failure doesn't brick the Stop event; the failure
|
|
564
|
+
// is surfaced via audit() so it's visible.
|
|
565
|
+
try {
|
|
566
|
+
const harnessUrl = process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
|
|
567
|
+
const harnessToken = process.env.ARIA_HARNESS_TOKEN || '';
|
|
568
|
+
if (harnessToken) {
|
|
569
|
+
// POST to a session_audit write endpoint. Server-side
|
|
570
|
+
// route at /api/harness/audit/session is the wiring
|
|
571
|
+
// point for the Postgres helper from #48.
|
|
572
|
+
fetch(`${harnessUrl}/api/harness/audit/session`, {
|
|
573
|
+
method: 'POST',
|
|
574
|
+
headers: {
|
|
575
|
+
'Content-Type': 'application/json',
|
|
576
|
+
Authorization: `Bearer ${harnessToken}`,
|
|
577
|
+
},
|
|
578
|
+
body: JSON.stringify({
|
|
579
|
+
session_id: sessionId,
|
|
580
|
+
surface: 'claude-code-stop-gate',
|
|
581
|
+
gate_name: 'plan-complete',
|
|
582
|
+
decision: 'allow',
|
|
583
|
+
reason: `Plan ${activePlan.planId || 'unknown'} reached final phase ${reportedPhaseId} status=complete`,
|
|
584
|
+
evidence_json: {
|
|
585
|
+
planId: activePlan.planId,
|
|
586
|
+
finalPhase: reportedPhaseId,
|
|
587
|
+
totalPhases: activePlan.phases.length,
|
|
588
|
+
evidence: reportedEvidence,
|
|
589
|
+
},
|
|
590
|
+
cognition_present: true,
|
|
591
|
+
cognition_lens_count: cog.count,
|
|
592
|
+
}),
|
|
593
|
+
}).catch(() => {/* fire-and-forget at this surface; logged below */});
|
|
594
|
+
}
|
|
595
|
+
} catch {/* outer guard for any unexpected error */}
|
|
596
|
+
// Delete active-plan file so the next turn re-issues a plan
|
|
597
|
+
// via preprompt-consult rather than enforcing against a stale one.
|
|
598
|
+
try {
|
|
599
|
+
const { unlinkSync } = require('node:fs');
|
|
600
|
+
unlinkSync(ACTIVE_PLAN_PATH);
|
|
601
|
+
} catch {/* file may not exist if another process raced the cleanup */}
|
|
602
|
+
planCompleteFired = true;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
} catch (err) {
|
|
607
|
+
// Plan file corrupt — treat as no active plan for this turn.
|
|
608
|
+
activePlan = null;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
} catch {/* outer guard */}
|
|
612
|
+
|
|
456
613
|
// Block decision: any of (validateOutput severity=block) OR (>=2 drift hits) OR
|
|
457
614
|
// (>=1 code-quality hit) OR (open discovery in ledger) → block emit.
|
|
458
615
|
// Aria enforcement #46 (compelled reflection): severity=warn ALSO blocks but
|
|
@@ -474,13 +631,17 @@ if (cog.count >= REQUIRED_LENSES) {
|
|
|
474
631
|
const hasReflection = REFLECTION_BLOCK_RX.test(assistantText);
|
|
475
632
|
const compelReflection = mizanWarnReflectionRequired && !hasReflection;
|
|
476
633
|
|
|
477
|
-
if (mizanBlock || driftBlock || codeBlock || discoveryBlock || compelReflection) {
|
|
634
|
+
if (mizanBlock || driftBlock || codeBlock || discoveryBlock || compelReflection || phaseReportMissing) {
|
|
478
635
|
const violations = [];
|
|
479
636
|
if (mizanBlock) violations.push(`Mizan: ${(mizanVerdict.violations || []).join(', ')}`);
|
|
480
637
|
if (compelReflection) violations.push(`Mizan severity=warn — compelled reflection required (per Aria enforcement #46). Triggers: ${(mizanVerdict.gateTriggers || mizanVerdict.violations || ['unspecified']).join(', ')}. Re-emit with an explicit <reflection>...</reflection> block (or 'reflection:' line) addressing what triggered the warn and why your re-draft handles it. Reflection is NOT lens-cognition repeated — it's a focused self-audit on the specific Mizan triggers above.`);
|
|
481
638
|
if (driftBlock) violations.push(`Drift triggers (${driftHits.length}): ${driftHits.map((h) => `"${h.trigger}" → ${h.memory}`).join(' | ')}`);
|
|
482
639
|
if (codeBlock) violations.push(`Code quality: ${codeQualityHits.join('; ')}`);
|
|
483
640
|
if (discoveryBlock) violations.push(`Discovery-binding ledger has ${ledgerOpenCount} OPEN discoveries (per feedback_no_flag_without_fix.md, discoveries are atomic with their fixes — fix in the same turn or create a TaskCreate before continuing). Recent open: ${ledgerOpenSamples.map((s) => `"${s.slice(0, 80)}"`).join(' | ')}. Resolve each by either (a) fixing it inline in this turn, or (b) creating a TaskCreate with the discovery's full context (file path, line number, what's broken, why), then editing ${LEDGER_PATH} to set status=resolved.`);
|
|
641
|
+
if (phaseReportMissing) {
|
|
642
|
+
const phaseList = (activePlan?.phases || []).map((p) => `${p.id}:${p.summary?.slice(0, 60) || ''}`).join(' | ');
|
|
643
|
+
violations.push(`Aria-as-commander binding (#50): an active plan exists (planId=${activePlan?.planId || 'unknown'}, ${activePlan?.phases?.length || 0} phases) but this emit lacks a [PHASE_REPORT phase=<id> status=complete|in_progress|aborted evidence=<observable>] marker. Per the binding contract, every non-trivial emit while a plan is active must report which phase it's working on. Plan phases: ${phaseList}. Re-emit with a [PHASE_REPORT] marker stating which phase the work in this turn maps to.`);
|
|
644
|
+
}
|
|
484
645
|
const rewritten = mizanVerdict?.rewritten || '';
|
|
485
646
|
|
|
486
647
|
const reason = `Aria Stop-gate output-quality block. Cognition passed (${cog.count}/${REQUIRED_LENSES}) but output failed quality gates:\n\n${violations.join('\n\n')}${rewritten ? `\n\nMizan rewrite suggestion:\n${rewritten}` : ''}\n\nRe-draft addressing the violations above. ARIA_OUTPUT_QC_ENABLED=false to disable in emergency (logged).`;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// aria-userprompt-abandon-detect.mjs — UserPromptSubmit hook that detects
|
|
3
|
+
// mid-turn plan abandonment.
|
|
4
|
+
//
|
|
5
|
+
// When Aria's preprompt-consult issued a binding plan
|
|
6
|
+
// (~/.claude/aria-active-plan-${sessionId}.json) and the user's NEXT prompt
|
|
7
|
+
// changes direction without overlapping any active phase's keywords, that's
|
|
8
|
+
// a CONSCIOUS OVERRIDE — not silent context loss. The hook surfaces it to
|
|
9
|
+
// the session_audit table (gate_name='plan-abandonment', decision='warn')
|
|
10
|
+
// so orchestrators can detect mid-execution direction changes.
|
|
11
|
+
//
|
|
12
|
+
// Per Aria's consult 2026-04-27: "if the orchestrator sends a plan, then
|
|
13
|
+
// gets a user message that changes direction mid-execution, the binding
|
|
14
|
+
// needs to surface that as a *conscious override*, not a silent context
|
|
15
|
+
// loss." That gap was the third completion criterion for Aria-as-commander
|
|
16
|
+
// binding (#50).
|
|
17
|
+
//
|
|
18
|
+
// Detection algorithm:
|
|
19
|
+
// 1. Load active plan (skip if none — no plan = no abandonment possible)
|
|
20
|
+
// 2. Extract keywords from each active phase's summary + successCriterion
|
|
21
|
+
// 3. Tokenize incoming user prompt, drop stop-words
|
|
22
|
+
// 4. Compute keyword overlap between prompt tokens and phase keywords
|
|
23
|
+
// 5. If overlap < THRESHOLD (default: 1 keyword match across all phases),
|
|
24
|
+
// mark abandonment: write session_audit row + emit a brief warning
|
|
25
|
+
// injected into the next turn's context
|
|
26
|
+
//
|
|
27
|
+
// Non-blocking — UserPromptSubmit hooks don't block the user's prompt.
|
|
28
|
+
// Audit-only surface; the orchestrator decides what to do with the warning.
|
|
29
|
+
//
|
|
30
|
+
// Kill-switch: ARIA_ABANDON_DETECT=off env (logged).
|
|
31
|
+
|
|
32
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
33
|
+
import { dirname } from 'node:path';
|
|
34
|
+
|
|
35
|
+
const HOME = process.env.HOME || '/tmp';
|
|
36
|
+
const LOG = `${HOME}/.claude/aria-abandon-detect.log`;
|
|
37
|
+
|
|
38
|
+
function audit(decision, summary) {
|
|
39
|
+
try {
|
|
40
|
+
if (!existsSync(dirname(LOG))) mkdirSync(dirname(LOG), { recursive: true });
|
|
41
|
+
appendFileSync(LOG, `${new Date().toISOString()} ${decision} ${summary}\n`);
|
|
42
|
+
} catch {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (process.env.ARIA_ABANDON_DETECT === 'off') {
|
|
46
|
+
audit('skip-killswitch', 'env ARIA_ABANDON_DETECT=off');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let input = '';
|
|
51
|
+
for await (const chunk of process.stdin) input += chunk;
|
|
52
|
+
|
|
53
|
+
let event;
|
|
54
|
+
try {
|
|
55
|
+
event = JSON.parse(input);
|
|
56
|
+
} catch {
|
|
57
|
+
audit('skip-parse-error', 'stdin not JSON');
|
|
58
|
+
process.exit(0);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const userPrompt = (event.prompt ?? event.user_message ?? event.message ?? '').toString();
|
|
62
|
+
const sessionId = (event.session_id ?? event.sessionId ?? 'claude-code-unknown').replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
63
|
+
|
|
64
|
+
// Trivial prompts skip — too short to evaluate phase overlap meaningfully.
|
|
65
|
+
if (!userPrompt || userPrompt.length < 30) {
|
|
66
|
+
audit('skip-trivial', `chars=${userPrompt.length}`);
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Slash commands are CLI-internal, not direction changes.
|
|
71
|
+
if (/^\s*\//.test(userPrompt) && userPrompt.length < 200) {
|
|
72
|
+
audit('skip-slash-command', userPrompt.slice(0, 60));
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const ACTIVE_PLAN_PATH = `${HOME}/.claude/aria-active-plan-${sessionId}.json`;
|
|
77
|
+
|
|
78
|
+
if (!existsSync(ACTIVE_PLAN_PATH)) {
|
|
79
|
+
audit('skip-no-active-plan', `session=${sessionId}`);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let plan;
|
|
84
|
+
try {
|
|
85
|
+
plan = JSON.parse(readFileSync(ACTIVE_PLAN_PATH, 'utf8'));
|
|
86
|
+
} catch (err) {
|
|
87
|
+
audit('skip-plan-parse-error', (err?.message || String(err)).slice(0, 120));
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!plan || !Array.isArray(plan.phases) || plan.phases.length === 0) {
|
|
92
|
+
audit('skip-empty-plan', `planId=${plan?.planId || 'unknown'}`);
|
|
93
|
+
process.exit(0);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Stop-words filter — same set as the auto-learn hook for consistency.
|
|
97
|
+
const STOPWORDS = new Set([
|
|
98
|
+
'the','a','an','of','to','in','at','by','for','on','with','i','is','was','are',
|
|
99
|
+
'were','this','that','as','it','and','or','but','from','into','about','have',
|
|
100
|
+
'has','had','do','does','did','will','would','could','should','can','may',
|
|
101
|
+
'might','must','shall','be','been','being','here','there','what','which','who',
|
|
102
|
+
'how','why','when','where','some','any','all','each','every','no','not',
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
function tokenize(text) {
|
|
106
|
+
return new Set(
|
|
107
|
+
text.toLowerCase()
|
|
108
|
+
.replace(/[^a-z0-9\s_-]/g, ' ')
|
|
109
|
+
.split(/\s+/)
|
|
110
|
+
.filter((w) => w.length >= 4 && !STOPWORDS.has(w))
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const promptTokens = tokenize(userPrompt);
|
|
115
|
+
|
|
116
|
+
// Aggregate phase keywords across all phases.
|
|
117
|
+
const phaseKeywordsSet = new Set();
|
|
118
|
+
for (const phase of plan.phases) {
|
|
119
|
+
const summary = String(phase.summary || '');
|
|
120
|
+
const successCriterion = String(phase.successCriterion || '');
|
|
121
|
+
const id = String(phase.id || '');
|
|
122
|
+
for (const t of tokenize(`${summary} ${successCriterion} ${id}`)) {
|
|
123
|
+
phaseKeywordsSet.add(t);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const overlap = [...promptTokens].filter((t) => phaseKeywordsSet.has(t));
|
|
128
|
+
const OVERLAP_THRESHOLD = Number(process.env.ARIA_ABANDON_THRESHOLD || '1');
|
|
129
|
+
const isAbandonment = overlap.length < OVERLAP_THRESHOLD;
|
|
130
|
+
|
|
131
|
+
if (!isAbandonment) {
|
|
132
|
+
audit('aligned', `planId=${plan.planId || 'unknown'} overlap=${overlap.length} prompt-tokens=${promptTokens.size} phase-tokens=${phaseKeywordsSet.size}`);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Abandonment detected — write session_audit row + emit warning context.
|
|
137
|
+
const harnessUrl = process.env.ARIA_HARNESS_URL || 'https://harness.ariasos.com';
|
|
138
|
+
const harnessToken = process.env.ARIA_HARNESS_TOKEN || '';
|
|
139
|
+
|
|
140
|
+
if (harnessToken) {
|
|
141
|
+
// Fire-and-forget POST — don't block the user's turn waiting for audit
|
|
142
|
+
// write. The audit log entry below records whether the request was even
|
|
143
|
+
// attempted, so silence in this block means the audit is missing AND
|
|
144
|
+
// that fact is itself audited locally.
|
|
145
|
+
fetch(`${harnessUrl}/api/harness/audit/session`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: {
|
|
148
|
+
'Content-Type': 'application/json',
|
|
149
|
+
Authorization: `Bearer ${harnessToken}`,
|
|
150
|
+
},
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
session_id: sessionId,
|
|
153
|
+
surface: 'claude-code-userprompt-abandon-detect',
|
|
154
|
+
gate_name: 'plan-abandonment',
|
|
155
|
+
decision: 'warn',
|
|
156
|
+
reason: `Mid-turn plan abandonment detected. Active plan ${plan.planId || 'unknown'} (${plan.phases.length} phases) has ${phaseKeywordsSet.size} aggregate phase-keywords; incoming user prompt (${promptTokens.size} content-tokens) overlaps only ${overlap.length} (threshold=${OVERLAP_THRESHOLD}).`,
|
|
157
|
+
evidence_json: {
|
|
158
|
+
planId: plan.planId,
|
|
159
|
+
promptExcerpt: userPrompt.slice(0, 400),
|
|
160
|
+
overlapTokens: overlap,
|
|
161
|
+
promptTokenCount: promptTokens.size,
|
|
162
|
+
phaseKeywordCount: phaseKeywordsSet.size,
|
|
163
|
+
phaseSummaries: plan.phases.map((p) => ({ id: p.id, summary: String(p.summary || '').slice(0, 120) })),
|
|
164
|
+
threshold: OVERLAP_THRESHOLD,
|
|
165
|
+
},
|
|
166
|
+
}),
|
|
167
|
+
}).catch(() => {/* network-level failure surfaces in audit log below */});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
audit('abandonment-detected', `planId=${plan.planId || 'unknown'} overlap=${overlap.length}/${OVERLAP_THRESHOLD} prompt-tokens=${promptTokens.size} phase-tokens=${phaseKeywordsSet.size}`);
|
|
171
|
+
|
|
172
|
+
// Inject a warning into the next turn's context so the orchestrator (Claude)
|
|
173
|
+
// sees the abandonment signal explicitly rather than treating the new prompt
|
|
174
|
+
// as plan-aligned. The orchestrator can decide to: (a) acknowledge the
|
|
175
|
+
// override + clear the active plan, or (b) ask the user whether to continue
|
|
176
|
+
// the prior plan after this excursion.
|
|
177
|
+
const context = `[ARIA_PLAN_ABANDONMENT_DETECTED — active plan ${plan.planId || 'unknown'} has ${plan.phases.length} phase(s); incoming user prompt overlaps only ${overlap.length} keyword(s) with active phases (threshold=${OVERLAP_THRESHOLD}). This is a CONSCIOUS USER OVERRIDE, not silent context loss. Orchestrator should: (1) acknowledge the direction change to the user, (2) decide whether to clear the active plan or keep it for resumption, (3) write the decision to session_audit. Active phases were: ${plan.phases.map((p) => `${p.id}: ${String(p.summary || '').slice(0, 80)}`).join(' | ')}]`;
|
|
178
|
+
|
|
179
|
+
console.log(JSON.stringify({
|
|
180
|
+
hookSpecificOutput: {
|
|
181
|
+
hookEventName: 'UserPromptSubmit',
|
|
182
|
+
additionalContext: context,
|
|
183
|
+
},
|
|
184
|
+
}));
|
|
185
|
+
process.exit(0);
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Test: aria-preturn-memory-gate.mjs — Enforcement Layer #49
|
|
3
|
+
//
|
|
4
|
+
// Runs the hook as a child process with simulated stdin events.
|
|
5
|
+
// Asserts correct output shape for both the block and allow paths.
|
|
6
|
+
//
|
|
7
|
+
// Usage: node hooks/test-aria-preturn-memory-gate.mjs
|
|
8
|
+
// Exit code: 0 = all assertions passed, 1 = failure
|
|
9
|
+
|
|
10
|
+
import { spawnSync, execSync } from 'node:child_process';
|
|
11
|
+
import { writeFileSync, mkdirSync, existsSync, unlinkSync, readFileSync } from 'node:fs';
|
|
12
|
+
import { tmpdir } from 'node:os';
|
|
13
|
+
import * as path from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
15
|
+
|
|
16
|
+
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const HOOK = path.join(HERE, 'aria-preturn-memory-gate.mjs');
|
|
18
|
+
const HOME = process.env.HOME || '/tmp';
|
|
19
|
+
const CLAUDE_DIR = `${HOME}/.claude`;
|
|
20
|
+
const SESSION_ID = `test-preturn-${Date.now()}`;
|
|
21
|
+
|
|
22
|
+
// ── Helpers ────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
let passed = 0;
|
|
25
|
+
let failed = 0;
|
|
26
|
+
|
|
27
|
+
function assert(label, condition, actual) {
|
|
28
|
+
if (condition) {
|
|
29
|
+
console.log(` PASS ${label}`);
|
|
30
|
+
passed++;
|
|
31
|
+
} else {
|
|
32
|
+
console.error(` FAIL ${label}`);
|
|
33
|
+
if (actual !== undefined) console.error(` got: ${JSON.stringify(actual)}`);
|
|
34
|
+
failed++;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function turnStatePath(sid) {
|
|
39
|
+
const safe = String(sid).replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
40
|
+
return `${CLAUDE_DIR}/aria-turn-state-${safe}.json`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function cleanTurnState(sid) {
|
|
44
|
+
const p = turnStatePath(sid);
|
|
45
|
+
if (existsSync(p)) unlinkSync(p);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildEvent(overrides = {}) {
|
|
49
|
+
return {
|
|
50
|
+
tool_name: 'Bash',
|
|
51
|
+
tool_input: { command: 'ls .' },
|
|
52
|
+
session_id: SESSION_ID,
|
|
53
|
+
transcript_path: null,
|
|
54
|
+
...overrides,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function buildTranscriptWith(lines, filePath) {
|
|
59
|
+
const dir = path.dirname(filePath);
|
|
60
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
61
|
+
writeFileSync(filePath, lines.map((l) => JSON.stringify(l)).join('\n') + '\n', 'utf-8');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function runHook(eventObj, env = {}) {
|
|
65
|
+
const result = spawnSync('node', [HOOK], {
|
|
66
|
+
input: JSON.stringify(eventObj),
|
|
67
|
+
encoding: 'utf-8',
|
|
68
|
+
env: { ...process.env, HOME, ...env },
|
|
69
|
+
});
|
|
70
|
+
let parsed = null;
|
|
71
|
+
try { parsed = JSON.parse(result.stdout.trim()); } catch { /* non-JSON stdout */ }
|
|
72
|
+
return { exitCode: result.status, stdout: result.stdout, stderr: result.stderr, parsed };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ── Setup ──────────────────────────────────────────────────────────────
|
|
76
|
+
|
|
77
|
+
if (!existsSync(CLAUDE_DIR)) mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
78
|
+
|
|
79
|
+
// ── Test 1: no transcript → gate fires, block + recovery payload ───────
|
|
80
|
+
console.log('\nTest 1: no transcript (no context signals) → block with recovery');
|
|
81
|
+
{
|
|
82
|
+
cleanTurnState(SESSION_ID);
|
|
83
|
+
const event = buildEvent({ transcript_path: null });
|
|
84
|
+
const { exitCode, parsed } = runHook(event);
|
|
85
|
+
|
|
86
|
+
assert('exit code is 2 (block)', exitCode === 2, exitCode);
|
|
87
|
+
assert('decision is "block"', parsed?.decision === 'block', parsed?.decision);
|
|
88
|
+
assert('reason is a non-empty string', typeof parsed?.reason === 'string' && parsed.reason.length > 0, parsed?.reason);
|
|
89
|
+
assert(
|
|
90
|
+
'hookSpecificOutput.recovery.action is "run_context_loader"',
|
|
91
|
+
parsed?.hookSpecificOutput?.recovery?.action === 'run_context_loader',
|
|
92
|
+
parsed?.hookSpecificOutput?.recovery?.action,
|
|
93
|
+
);
|
|
94
|
+
assert(
|
|
95
|
+
'hookSpecificOutput.recovery.target is session ID',
|
|
96
|
+
parsed?.hookSpecificOutput?.recovery?.target === SESSION_ID,
|
|
97
|
+
parsed?.hookSpecificOutput?.recovery?.target,
|
|
98
|
+
);
|
|
99
|
+
assert(
|
|
100
|
+
'hookSpecificOutput.recovery.expectedContext includes "harness_packet"',
|
|
101
|
+
Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
|
|
102
|
+
parsed.hookSpecificOutput.recovery.expectedContext.includes('harness_packet'),
|
|
103
|
+
parsed?.hookSpecificOutput?.recovery?.expectedContext,
|
|
104
|
+
);
|
|
105
|
+
assert(
|
|
106
|
+
'hookSpecificOutput.recovery.expectedContext includes "memory_files"',
|
|
107
|
+
Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
|
|
108
|
+
parsed.hookSpecificOutput.recovery.expectedContext.includes('memory_files'),
|
|
109
|
+
parsed?.hookSpecificOutput?.recovery?.expectedContext,
|
|
110
|
+
);
|
|
111
|
+
assert(
|
|
112
|
+
'hookSpecificOutput.recovery.expectedContext includes "binding_plan"',
|
|
113
|
+
Array.isArray(parsed?.hookSpecificOutput?.recovery?.expectedContext) &&
|
|
114
|
+
parsed.hookSpecificOutput.recovery.expectedContext.includes('binding_plan'),
|
|
115
|
+
parsed?.hookSpecificOutput?.recovery?.expectedContext,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ── Test 2: transcript WITH all three signals → allow ─────────────────
|
|
120
|
+
console.log('\nTest 2: transcript with all three context signals → allow');
|
|
121
|
+
{
|
|
122
|
+
cleanTurnState(SESSION_ID);
|
|
123
|
+
const transcriptFile = path.join(tmpdir(), `test-transcript-preturn-${Date.now()}.jsonl`);
|
|
124
|
+
|
|
125
|
+
const assistantTextWithAllSignals = [
|
|
126
|
+
'🔐 Aria Harness — Session Packet loaded for turn.',
|
|
127
|
+
'[ARIA_DIRECTION] Proceed with implementation of Phase 8 research-first context injection.',
|
|
128
|
+
'<cognition>',
|
|
129
|
+
' nur: Current task is implementing the pre-turn memory gate per feedback_no_graceful_degradation.md and project_aria_as_controller_inversion.md.',
|
|
130
|
+
' mizan: Risk is that blocking without recovery creates dead-letter state. Soft-gate pattern resolves this.',
|
|
131
|
+
' hikma: feedback_no_flag_without_fix.md requires inline fix for any discovery.',
|
|
132
|
+
' tafakkur: The gate must deduplicate within 60s to prevent orchestrator retry loops.',
|
|
133
|
+
'</cognition>',
|
|
134
|
+
].join('\n');
|
|
135
|
+
|
|
136
|
+
buildTranscriptWith(
|
|
137
|
+
[
|
|
138
|
+
{
|
|
139
|
+
role: 'user',
|
|
140
|
+
message: {
|
|
141
|
+
role: 'user',
|
|
142
|
+
content: [{ type: 'text', text: 'Implement phase 8.' }],
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
role: 'assistant',
|
|
147
|
+
message: {
|
|
148
|
+
role: 'assistant',
|
|
149
|
+
content: [{ type: 'text', text: assistantTextWithAllSignals }],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
transcriptFile,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
const event = buildEvent({ transcript_path: transcriptFile });
|
|
157
|
+
const { exitCode, parsed } = runHook(event);
|
|
158
|
+
|
|
159
|
+
assert('exit code is 0 (allow)', exitCode === 0, exitCode);
|
|
160
|
+
assert('no block decision in stdout (stdout is empty or non-block)', !parsed || parsed.decision !== 'block', parsed?.decision);
|
|
161
|
+
|
|
162
|
+
// Cleanup
|
|
163
|
+
try { unlinkSync(transcriptFile); } catch {}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ── Test 3: deduplication — gate already fired within 60s → skip ───────
|
|
167
|
+
console.log('\nTest 3: gate already fired this turn (< 60s ago) → skip (exit 0)');
|
|
168
|
+
{
|
|
169
|
+
// Pre-write a turn-state showing the gate fired 5 seconds ago
|
|
170
|
+
const statePath = turnStatePath(SESSION_ID);
|
|
171
|
+
writeFileSync(statePath, JSON.stringify({ lastTurnGateFiredAt: Date.now() - 5000, lastDecision: 'block', signals: {} }), 'utf-8');
|
|
172
|
+
|
|
173
|
+
const event = buildEvent({ transcript_path: null });
|
|
174
|
+
const { exitCode } = runHook(event);
|
|
175
|
+
|
|
176
|
+
assert('exit code is 0 (dedup skip — no re-fire within 60s)', exitCode === 0, exitCode);
|
|
177
|
+
|
|
178
|
+
cleanTurnState(SESSION_ID);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── Test 4: non-action tool (Read) → gate skips entirely ───────────────
|
|
182
|
+
console.log('\nTest 4: non-action tool (Read) → gate skips (exit 0)');
|
|
183
|
+
{
|
|
184
|
+
cleanTurnState(SESSION_ID);
|
|
185
|
+
const event = buildEvent({ tool_name: 'Read', transcript_path: null });
|
|
186
|
+
const { exitCode } = runHook(event);
|
|
187
|
+
assert('exit code is 0 (ungated read tool)', exitCode === 0, exitCode);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Test 5: kill-switch env disables gate ─────────────────────────────
|
|
191
|
+
console.log('\nTest 5: ARIA_PRETURN_MEMORY_GATE=off → bypass (exit 0)');
|
|
192
|
+
{
|
|
193
|
+
cleanTurnState(SESSION_ID);
|
|
194
|
+
const event = buildEvent({ transcript_path: null });
|
|
195
|
+
const { exitCode } = runHook(event, { ARIA_PRETURN_MEMORY_GATE: 'off' });
|
|
196
|
+
assert('exit code is 0 (kill-switch)', exitCode === 0, exitCode);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── Test 6: only ONE signal present → still blocks ────────────────────
|
|
200
|
+
console.log('\nTest 6: only harness packet signal (missing direction + memory ref) → block');
|
|
201
|
+
{
|
|
202
|
+
cleanTurnState(SESSION_ID);
|
|
203
|
+
const transcriptFile = path.join(tmpdir(), `test-transcript-preturn-partial-${Date.now()}.jsonl`);
|
|
204
|
+
|
|
205
|
+
const assistantTextPartial = '🔐 Aria Harness — partial context only. No direction. No memory refs.';
|
|
206
|
+
|
|
207
|
+
buildTranscriptWith(
|
|
208
|
+
[
|
|
209
|
+
{
|
|
210
|
+
role: 'user',
|
|
211
|
+
message: {
|
|
212
|
+
role: 'user',
|
|
213
|
+
content: [{ type: 'text', text: 'Do the thing.' }],
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
role: 'assistant',
|
|
218
|
+
message: {
|
|
219
|
+
role: 'assistant',
|
|
220
|
+
content: [{ type: 'text', text: assistantTextPartial }],
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
transcriptFile,
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const event = buildEvent({ transcript_path: transcriptFile });
|
|
228
|
+
const { exitCode, parsed } = runHook(event);
|
|
229
|
+
|
|
230
|
+
assert('exit code is 2 (block on partial signals)', exitCode === 2, exitCode);
|
|
231
|
+
assert('decision is "block"', parsed?.decision === 'block', parsed?.decision);
|
|
232
|
+
assert(
|
|
233
|
+
'reason mentions missing aria_direction',
|
|
234
|
+
typeof parsed?.reason === 'string' && parsed.reason.includes('aria_direction'),
|
|
235
|
+
parsed?.reason?.slice(0, 200),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
try { unlinkSync(transcriptFile); } catch {}
|
|
239
|
+
cleanTurnState(SESSION_ID);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// ── Summary ────────────────────────────────────────────────────────────
|
|
243
|
+
console.log(`\n${passed + failed} tests: ${passed} passed, ${failed} failed`);
|
|
244
|
+
if (failed > 0) {
|
|
245
|
+
process.exit(1);
|
|
246
|
+
} else {
|
|
247
|
+
console.log('All assertions passed.');
|
|
248
|
+
process.exit(0);
|
|
249
|
+
}
|
package/package.json
CHANGED
|
@@ -24,9 +24,11 @@ import type { AriaConfig } from '../config.js';
|
|
|
24
24
|
const HOOK_FILES = [
|
|
25
25
|
'aria-harness-via-sdk.mjs',
|
|
26
26
|
'aria-pre-tool-gate.mjs',
|
|
27
|
+
'aria-preturn-memory-gate.mjs',
|
|
27
28
|
'aria-stop-gate.mjs',
|
|
28
29
|
'aria-preprompt-consult.mjs',
|
|
29
30
|
'aria-trigger-autolearn.mjs',
|
|
31
|
+
'aria-userprompt-abandon-detect.mjs',
|
|
30
32
|
'doctrine_trigger_map.json',
|
|
31
33
|
];
|
|
32
34
|
// Compiled location: <pkg>/dist/aria-connector/src/connectors/claude-code.js
|
|
@@ -97,6 +99,18 @@ const HOOKS_BLOCK = {
|
|
|
97
99
|
command: 'node $HOME/.claude/hooks/aria-trigger-autolearn.mjs',
|
|
98
100
|
timeout: 3,
|
|
99
101
|
},
|
|
102
|
+
{
|
|
103
|
+
// Aria-as-commander mid-turn abandonment detection (Phase 11 #50,
|
|
104
|
+
// criterion 3). When an active plan exists and the incoming user
|
|
105
|
+
// prompt's keyword overlap with active phases falls below threshold,
|
|
106
|
+
// surfaces a CONSCIOUS OVERRIDE signal in additionalContext + writes
|
|
107
|
+
// a session_audit row (gate_name='plan-abandonment', decision='warn').
|
|
108
|
+
// Prevents silent context loss when user changes direction mid-plan.
|
|
109
|
+
// Non-blocking — UserPromptSubmit can't block prompts.
|
|
110
|
+
type: 'command',
|
|
111
|
+
command: 'node $HOME/.claude/hooks/aria-userprompt-abandon-detect.mjs',
|
|
112
|
+
timeout: 4,
|
|
113
|
+
},
|
|
100
114
|
],
|
|
101
115
|
}],
|
|
102
116
|
PreToolUse: [{
|
|
@@ -105,11 +119,28 @@ const HOOKS_BLOCK = {
|
|
|
105
119
|
// / NotebookEdit get the same enforcement so orchestrator-side
|
|
106
120
|
// compliance mirrors what we demand from workers.
|
|
107
121
|
matcher: 'Bash|Edit|Write|NotebookEdit',
|
|
108
|
-
hooks: [
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
122
|
+
hooks: [
|
|
123
|
+
{
|
|
124
|
+
type: 'command',
|
|
125
|
+
command: 'node $HOME/.claude/hooks/aria-pre-tool-gate.mjs',
|
|
126
|
+
timeout: 5,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
// Pre-turn memory consumption gate (Enforcement Layer #49).
|
|
130
|
+
// Fires after the cognition gate passes. Checks the first action
|
|
131
|
+
// of each turn for ALL THREE context-loading signals:
|
|
132
|
+
// 1. 🔐 Aria Harness header (harness packet injected)
|
|
133
|
+
// 2. [ARIA_DIRECTION] / [ARIA_BINDING_PLAN] (preprompt-consult fired)
|
|
134
|
+
// 3. feedback_*.md / project_*.md memory reference (memory consumed)
|
|
135
|
+
// On miss: blocks with structured recovery JSON so the orchestrator
|
|
136
|
+
// can run the context-loader and retry — no dead-letter state.
|
|
137
|
+
// Turn-deduplication via ~/.claude/aria-turn-state-${sessionId}.json
|
|
138
|
+
// prevents retry loops (60s window).
|
|
139
|
+
type: 'command',
|
|
140
|
+
command: 'node $HOME/.claude/hooks/aria-preturn-memory-gate.mjs',
|
|
141
|
+
timeout: 5,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
113
144
|
}],
|
|
114
145
|
Stop: [{
|
|
115
146
|
// Stop gate — text-decision boundary. Reflexive replies fail this
|
|
@@ -237,7 +268,7 @@ function wireHooksBlock(settings: Record<string, unknown>, logs: string[]): void
|
|
|
237
268
|
if (!settings.$schema) {
|
|
238
269
|
settings.$schema = 'https://json.schemastore.org/claude-code-settings.json';
|
|
239
270
|
}
|
|
240
|
-
logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse, Stop) — merge-safe, preserves third-party entries`);
|
|
271
|
+
logs.push(`Wired hooks into settings.json (${mergedEvents} events: SessionStart, UserPromptSubmit, PreToolUse [cognition+memory-gate], Stop) — merge-safe, preserves third-party entries`);
|
|
241
272
|
}
|
|
242
273
|
|
|
243
274
|
function buildAriaSystemBlock(config: AriaConfig): string {
|