@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.
Files changed (95) hide show
  1. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  2. package/dist/aria-connector/src/connectors/claude-code.js +115 -20
  3. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  4. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
  5. package/dist/aria-connector/src/connectors/codex.js +551 -11
  6. package/dist/aria-connector/src/connectors/codex.js.map +1 -1
  7. package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts +7 -0
  8. package/dist/aria-connector/src/connectors/doctrine-trigger-map.d.ts.map +1 -0
  9. package/dist/aria-connector/src/connectors/doctrine-trigger-map.js +87 -0
  10. package/dist/aria-connector/src/connectors/doctrine-trigger-map.js.map +1 -0
  11. package/dist/aria-connector/src/connectors/must-read.d.ts +4 -0
  12. package/dist/aria-connector/src/connectors/must-read.d.ts.map +1 -0
  13. package/dist/aria-connector/src/connectors/must-read.js +115 -0
  14. package/dist/aria-connector/src/connectors/must-read.js.map +1 -0
  15. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  16. package/dist/aria-connector/src/connectors/opencode.js +27 -9
  17. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  18. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
  19. package/dist/aria-connector/src/connectors/runtime.js +231 -19
  20. package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
  21. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  22. package/dist/aria-connector/src/connectors/shell.js +76 -3
  23. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  24. package/dist/assets/hooks/aria-agent-handoff.mjs +23 -0
  25. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +121 -28
  26. package/dist/assets/hooks/aria-harness-via-sdk.mjs +126 -12
  27. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +35 -0
  28. package/dist/assets/hooks/aria-pre-tool-gate.mjs +383 -93
  29. package/dist/assets/hooks/aria-preprompt-consult.mjs +28 -2
  30. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +93 -16
  31. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +33 -1
  32. package/dist/assets/hooks/aria-stop-gate.mjs +346 -81
  33. package/dist/assets/hooks/doctrine_trigger_map.json +55 -0
  34. package/dist/assets/hooks/lib/canonical-lenses.mjs +6 -5
  35. package/dist/assets/hooks/lib/gate-loop-state.mjs +50 -0
  36. package/dist/assets/hooks/lib/hook-message-window.mjs +121 -0
  37. package/dist/assets/hooks/test-tier-lens-labeling.mjs +26 -58
  38. package/dist/assets/opencode-plugins/harness-gate/index.js +40 -5
  39. package/dist/assets/opencode-plugins/harness-stop/index.js +133 -10
  40. package/dist/runtime/auth-middleware.mjs +251 -0
  41. package/dist/runtime/codex-bridge.mjs +644 -0
  42. package/dist/runtime/discipline/CLAUDE.md +28 -0
  43. package/dist/runtime/discipline/doctrine_trigger_map.json +534 -0
  44. package/dist/runtime/doctrine_trigger_map.json +534 -0
  45. package/dist/runtime/fleet-engine.mjs +231 -0
  46. package/dist/runtime/harness-daemon.mjs +460 -0
  47. package/dist/runtime/manifest.json +1 -1
  48. package/dist/runtime/metering.mjs +100 -0
  49. package/dist/runtime/onboarding-engine.mjs +89 -0
  50. package/dist/runtime/plugin-engine.mjs +196 -0
  51. package/dist/runtime/sdk/BUNDLED.json +1 -1
  52. package/dist/runtime/sdk/index.d.ts +12 -0
  53. package/dist/runtime/sdk/index.js +120 -14
  54. package/dist/runtime/sdk/index.js.map +1 -1
  55. package/dist/runtime/service.mjs +1140 -48
  56. package/dist/runtime/workflow-engine.mjs +322 -0
  57. package/dist/sdk/BUNDLED.json +1 -1
  58. package/dist/sdk/index.d.ts +12 -0
  59. package/dist/sdk/index.js +120 -14
  60. package/dist/sdk/index.js.map +1 -1
  61. package/hooks/aria-agent-handoff.mjs +23 -0
  62. package/hooks/aria-cognition-substrate-binding.mjs +121 -28
  63. package/hooks/aria-harness-via-sdk.mjs +126 -12
  64. package/hooks/aria-pre-emit-dryrun.mjs +35 -0
  65. package/hooks/aria-pre-tool-gate.mjs +383 -93
  66. package/hooks/aria-preprompt-consult.mjs +28 -2
  67. package/hooks/aria-preturn-memory-gate.mjs +93 -16
  68. package/hooks/aria-repo-doctrine-gate.mjs +33 -1
  69. package/hooks/aria-stop-gate.mjs +346 -81
  70. package/hooks/doctrine_trigger_map.json +55 -0
  71. package/hooks/lib/canonical-lenses.mjs +6 -5
  72. package/hooks/lib/gate-loop-state.mjs +50 -0
  73. package/hooks/lib/hook-message-window.mjs +121 -0
  74. package/hooks/test-tier-lens-labeling.mjs +26 -58
  75. package/opencode-plugins/harness-gate/index.js +40 -5
  76. package/opencode-plugins/harness-stop/index.js +133 -10
  77. package/package.json +1 -1
  78. package/runtime-src/auth-middleware.mjs +251 -0
  79. package/runtime-src/codex-bridge.mjs +644 -0
  80. package/runtime-src/fleet-engine.mjs +231 -0
  81. package/runtime-src/harness-daemon.mjs +460 -0
  82. package/runtime-src/metering.mjs +100 -0
  83. package/runtime-src/onboarding-engine.mjs +89 -0
  84. package/runtime-src/plugin-engine.mjs +196 -0
  85. package/runtime-src/service.mjs +1140 -48
  86. package/runtime-src/workflow-engine.mjs +322 -0
  87. package/scripts/bundle-sdk.mjs +5 -0
  88. package/src/connectors/claude-code.ts +126 -20
  89. package/src/connectors/codex.ts +559 -10
  90. package/src/connectors/doctrine-trigger-map.ts +112 -0
  91. package/src/connectors/must-read.ts +117 -0
  92. package/src/connectors/opencode.ts +28 -9
  93. package/src/connectors/runtime.ts +241 -21
  94. package/src/connectors/shell.ts +78 -3
  95. package/dist/cli-0.2.0.tgz +0 -0
@@ -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 still does not expose Claude-style native user hook interception, so explicit runtime or SDK calls are still required for hard guarantees on this platform
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
- Apply multi-perspective analysis for every decision:
177
- - Truth lens: Is this factually correct?
178
- - Harm lens: Could this cause damage?
179
- - Trust lens: Does this maintain sacred trust?
180
- - Power lens: Am I using capability responsibly?
181
- - Reflection lens: Have I thought deeply enough?
182
- - Context lens: Do I have full context?
183
- - Impact lens: What are second-order effects?
184
- - Beauty lens: Is this elegant and sustainable?
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
  }