@aria_asi/cli 0.2.30 → 0.2.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/claude-code.js +115 -20
- package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
- package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/codex.js +551 -11
- package/dist/aria-connector/src/connectors/codex.js.map +1 -1
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
- package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
- package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
- package/dist/aria-connector/src/connectors/must-read.js +115 -0
- package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
- package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/opencode.js +27 -9
- package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/runtime.js +231 -19
- package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
- package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
- package/dist/aria-connector/src/connectors/shell.js +76 -3
- package/dist/aria-connector/src/connectors/shell.js.map +1 -1
- package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
- package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
- package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
- package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
- package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
- package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
- package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
- package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
- package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
- package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
- package/dist/assets/opencode-plugins/harness-gate/index.js +40 -5
- package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
- package/dist/runtime/auth-middleware.mjs +251 -0
- package/dist/runtime/codex-bridge.mjs +644 -0
- package/dist/runtime/discipline/CLAUDE.md +28 -0
- package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
- package/dist/runtime/doctrine_trigger_map.json +534 -0
- package/dist/runtime/fleet-engine.mjs +231 -0
- package/dist/runtime/harness-daemon.mjs +460 -0
- package/dist/runtime/manifest.json +1 -1
- package/dist/runtime/metering.mjs +100 -0
- package/dist/runtime/onboarding-engine.mjs +89 -0
- package/dist/runtime/plugin-engine.mjs +196 -0
- package/dist/runtime/sdk/BUNDLED.json +1 -1
- package/dist/runtime/sdk/index.d.ts +12 -0
- package/dist/runtime/sdk/index.js +120 -14
- package/dist/runtime/sdk/index.js.map +1 -1
- package/dist/runtime/service.mjs +1140 -48
- package/dist/runtime/workflow-engine.mjs +322 -0
- package/dist/sdk/BUNDLED.json +1 -1
- package/dist/sdk/index.d.ts +12 -0
- package/dist/sdk/index.js +120 -14
- package/dist/sdk/index.js.map +1 -1
- package/hooks/aria-agent-handoff.mjs +23 -0
- package/hooks/aria-cognition-substrate-binding.mjs +121 -28
- package/hooks/aria-harness-via-sdk.mjs +126 -12
- package/hooks/aria-pre-emit-dryrun.mjs +35 -0
- package/hooks/aria-pre-tool-gate.mjs +383 -93
- package/hooks/aria-preprompt-consult.mjs +28 -2
- package/hooks/aria-preturn-memory-gate.mjs +93 -16
- package/hooks/aria-repo-doctrine-gate.mjs +33 -1
- package/hooks/aria-stop-gate.mjs +346 -81
- package/hooks/doctrine_trigger_map.json +55 -0
- package/hooks/lib/canonical-lenses.mjs +6 -5
- package/hooks/lib/gate-loop-state.mjs +50 -0
- package/hooks/lib/hook-message-window.mjs +121 -0
- package/hooks/test-tier-lens-labeling.mjs +26 -58
- package/opencode-plugins/harness-gate/index.js +40 -5
- package/opencode-plugins/harness-stop/index.js +133 -10
- package/package.json +1 -1
- package/runtime-src/auth-middleware.mjs +251 -0
- package/runtime-src/codex-bridge.mjs +644 -0
- package/runtime-src/fleet-engine.mjs +231 -0
- package/runtime-src/harness-daemon.mjs +460 -0
- package/runtime-src/metering.mjs +100 -0
- package/runtime-src/onboarding-engine.mjs +89 -0
- package/runtime-src/plugin-engine.mjs +196 -0
- package/runtime-src/service.mjs +1140 -48
- package/runtime-src/workflow-engine.mjs +322 -0
- package/scripts/bundle-sdk.mjs +5 -0
- package/src/connectors/claude-code.ts +126 -20
- package/src/connectors/codex.ts +559 -10
- package/src/connectors/doctrine-trigger-map.ts +112 -0
- package/src/connectors/must-read.ts +117 -0
- package/src/connectors/opencode.ts +28 -9
- package/src/connectors/runtime.ts +241 -21
- package/src/connectors/shell.ts +78 -3
- package/dist/cli-0.2.0.tgz +0 -0
package/src/connectors/codex.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
copyFileSync,
|
|
9
9
|
chmodSync,
|
|
10
10
|
writeFileSync,
|
|
11
|
+
readFileSync,
|
|
11
12
|
} from 'fs';
|
|
12
13
|
import { homedir } from 'os';
|
|
13
14
|
import * as path from 'path';
|
|
@@ -15,6 +16,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
15
16
|
import type { AriaConfig } from '../config.js';
|
|
16
17
|
import { connectShell } from './shell.js';
|
|
17
18
|
import { installAriaCognitionSkills } from './cognitive-skills.js';
|
|
19
|
+
import { syncDoctrineTriggerMap } from './doctrine-trigger-map.js';
|
|
20
|
+
import { buildMustReadGuide, mustReadIntro } from './must-read.js';
|
|
18
21
|
|
|
19
22
|
function packageSdkDir(): string {
|
|
20
23
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -81,6 +84,528 @@ function installNodePackage(codexDir: string, logs: string[]): void {
|
|
|
81
84
|
logs.push(`Installed Codex Node package → ${pkgRoot}`);
|
|
82
85
|
}
|
|
83
86
|
|
|
87
|
+
function tomlString(value: string): string {
|
|
88
|
+
return JSON.stringify(value);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildCodexHookRuntimeClient(): string {
|
|
92
|
+
return `import { readFileSync, existsSync, mkdirSync, writeFileSync, unlinkSync } from 'node:fs';
|
|
93
|
+
import { homedir } from 'node:os';
|
|
94
|
+
import path from 'node:path';
|
|
95
|
+
import { HTTPHarnessClient } from '@aria_asi/harness-http-client';
|
|
96
|
+
|
|
97
|
+
const HOME = homedir();
|
|
98
|
+
const DEFAULT_RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\\/+$/, '');
|
|
99
|
+
const TURN_STATE_DIR = path.join(HOME, '.codex', 'tmp', 'aria-hook-turn-state');
|
|
100
|
+
|
|
101
|
+
function readToken() {
|
|
102
|
+
const envToken = process.env.ARIA_API_KEY || process.env.ARIA_MASTER_TOKEN || process.env.OPENAI_API_KEY;
|
|
103
|
+
if (envToken) return envToken;
|
|
104
|
+
const ownerPath = path.join(HOME, '.aria', 'owner-token');
|
|
105
|
+
if (existsSync(ownerPath)) return readFileSync(ownerPath, 'utf8').trim();
|
|
106
|
+
const licensePath = path.join(HOME, '.aria', 'license.json');
|
|
107
|
+
if (existsSync(licensePath)) {
|
|
108
|
+
const text = readFileSync(licensePath, 'utf8');
|
|
109
|
+
const harnessToken = text.match(/"harnessToken"\\s*:\\s*"([^"]+)"/)?.[1];
|
|
110
|
+
if (harnessToken) return harnessToken;
|
|
111
|
+
const token = text.match(/"token"\\s*:\\s*"([^"]+)"/)?.[1];
|
|
112
|
+
if (token) return token;
|
|
113
|
+
}
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let clientInstance = null;
|
|
118
|
+
|
|
119
|
+
export function getHarnessClient() {
|
|
120
|
+
if (clientInstance) return clientInstance;
|
|
121
|
+
clientInstance = new HTTPHarnessClient({
|
|
122
|
+
baseUrl: DEFAULT_RUNTIME_URL,
|
|
123
|
+
apiKey: readToken(),
|
|
124
|
+
workspaceRoot: process.cwd(),
|
|
125
|
+
});
|
|
126
|
+
return clientInstance;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function readEventFromStdin() {
|
|
130
|
+
try {
|
|
131
|
+
const raw = readFileSync(0, 'utf8');
|
|
132
|
+
return raw.trim() ? JSON.parse(raw) : {};
|
|
133
|
+
} catch {
|
|
134
|
+
return {};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function extractFirst(value, keys) {
|
|
139
|
+
if (!value || typeof value !== 'object') return null;
|
|
140
|
+
for (const key of keys) {
|
|
141
|
+
const candidate = value[key];
|
|
142
|
+
if (typeof candidate === 'string' && candidate.trim()) return candidate.trim();
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function inferSessionId(event) {
|
|
148
|
+
const threadId =
|
|
149
|
+
extractFirst(event, ['thread_id', 'threadId', 'conversation_id', 'conversationId']) ||
|
|
150
|
+
'codex';
|
|
151
|
+
const turnId =
|
|
152
|
+
extractFirst(event, ['turn_id', 'turnId', 'session_id', 'sessionId']) ||
|
|
153
|
+
String(Date.now());
|
|
154
|
+
return \`codex:\${threadId}:\${turnId}\`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function inferUserId(event) {
|
|
158
|
+
return (
|
|
159
|
+
extractFirst(event, ['user_id', 'userId']) ||
|
|
160
|
+
extractFirst(event?.metadata, ['user_id', 'userId'])
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function extractText(value) {
|
|
165
|
+
if (!value) return '';
|
|
166
|
+
if (typeof value === 'string') return value.trim();
|
|
167
|
+
if (Array.isArray(value)) {
|
|
168
|
+
return value.map(extractText).filter(Boolean).join('\\n\\n').trim();
|
|
169
|
+
}
|
|
170
|
+
if (typeof value === 'object') {
|
|
171
|
+
const parts = [];
|
|
172
|
+
for (const key of ['text', 'message', 'content', 'input_text', 'inputText', 'reasoning_text', 'summary_text', 'delta']) {
|
|
173
|
+
if (key in value) {
|
|
174
|
+
const piece = extractText(value[key]);
|
|
175
|
+
if (piece) parts.push(piece);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return parts.join('\\n\\n').trim();
|
|
179
|
+
}
|
|
180
|
+
return '';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function ensureTurnStateDir() {
|
|
184
|
+
if (!existsSync(TURN_STATE_DIR)) {
|
|
185
|
+
mkdirSync(TURN_STATE_DIR, { recursive: true, mode: 0o700 });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function turnStatePath(sessionId) {
|
|
190
|
+
ensureTurnStateDir();
|
|
191
|
+
const safe = String(sessionId || 'codex').replace(/[^a-zA-Z0-9._-]+/g, '_');
|
|
192
|
+
return path.join(TURN_STATE_DIR, \`\${safe}.json\`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export function loadTurnState(sessionId) {
|
|
196
|
+
const statePath = turnStatePath(sessionId);
|
|
197
|
+
if (!existsSync(statePath)) return {};
|
|
198
|
+
try {
|
|
199
|
+
return JSON.parse(readFileSync(statePath, 'utf8'));
|
|
200
|
+
} catch {
|
|
201
|
+
return {};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function saveTurnState(sessionId, patch) {
|
|
206
|
+
const next = {
|
|
207
|
+
...loadTurnState(sessionId),
|
|
208
|
+
...patch,
|
|
209
|
+
sessionId,
|
|
210
|
+
updatedAt: new Date().toISOString(),
|
|
211
|
+
};
|
|
212
|
+
writeFileSync(turnStatePath(sessionId), JSON.stringify(next, null, 2) + '\\n', { mode: 0o600 });
|
|
213
|
+
return next;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function clearTurnState(sessionId) {
|
|
217
|
+
const statePath = turnStatePath(sessionId);
|
|
218
|
+
if (!existsSync(statePath)) return;
|
|
219
|
+
try {
|
|
220
|
+
unlinkSync(statePath);
|
|
221
|
+
} catch {}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export async function runtimePost(route, body = {}) {
|
|
225
|
+
const token = readToken();
|
|
226
|
+
const headers = {
|
|
227
|
+
'Content-Type': 'application/json',
|
|
228
|
+
};
|
|
229
|
+
if (token) headers.Authorization = \`Bearer \${token}\`;
|
|
230
|
+
const response = await fetch(\`\${DEFAULT_RUNTIME_URL}\${route}\`, {
|
|
231
|
+
method: 'POST',
|
|
232
|
+
headers,
|
|
233
|
+
body: JSON.stringify(body),
|
|
234
|
+
});
|
|
235
|
+
if (!response.ok) {
|
|
236
|
+
const detail = await response.text().catch(() => response.statusText);
|
|
237
|
+
throw new Error(\`runtime \${route} failed (\${response.status}): \${detail}\`);
|
|
238
|
+
}
|
|
239
|
+
return response.json();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function classifyAction(event) {
|
|
243
|
+
const toolName = String(event?.tool_name || event?.toolName || '').trim();
|
|
244
|
+
const toolCommand = String(
|
|
245
|
+
event?.tool_input?.command ??
|
|
246
|
+
event?.toolInput?.command ??
|
|
247
|
+
''
|
|
248
|
+
).trim();
|
|
249
|
+
const haystack = \`\${toolName}\\n\${toolCommand}\\n\${JSON.stringify(event)}\`;
|
|
250
|
+
if (/deploy|kubectl|docker\\s+push/i.test(haystack)) return 'deploy';
|
|
251
|
+
if (/\\brm\\b|delete|unlink|drop\\s+table/i.test(haystack)) return 'delete';
|
|
252
|
+
if (/build|test|tsc|jest|vitest|npm run|pnpm|yarn/i.test(haystack)) return 'build';
|
|
253
|
+
return 'write';
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function summarizeTarget(event) {
|
|
257
|
+
return JSON.stringify({
|
|
258
|
+
tool_name: event?.tool_name || event?.toolName || null,
|
|
259
|
+
tool_input: event?.tool_input || event?.toolInput || null,
|
|
260
|
+
}).slice(0, 4000);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function extractUserText(event) {
|
|
264
|
+
return extractText(
|
|
265
|
+
event?.input ??
|
|
266
|
+
event?.user_input ??
|
|
267
|
+
event?.userInput ??
|
|
268
|
+
event?.prompt ??
|
|
269
|
+
event?.message ??
|
|
270
|
+
event
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function extractAssistantText(event) {
|
|
275
|
+
return extractText(
|
|
276
|
+
event?.last_assistant_message ??
|
|
277
|
+
event?.lastAssistantMessage ??
|
|
278
|
+
event?.output ??
|
|
279
|
+
event?.response ??
|
|
280
|
+
event?.message ??
|
|
281
|
+
event
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function formatValidationFailure(result) {
|
|
286
|
+
const parts = [];
|
|
287
|
+
if (Array.isArray(result?.validation?.violations) && result.validation.violations.length) {
|
|
288
|
+
parts.push(...result.validation.violations);
|
|
289
|
+
}
|
|
290
|
+
if (Array.isArray(result?.layer3?.failures) && result.layer3.failures.length) {
|
|
291
|
+
parts.push(...result.layer3.failures.map((failure) => failure?.detail || failure?.kind || JSON.stringify(failure)));
|
|
292
|
+
}
|
|
293
|
+
if (!parts.length && typeof result?.summary === 'string' && result.summary.trim()) {
|
|
294
|
+
parts.push(result.summary.trim());
|
|
295
|
+
}
|
|
296
|
+
return parts.join(' | ') || 'Aria validation failed.';
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function emitJson(payload, code = 0) {
|
|
300
|
+
process.stdout.write(\`\${JSON.stringify(payload)}\\n\`);
|
|
301
|
+
process.exit(code);
|
|
302
|
+
}
|
|
303
|
+
`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function buildCodexUserPromptHook(): string {
|
|
307
|
+
return `#!/usr/bin/env node
|
|
308
|
+
import {
|
|
309
|
+
getHarnessClient,
|
|
310
|
+
inferSessionId,
|
|
311
|
+
inferUserId,
|
|
312
|
+
extractUserText,
|
|
313
|
+
readEventFromStdin,
|
|
314
|
+
runtimePost,
|
|
315
|
+
saveTurnState,
|
|
316
|
+
emitJson,
|
|
317
|
+
} from './lib/runtime-client.mjs';
|
|
318
|
+
|
|
319
|
+
const event = readEventFromStdin();
|
|
320
|
+
const client = getHarnessClient();
|
|
321
|
+
const userText = extractUserText(event);
|
|
322
|
+
const sessionId = inferSessionId(event);
|
|
323
|
+
const userId = inferUserId(event);
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const packet = await client.getHarnessPacket({
|
|
327
|
+
sessionId,
|
|
328
|
+
platform: 'codex',
|
|
329
|
+
message: userText || 'codex turn start',
|
|
330
|
+
});
|
|
331
|
+
const result = await runtimePost('/mizan/pre', {
|
|
332
|
+
sessionId,
|
|
333
|
+
packet,
|
|
334
|
+
packetRequest: {
|
|
335
|
+
sessionId,
|
|
336
|
+
platform: 'codex',
|
|
337
|
+
message: userText || 'codex turn start',
|
|
338
|
+
stage: 'codex-userprompt',
|
|
339
|
+
actor: 'codex-hook',
|
|
340
|
+
system: 'codex-hook',
|
|
341
|
+
},
|
|
342
|
+
context: {
|
|
343
|
+
sessionId,
|
|
344
|
+
surface: 'codex-hooks',
|
|
345
|
+
platform: 'codex',
|
|
346
|
+
userText,
|
|
347
|
+
userId,
|
|
348
|
+
},
|
|
349
|
+
});
|
|
350
|
+
saveTurnState(sessionId, {
|
|
351
|
+
userId,
|
|
352
|
+
userText,
|
|
353
|
+
preReceiptId: result?.receipt?.receiptId || null,
|
|
354
|
+
packetTimestamp: packet?.timestamp || null,
|
|
355
|
+
lastEvent: 'UserPromptSubmit',
|
|
356
|
+
});
|
|
357
|
+
process.exit(0);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
emitJson({
|
|
360
|
+
decision: 'block',
|
|
361
|
+
reason: \`Aria cognition gate failed before turn start: \${error instanceof Error ? error.message : String(error)}\`,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function buildCodexPreToolHook(): string {
|
|
368
|
+
return `#!/usr/bin/env node
|
|
369
|
+
import {
|
|
370
|
+
getHarnessClient,
|
|
371
|
+
inferSessionId,
|
|
372
|
+
classifyAction,
|
|
373
|
+
summarizeTarget,
|
|
374
|
+
readEventFromStdin,
|
|
375
|
+
loadTurnState,
|
|
376
|
+
saveTurnState,
|
|
377
|
+
emitJson,
|
|
378
|
+
} from './lib/runtime-client.mjs';
|
|
379
|
+
|
|
380
|
+
const event = readEventFromStdin();
|
|
381
|
+
const client = getHarnessClient();
|
|
382
|
+
const sessionId = inferSessionId(event);
|
|
383
|
+
const action = classifyAction(event);
|
|
384
|
+
const target = summarizeTarget(event);
|
|
385
|
+
const state = loadTurnState(sessionId);
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
if (!state?.preReceiptId && !state?.userText) {
|
|
389
|
+
emitJson({
|
|
390
|
+
decision: 'block',
|
|
391
|
+
reason: 'Aria pre-tool gate blocked action because this turn has no pre-turn Mizan receipt. Re-submit the prompt so cognition is established before tool use.',
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
const actionCheck = await client.checkAction(action, target);
|
|
395
|
+
if (actionCheck?.allowed === false) {
|
|
396
|
+
emitJson({
|
|
397
|
+
decision: 'block',
|
|
398
|
+
reason: actionCheck?.reason || \`Aria denied \${action}\`,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
const toolName = String(event?.tool_name || event?.toolName || '').trim() || null;
|
|
402
|
+
const tools = Array.isArray(state?.tools) ? state.tools.slice(-24) : [];
|
|
403
|
+
tools.push({
|
|
404
|
+
at: new Date().toISOString(),
|
|
405
|
+
action,
|
|
406
|
+
toolName,
|
|
407
|
+
target,
|
|
408
|
+
});
|
|
409
|
+
saveTurnState(sessionId, {
|
|
410
|
+
tools,
|
|
411
|
+
lastEvent: 'PreToolUse',
|
|
412
|
+
});
|
|
413
|
+
process.exit(0);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
emitJson({
|
|
416
|
+
decision: 'block',
|
|
417
|
+
reason: \`Aria pre-tool hook failed closed: \${error instanceof Error ? error.message : String(error)}\`,
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
`;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function buildCodexPostToolHook(): string {
|
|
424
|
+
return `#!/usr/bin/env node
|
|
425
|
+
import {
|
|
426
|
+
inferSessionId,
|
|
427
|
+
readEventFromStdin,
|
|
428
|
+
loadTurnState,
|
|
429
|
+
saveTurnState,
|
|
430
|
+
} from './lib/runtime-client.mjs';
|
|
431
|
+
|
|
432
|
+
const event = readEventFromStdin();
|
|
433
|
+
const sessionId = inferSessionId(event);
|
|
434
|
+
const state = loadTurnState(sessionId);
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const toolResponse = JSON.stringify(event?.tool_response ?? event?.toolResponse ?? null).slice(0, 4000);
|
|
438
|
+
const toolOutputs = Array.isArray(state?.toolOutputs) ? state.toolOutputs.slice(-24) : [];
|
|
439
|
+
toolOutputs.push({
|
|
440
|
+
at: new Date().toISOString(),
|
|
441
|
+
toolName: event?.tool_name || event?.toolName || null,
|
|
442
|
+
toolResponse,
|
|
443
|
+
});
|
|
444
|
+
saveTurnState(sessionId, {
|
|
445
|
+
toolOutputs,
|
|
446
|
+
lastEvent: 'PostToolUse',
|
|
447
|
+
});
|
|
448
|
+
process.exit(0);
|
|
449
|
+
} catch {
|
|
450
|
+
process.exit(0);
|
|
451
|
+
}
|
|
452
|
+
`;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function buildCodexStopHook(): string {
|
|
456
|
+
return `#!/usr/bin/env node
|
|
457
|
+
import {
|
|
458
|
+
getHarnessClient,
|
|
459
|
+
inferSessionId,
|
|
460
|
+
extractAssistantText,
|
|
461
|
+
readEventFromStdin,
|
|
462
|
+
runtimePost,
|
|
463
|
+
loadTurnState,
|
|
464
|
+
clearTurnState,
|
|
465
|
+
formatValidationFailure,
|
|
466
|
+
emitJson,
|
|
467
|
+
} from './lib/runtime-client.mjs';
|
|
468
|
+
|
|
469
|
+
const event = readEventFromStdin();
|
|
470
|
+
const client = getHarnessClient();
|
|
471
|
+
const sessionId = inferSessionId(event);
|
|
472
|
+
const state = loadTurnState(sessionId);
|
|
473
|
+
const text = extractAssistantText(event);
|
|
474
|
+
|
|
475
|
+
try {
|
|
476
|
+
if (!text) {
|
|
477
|
+
emitJson({ continue: true });
|
|
478
|
+
}
|
|
479
|
+
const validation = await runtimePost('/validate-output', {
|
|
480
|
+
text,
|
|
481
|
+
sessionId,
|
|
482
|
+
packetRequest: {
|
|
483
|
+
sessionId,
|
|
484
|
+
platform: 'codex',
|
|
485
|
+
message: state?.userText || 'codex stop validation',
|
|
486
|
+
stage: 'codex-stop',
|
|
487
|
+
actor: 'codex-hook',
|
|
488
|
+
system: 'codex-hook',
|
|
489
|
+
},
|
|
490
|
+
requireCognitionBlock: false,
|
|
491
|
+
runLayer3: true,
|
|
492
|
+
});
|
|
493
|
+
if (validation?.pass === false || validation?.validation?.passed === false || validation?.layer3?.pass === false) {
|
|
494
|
+
emitJson({
|
|
495
|
+
decision: 'block',
|
|
496
|
+
reason: \`Aria stop gate blocked output: \${formatValidationFailure(validation)}\`,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
const post = await runtimePost('/mizan/post', {
|
|
500
|
+
sessionId,
|
|
501
|
+
text,
|
|
502
|
+
evidence: {
|
|
503
|
+
validated_output: true,
|
|
504
|
+
validation_severity: validation?.validation?.severity || 'pass',
|
|
505
|
+
layer3_pass: validation?.layer3?.pass !== false,
|
|
506
|
+
surface: 'codex-hooks',
|
|
507
|
+
tool_count: Array.isArray(state?.tools) ? state.tools.length : 0,
|
|
508
|
+
},
|
|
509
|
+
context: {
|
|
510
|
+
sessionId,
|
|
511
|
+
surface: 'codex-hooks',
|
|
512
|
+
platform: 'codex',
|
|
513
|
+
userText: state?.userText || null,
|
|
514
|
+
},
|
|
515
|
+
parentReceiptId: state?.preReceiptId || null,
|
|
516
|
+
});
|
|
517
|
+
await runtimePost('/decision/log', {
|
|
518
|
+
session_id: sessionId,
|
|
519
|
+
decision_type: 'operational',
|
|
520
|
+
category: 'codex-hooks',
|
|
521
|
+
context: \`Codex native stop hook for \${sessionId}\`,
|
|
522
|
+
decision: 'validated output release',
|
|
523
|
+
reasoning: 'Mounted Aria runtime validated the output, issued Mizan post, and preserved the turn in garden memory.',
|
|
524
|
+
summary: 'codex native stop hook validated output',
|
|
525
|
+
outcome: 'success',
|
|
526
|
+
surface: 'codex-hooks',
|
|
527
|
+
metadata: {
|
|
528
|
+
post_receipt_id: post?.receipt?.receiptId || null,
|
|
529
|
+
pre_receipt_id: state?.preReceiptId || null,
|
|
530
|
+
tool_count: Array.isArray(state?.tools) ? state.tools.length : 0,
|
|
531
|
+
validation_severity: validation?.validation?.severity || 'pass',
|
|
532
|
+
layer3_pass: validation?.layer3?.pass !== false,
|
|
533
|
+
},
|
|
534
|
+
});
|
|
535
|
+
if (typeof state?.userText === 'string' && state.userText.trim()) {
|
|
536
|
+
await client.gardenTurn(sessionId, state.userText, text, state?.userId || undefined);
|
|
537
|
+
}
|
|
538
|
+
clearTurnState(sessionId);
|
|
539
|
+
emitJson({ continue: true });
|
|
540
|
+
} catch (error) {
|
|
541
|
+
emitJson({
|
|
542
|
+
decision: 'block',
|
|
543
|
+
reason: \`Aria stop hook failed closed: \${error instanceof Error ? error.message : String(error)}\`,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
`;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function buildCodexHooksToml(codexDir: string): string {
|
|
550
|
+
const hooksDir = path.join(codexDir, 'hooks');
|
|
551
|
+
const command = (name: string) => tomlString(`node ${path.join(hooksDir, name)}`);
|
|
552
|
+
return `# BEGIN ARIA MANAGED HOOKS
|
|
553
|
+
[hooks]
|
|
554
|
+
managed_dir = ${tomlString(hooksDir)}
|
|
555
|
+
|
|
556
|
+
[[hooks.UserPromptSubmit]]
|
|
557
|
+
hooks = [{ type = "command", command = ${command('aria-userprompt-submit.mjs')} }]
|
|
558
|
+
|
|
559
|
+
[[hooks.PreToolUse]]
|
|
560
|
+
matcher = ".*"
|
|
561
|
+
hooks = [{ type = "command", command = ${command('aria-pre-tool-use.mjs')} }]
|
|
562
|
+
|
|
563
|
+
[[hooks.PostToolUse]]
|
|
564
|
+
matcher = ".*"
|
|
565
|
+
hooks = [{ type = "command", command = ${command('aria-post-tool-use.mjs')} }]
|
|
566
|
+
|
|
567
|
+
[[hooks.Stop]]
|
|
568
|
+
hooks = [{ type = "command", command = ${command('aria-stop.mjs')} }]
|
|
569
|
+
# END ARIA MANAGED HOOKS
|
|
570
|
+
`;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function installCodexHooksConfig(codexDir: string, logs: string[]): void {
|
|
574
|
+
const configPath = path.join(codexDir, 'config.toml');
|
|
575
|
+
const managedBlock = buildCodexHooksToml(codexDir);
|
|
576
|
+
const markerRegex = /# BEGIN ARIA MANAGED HOOKS[\s\S]*?# END ARIA MANAGED HOOKS\n?/m;
|
|
577
|
+
let text = existsSync(configPath) ? readFileSync(configPath, 'utf8') : '';
|
|
578
|
+
if (markerRegex.test(text)) {
|
|
579
|
+
text = text.replace(markerRegex, managedBlock);
|
|
580
|
+
} else {
|
|
581
|
+
if (text && !text.endsWith('\n')) text += '\n';
|
|
582
|
+
text += `\n${managedBlock}`;
|
|
583
|
+
}
|
|
584
|
+
writeFileSync(configPath, text, { mode: 0o600 });
|
|
585
|
+
logs.push(`Installed Codex managed hooks config → ${configPath}`);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function installCodexHooks(codexDir: string, logs: string[]): void {
|
|
589
|
+
const hooksDir = path.join(codexDir, 'hooks');
|
|
590
|
+
mkdirSync(path.join(hooksDir, 'lib'), { recursive: true, mode: 0o755 });
|
|
591
|
+
|
|
592
|
+
const files: Array<[string, string]> = [
|
|
593
|
+
[path.join(hooksDir, 'lib', 'runtime-client.mjs'), buildCodexHookRuntimeClient()],
|
|
594
|
+
[path.join(hooksDir, 'aria-userprompt-submit.mjs'), buildCodexUserPromptHook()],
|
|
595
|
+
[path.join(hooksDir, 'aria-pre-tool-use.mjs'), buildCodexPreToolHook()],
|
|
596
|
+
[path.join(hooksDir, 'aria-post-tool-use.mjs'), buildCodexPostToolHook()],
|
|
597
|
+
[path.join(hooksDir, 'aria-stop.mjs'), buildCodexStopHook()],
|
|
598
|
+
];
|
|
599
|
+
|
|
600
|
+
for (const [filePath, content] of files) {
|
|
601
|
+
writeFileSync(filePath, content, { mode: 0o755 });
|
|
602
|
+
try { chmodSync(filePath, 0o755); } catch {}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
logs.push(`Installed Codex native hooks → ${hooksDir}`);
|
|
606
|
+
installCodexHooksConfig(codexDir, logs);
|
|
607
|
+
}
|
|
608
|
+
|
|
84
609
|
function buildCodexSkill(_config: AriaConfig): string {
|
|
85
610
|
return `---
|
|
86
611
|
name: aria-http-harness-client
|
|
@@ -111,7 +636,7 @@ That means:
|
|
|
111
636
|
|
|
112
637
|
- the mounted runtime is the universal control plane
|
|
113
638
|
- the shared SDK at \`~/.aria/sdk\` is the universal client substrate
|
|
114
|
-
- Codex
|
|
639
|
+
- Codex native hooks plus the runtime are the hard-gate path for this platform
|
|
115
640
|
|
|
116
641
|
## Preferred Call Paths
|
|
117
642
|
|
|
@@ -153,6 +678,8 @@ function buildCodexAgentsMd(config: AriaConfig): string {
|
|
|
153
678
|
|
|
154
679
|
Automatically injected by @aria_asi/cli. These instructions are always on for Codex sessions.
|
|
155
680
|
|
|
681
|
+
${mustReadIntro('codex')}
|
|
682
|
+
|
|
156
683
|
## Connected Repositories
|
|
157
684
|
${repoList || '(none linked yet)'}
|
|
158
685
|
|
|
@@ -173,15 +700,31 @@ ${schemaText || '(no schema images yet — run \`aria repo scan\`)'}
|
|
|
173
700
|
- If a fix only works by weakening the contract, the mechanism is still broken. Debug the mechanism.
|
|
174
701
|
|
|
175
702
|
## 8-Lens Cognition
|
|
176
|
-
|
|
177
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
-
|
|
183
|
-
|
|
184
|
-
|
|
703
|
+
Use Aria cognition as a work method, not a checklist:
|
|
704
|
+
- Perceive the real substrate: user intent, repo/runtime state, loaded harness packet, memories, prior tool results, and uncertainty.
|
|
705
|
+
- Infer constraints: what must be true, what must not be harmed, what prior doctrine changes, and what evidence is missing.
|
|
706
|
+
- Shape the next action: choose the smallest useful tool call, edit, question, or answer that satisfies those constraints.
|
|
707
|
+
- Improve the artifact: make the tool input, code change, review finding, or prose more specific because of the cognition.
|
|
708
|
+
- Predict the observable result: define what would prove the action or answer succeeded before claiming it.
|
|
709
|
+
- Report evidence: distinguish observed fact, bounded inference, unresolved risk, and next real action.
|
|
710
|
+
|
|
711
|
+
Lens roles for decisions:
|
|
712
|
+
- Truth lens: binds claims to observed evidence and missing evidence.
|
|
713
|
+
- Harm lens: removes or changes actions that could damage state, trust, data, or runtime health.
|
|
714
|
+
- Trust lens: preserves user intent, prior directives, and secret/infra boundaries.
|
|
715
|
+
- Power lens: uses capability to serve the task, not convenience or control.
|
|
716
|
+
- Reflection lens: catches mechanism failure, shortcut pressure, and repeated loops.
|
|
717
|
+
- Context lens: integrates repo patterns, runtime state, and loaded substrate.
|
|
718
|
+
- Impact lens: predicts downstream effects and verification predicates.
|
|
719
|
+
- Beauty lens: prefers the simplest durable artifact that preserves the contract.
|
|
720
|
+
|
|
721
|
+
## Structural Cognition Contract
|
|
722
|
+
- Cognition is not accepted as proof by itself. It must change the next action, tool call, or output claim.
|
|
723
|
+
- Each lens must affect work selection or artifact shape; do not write lenses after the decision is already made.
|
|
724
|
+
- For every non-trivial output, include an \`<applied_cognition>\` block with \`decision_delta\`, \`dominant_domain\`, \`binds_to\`, \`expected_predicate\`, and \`artifact_change\`.
|
|
725
|
+
- Tool-bound cognition must name the exact tool/action it constrains and the measurable predicate that proves the action succeeded.
|
|
726
|
+
- Deploy or destructive actions still require \`<verify>\` and \`<expected>\` blocks before execution.
|
|
727
|
+
- If cognition did not change anything, stop and re-think; \`decision_delta: none\` is treated as performative.
|
|
185
728
|
`;
|
|
186
729
|
}
|
|
187
730
|
|
|
@@ -253,6 +796,10 @@ function installAgentsMd(codexDir: string, config: AriaConfig, logs: string[]):
|
|
|
253
796
|
const agentsPath = path.join(codexDir, 'AGENTS.md');
|
|
254
797
|
writeFileSync(agentsPath, buildCodexAgentsMd(config), { mode: 0o644 });
|
|
255
798
|
logs.push(`Installed Codex AGENTS.md → ${agentsPath}`);
|
|
799
|
+
|
|
800
|
+
const mustReadPath = path.join(codexDir, 'ARIA_MUST_READ.md');
|
|
801
|
+
writeFileSync(mustReadPath, buildMustReadGuide('codex'), { mode: 0o644 });
|
|
802
|
+
logs.push(`Installed Codex must-read guide → ${mustReadPath}`);
|
|
256
803
|
}
|
|
257
804
|
|
|
258
805
|
export async function connectCodex(config: AriaConfig): Promise<string[]> {
|
|
@@ -268,7 +815,9 @@ export async function connectCodex(config: AriaConfig): Promise<string[]> {
|
|
|
268
815
|
installNodePackage(codexDir, logs);
|
|
269
816
|
installAgentsMd(codexDir, config, logs);
|
|
270
817
|
installSkill(codexDir, config, logs);
|
|
818
|
+
installCodexHooks(codexDir, logs);
|
|
271
819
|
installAriaCognitionSkills(codexDir, logs);
|
|
820
|
+
syncDoctrineTriggerMap(logs);
|
|
272
821
|
logs.push(...await connectShell('codex', config));
|
|
273
822
|
return logs;
|
|
274
823
|
}
|