@aria_asi/cli 0.2.36 → 0.2.37

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 (195) hide show
  1. package/CLIENT-ONBOARDING.md +4 -2
  2. package/bin/aria.js +11 -7
  3. package/dist/aria-connector/src/auth.d.ts +14 -0
  4. package/dist/aria-connector/src/auth.d.ts.map +1 -1
  5. package/dist/aria-connector/src/auth.js +103 -1
  6. package/dist/aria-connector/src/auth.js.map +1 -1
  7. package/dist/aria-connector/src/chat.d.ts.map +1 -1
  8. package/dist/aria-connector/src/chat.js +13 -8
  9. package/dist/aria-connector/src/chat.js.map +1 -1
  10. package/dist/aria-connector/src/config.d.ts +6 -1
  11. package/dist/aria-connector/src/config.d.ts.map +1 -1
  12. package/dist/aria-connector/src/config.js.map +1 -1
  13. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  14. package/dist/aria-connector/src/connectors/claude-code.js +50 -6
  15. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  16. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -1
  17. package/dist/aria-connector/src/connectors/codex.js +310 -10
  18. package/dist/aria-connector/src/connectors/codex.js.map +1 -1
  19. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  20. package/dist/aria-connector/src/connectors/opencode.js +35 -11
  21. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  22. package/dist/aria-connector/src/connectors/repo-guard.d.ts +10 -0
  23. package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -1
  24. package/dist/aria-connector/src/connectors/repo-guard.js +110 -164
  25. package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -1
  26. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -1
  27. package/dist/aria-connector/src/connectors/runtime.js +17 -7
  28. package/dist/aria-connector/src/connectors/runtime.js.map +1 -1
  29. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  30. package/dist/aria-connector/src/connectors/shell.js +12 -8
  31. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  32. package/dist/aria-connector/src/harness-client.d.ts +3 -1
  33. package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
  34. package/dist/aria-connector/src/harness-client.js +7 -20
  35. package/dist/aria-connector/src/harness-client.js.map +1 -1
  36. package/dist/aria-connector/src/model-context.d.ts.map +1 -1
  37. package/dist/aria-connector/src/model-context.js +5 -0
  38. package/dist/aria-connector/src/model-context.js.map +1 -1
  39. package/dist/aria-connector/src/providers/types.d.ts +1 -1
  40. package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
  41. package/dist/aria-connector/src/providers/xai.d.ts +3 -0
  42. package/dist/aria-connector/src/providers/xai.d.ts.map +1 -0
  43. package/dist/aria-connector/src/providers/xai.js +40 -0
  44. package/dist/aria-connector/src/providers/xai.js.map +1 -0
  45. package/dist/aria-connector/src/setup-wizard.js +1 -0
  46. package/dist/aria-connector/src/setup-wizard.js.map +1 -1
  47. package/dist/aria-connector/src/types.d.ts +2 -0
  48. package/dist/aria-connector/src/types.d.ts.map +1 -1
  49. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +51 -9
  50. package/dist/assets/hooks/aria-first-class-coach.mjs +129 -0
  51. package/dist/assets/hooks/aria-harness-via-sdk.mjs +33 -6
  52. package/dist/assets/hooks/aria-pre-tool-gate.mjs +33 -8
  53. package/dist/assets/hooks/aria-preprompt-consult.mjs +5 -6
  54. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +5 -0
  55. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +15 -0
  56. package/dist/assets/hooks/aria-stop-gate.mjs +125 -17
  57. package/dist/assets/hooks/doctrine_trigger_map.json +11 -0
  58. package/dist/assets/hooks/lib/emergency-gateoff-impl.mjs +39 -0
  59. package/dist/assets/hooks/lib/emergency-gateoff.mjs +6 -0
  60. package/dist/assets/hooks/lib/first-class-coach.mjs +755 -0
  61. package/dist/assets/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
  62. package/dist/assets/hooks/lib/skill-autoload-gate.mjs +1 -14
  63. package/dist/assets/opencode-plugins/harness-context/auth-token.mjs +126 -0
  64. package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +62 -22
  65. package/dist/assets/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
  66. package/dist/assets/opencode-plugins/harness-gate/index.js +87 -27
  67. package/dist/assets/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
  68. package/dist/assets/opencode-plugins/harness-outcome/index.js +29 -24
  69. package/dist/assets/opencode-plugins/harness-stop/index.js +229 -68
  70. package/dist/assets/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
  71. package/dist/runtime/auth-token.mjs +121 -0
  72. package/dist/runtime/coach-kernel.mjs +371 -0
  73. package/dist/runtime/codex-bridge.mjs +440 -69
  74. package/dist/runtime/discipline/doctrine_trigger_map.json +11 -0
  75. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +18 -0
  76. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
  77. package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
  78. package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
  79. package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
  80. package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
  81. package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
  82. package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +18 -0
  83. package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +18 -0
  84. package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
  85. package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +18 -0
  86. package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
  87. package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +18 -0
  88. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +18 -0
  89. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +18 -0
  90. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +18 -0
  91. package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +18 -0
  92. package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +18 -0
  93. package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +18 -0
  94. package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +18 -0
  95. package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +18 -0
  96. package/dist/runtime/doctrine_trigger_map.json +11 -0
  97. package/dist/runtime/hooks/aria-cognition-substrate-binding.mjs +51 -9
  98. package/dist/runtime/hooks/aria-first-class-coach.mjs +129 -0
  99. package/dist/runtime/hooks/aria-harness-via-sdk.mjs +33 -6
  100. package/dist/runtime/hooks/aria-pre-tool-gate.mjs +33 -8
  101. package/dist/runtime/hooks/aria-preprompt-consult.mjs +5 -6
  102. package/dist/runtime/hooks/aria-preturn-memory-gate.mjs +5 -0
  103. package/dist/runtime/hooks/aria-repo-doctrine-gate.mjs +15 -0
  104. package/dist/runtime/hooks/aria-stop-gate.mjs +125 -17
  105. package/dist/runtime/hooks/doctrine_trigger_map.json +11 -0
  106. package/dist/runtime/hooks/lib/emergency-gateoff-impl.mjs +39 -0
  107. package/dist/runtime/hooks/lib/emergency-gateoff.mjs +6 -0
  108. package/dist/runtime/hooks/lib/first-class-coach.mjs +755 -0
  109. package/dist/runtime/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
  110. package/dist/runtime/hooks/lib/skill-autoload-gate.mjs +1 -14
  111. package/dist/runtime/local-phase.mjs +8 -0
  112. package/dist/runtime/manifest.json +2 -2
  113. package/dist/runtime/provider-proxy.mjs +136 -33
  114. package/dist/runtime/sdk/BUNDLED.json +2 -2
  115. package/dist/runtime/sdk/auth.d.ts +17 -0
  116. package/dist/runtime/sdk/auth.js +158 -0
  117. package/dist/runtime/sdk/auth.js.map +1 -0
  118. package/dist/runtime/sdk/index.d.ts +8 -1
  119. package/dist/runtime/sdk/index.js +15 -1
  120. package/dist/runtime/sdk/index.js.map +1 -1
  121. package/dist/runtime/service.mjs +1711 -74
  122. package/dist/runtime/task-project-ledger.mjs +290 -0
  123. package/dist/sdk/BUNDLED.json +2 -2
  124. package/dist/sdk/auth.d.ts +17 -0
  125. package/dist/sdk/auth.js +158 -0
  126. package/dist/sdk/auth.js.map +1 -0
  127. package/dist/sdk/index.d.ts +8 -1
  128. package/dist/sdk/index.js +15 -1
  129. package/dist/sdk/index.js.map +1 -1
  130. package/hooks/aria-cognition-substrate-binding.mjs +51 -9
  131. package/hooks/aria-first-class-coach.mjs +129 -0
  132. package/hooks/aria-harness-via-sdk.mjs +33 -6
  133. package/hooks/aria-pre-tool-gate.mjs +33 -8
  134. package/hooks/aria-preprompt-consult.mjs +5 -6
  135. package/hooks/aria-preturn-memory-gate.mjs +5 -0
  136. package/hooks/aria-repo-doctrine-gate.mjs +15 -0
  137. package/hooks/aria-stop-gate.mjs +125 -17
  138. package/hooks/doctrine_trigger_map.json +11 -0
  139. package/hooks/lib/emergency-gateoff-impl.mjs +39 -0
  140. package/hooks/lib/emergency-gateoff.mjs +6 -0
  141. package/hooks/lib/first-class-coach.mjs +755 -0
  142. package/hooks/lib/skill-autoload-gate-impl.mjs +103 -0
  143. package/hooks/lib/skill-autoload-gate.mjs +1 -14
  144. package/opencode-plugins/harness-context/auth-token.mjs +126 -0
  145. package/opencode-plugins/harness-context/inject-context.mjs +62 -22
  146. package/opencode-plugins/harness-context/task-project-ledger.mjs +290 -0
  147. package/opencode-plugins/harness-gate/index.js +87 -27
  148. package/opencode-plugins/harness-gate/lib/skill-autoload-gate.js +1 -14
  149. package/opencode-plugins/harness-outcome/index.js +29 -24
  150. package/opencode-plugins/harness-stop/index.js +229 -68
  151. package/opencode-plugins/harness-stop/lib/skill-autoload-gate.js +1 -14
  152. package/package.json +8 -2
  153. package/runtime-src/auth-token.mjs +121 -0
  154. package/runtime-src/coach-kernel.mjs +371 -0
  155. package/runtime-src/codex-bridge.mjs +440 -69
  156. package/runtime-src/local-phase.mjs +8 -0
  157. package/runtime-src/provider-proxy.mjs +136 -33
  158. package/runtime-src/service.mjs +1711 -74
  159. package/scripts/bundle-sdk.mjs +8 -0
  160. package/scripts/check-client-compatibility.mjs +422 -0
  161. package/scripts/check-coach-kernel.mjs +204 -0
  162. package/scripts/check-managed-runtime-ledger.mjs +107 -0
  163. package/scripts/check-opencode-config-contract.mjs +78 -0
  164. package/scripts/check-quality-ledger.mjs +121 -0
  165. package/scripts/self-test-harness-gates.mjs +179 -11
  166. package/scripts/self-test-repo-guard.mjs +38 -0
  167. package/scripts/validate-skill-prompts.mjs +14 -1
  168. package/skills/aria-cognition/aria-essence/SKILL.md +18 -0
  169. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +18 -0
  170. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +18 -0
  171. package/skills/aria-cognition/forge-quality-rules/SKILL.md +18 -0
  172. package/skills/aria-cognition/ghazali-8lens/SKILL.md +18 -0
  173. package/skills/aria-cognition/istiqra-induction/SKILL.md +18 -0
  174. package/skills/aria-cognition/ladunni-22/SKILL.md +18 -0
  175. package/skills/aria-cognition/mizan/SKILL.md +18 -0
  176. package/skills/aria-cognition/nadia/SKILL.md +18 -0
  177. package/skills/aria-cognition/nadia-psi/SKILL.md +18 -0
  178. package/skills/aria-cognition/predictor/SKILL.md +18 -0
  179. package/skills/aria-cognition/qiyas-analogy/SKILL.md +18 -0
  180. package/skills/aria-cognition/soul-domains/SKILL.md +18 -0
  181. package/src/auth.ts +136 -1
  182. package/src/chat.ts +13 -8
  183. package/src/config.ts +6 -1
  184. package/src/connectors/claude-code.ts +62 -18
  185. package/src/connectors/codex.ts +308 -10
  186. package/src/connectors/opencode.ts +35 -12
  187. package/src/connectors/repo-guard.ts +117 -172
  188. package/src/connectors/runtime.ts +19 -7
  189. package/src/connectors/shell.ts +12 -8
  190. package/src/harness-client.ts +8 -22
  191. package/src/model-context.ts +6 -0
  192. package/src/providers/types.ts +1 -1
  193. package/src/providers/xai.ts +55 -0
  194. package/src/setup-wizard.ts +1 -0
  195. package/src/types.ts +2 -0
@@ -24,6 +24,15 @@ function packageSdkDir(): string {
24
24
  return path.resolve(here, '..', '..', '..', 'sdk');
25
25
  }
26
26
 
27
+ function packageTaskProjectLedgerHelperPath(): string {
28
+ const here = path.dirname(fileURLToPath(import.meta.url));
29
+ const candidates = [
30
+ path.resolve(here, '..', '..', '..', '..', 'opencode-plugins', 'harness-context', 'task-project-ledger.mjs'),
31
+ path.resolve(here, '..', '..', '..', 'assets', 'opencode-plugins', 'harness-context', 'task-project-ledger.mjs'),
32
+ ];
33
+ return candidates.find((candidate) => existsSync(candidate)) || candidates[0];
34
+ }
35
+
27
36
  function installSdk(codexDir: string, logs: string[]): void {
28
37
  const sdkSrc = packageSdkDir();
29
38
  if (!existsSync(sdkSrc)) {
@@ -95,6 +104,9 @@ import { createHash, randomUUID } from 'node:crypto';
95
104
  import { homedir } from 'node:os';
96
105
  import path from 'node:path';
97
106
  import { HTTPHarnessClient } from '@aria_asi/harness-http-client';
107
+ import { updateTaskProjectLedger, evaluateTaskProjectClaim, recordBlockedTaskProjectClaim } from './task-project-ledger.mjs';
108
+
109
+ export { updateTaskProjectLedger, evaluateTaskProjectClaim, recordBlockedTaskProjectClaim };
98
110
 
99
111
  const HOME = homedir();
100
112
  const DEFAULT_RUNTIME_URL = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\\/+$/, '');
@@ -102,7 +114,7 @@ const TURN_STATE_DIR = path.join(HOME, '.codex', 'tmp', 'aria-hook-turn-state');
102
114
  const GOVERNANCE_GATE_PATH = path.join(HOME, '.aria', 'bin', 'aria-governance-gate');
103
115
 
104
116
  function readToken() {
105
- const envToken = process.env.ARIA_API_KEY || process.env.ARIA_MASTER_TOKEN || process.env.OPENAI_API_KEY;
117
+ const envToken = process.env.ARIA_HARNESS_TOKEN || process.env.ARIA_API_KEY || process.env.ARIA_MASTER_TOKEN;
106
118
  if (envToken) return envToken;
107
119
  const ownerPath = path.join(HOME, '.aria', 'owner-token');
108
120
  if (existsSync(ownerPath)) return readFileSync(ownerPath, 'utf8').trim();
@@ -264,6 +276,24 @@ export async function runtimePost(route, body = {}) {
264
276
  return response.json();
265
277
  }
266
278
 
279
+ export async function recordCoachPhase(phase, body = {}) {
280
+ try {
281
+ return await runtimePost('/coach/phase', {
282
+ phase,
283
+ surface: 'codex-hooks',
284
+ lane: 'codex_native_hooks',
285
+ ...body,
286
+ });
287
+ } catch (error) {
288
+ return {
289
+ ok: false,
290
+ permitted: true,
291
+ decision: 'warn_operator_only',
292
+ error: error instanceof Error ? error.message : String(error),
293
+ };
294
+ }
295
+ }
296
+
267
297
  export function classifyAction(event) {
268
298
  const toolName = String(event?.tool_name || event?.toolName || '').trim();
269
299
  const toolCommand = String(
@@ -307,6 +337,23 @@ export function extractAssistantText(event) {
307
337
  );
308
338
  }
309
339
 
340
+ function normalizeValidationIssue(issue) {
341
+ const raw = String(issue || '').replace(/\\s+/g, ' ').trim();
342
+ if (!raw) return '';
343
+ if (/No <cognition>/i.test(raw)) return 'missing readable cognition block';
344
+ if (/missing\\s+<applied_cognition>/i.test(raw)) return 'missing applied cognition contract';
345
+ if (/qualitative_drift/i.test(raw)) return 'qualitative drift language needs a measurable predicate';
346
+ if (/premature_task_closeout|feedback_no_premature_task_closeout|done\\|complete\\|completed\\|ready\\|verified\\|fixed/i.test(raw)) {
347
+ return 'completion/readiness claim conflicts with unresolved blocker state';
348
+ }
349
+ if (/\\(\\?:/.test(raw)) return 'doctrine matcher triggered; re-author with the named doctrine and concrete evidence';
350
+ return raw.slice(0, 600);
351
+ }
352
+
353
+ function uniqueStrings(values) {
354
+ return Array.from(new Set(values.map(normalizeValidationIssue).filter(Boolean)));
355
+ }
356
+
310
357
  export function formatValidationFailure(result) {
311
358
  const parts = [];
312
359
  if (Array.isArray(result?.validation?.violations) && result.validation.violations.length) {
@@ -318,7 +365,31 @@ export function formatValidationFailure(result) {
318
365
  if (!parts.length && typeof result?.summary === 'string' && result.summary.trim()) {
319
366
  parts.push(result.summary.trim());
320
367
  }
321
- return parts.join(' | ') || 'Aria validation failed.';
368
+ return uniqueStrings(parts).join(' | ') || 'Aria validation failed.';
369
+ }
370
+
371
+ export function isAriaControlBlock(text) {
372
+ return /^(?:ARIA CODEX RECOVERY CONTRACT|Aria runtime blocked final output for this Codex turn\\.|Aria stop gate blocked output:|Aria task\\/project ledger blocked output claim\\.|Aria stop hook failed closed:)/i.test(String(text || '').trim());
373
+ }
374
+
375
+ export function formatCodexRecoveryBlock({ surface = 'codex', reason = '', issues = [], next = '' } = {}) {
376
+ const blockers = uniqueStrings([reason, ...issues]);
377
+ return [
378
+ 'ARIA CODEX RECOVERY CONTRACT',
379
+ 'surface: ' + surface,
380
+ 'status: output held for re-authoring',
381
+ '',
382
+ 'Observed blockers:',
383
+ ...(blockers.length ? blockers.map((item) => '- ' + item) : ['- Aria validation failed.']),
384
+ '',
385
+ 'Recovery contract:',
386
+ '1. Do not retry the same blocked text.',
387
+ '2. Re-author the answer from the user request, not from this block report.',
388
+ '3. Include <cognition> and <applied_cognition> when the answer is non-trivial.',
389
+ '4. Use bounded status language when evidence is missing; do not use completion/readiness claims without proof.',
390
+ '5. Name a measurable verification predicate, or explicitly state that verification has not run.',
391
+ next || '6. Re-submit the corrected answer; if the same blocker repeats twice, escalate with this full recovery contract.',
392
+ ].join('\\n');
322
393
  }
323
394
 
324
395
  export function emitJson(payload, code = 0) {
@@ -355,9 +426,12 @@ import {
355
426
  makeEvidenceRef,
356
427
  readEventFromStdin,
357
428
  runtimePost,
429
+ recordCoachPhase,
358
430
  loadTurnState,
359
431
  saveTurnState,
360
432
  runGovernanceGate,
433
+ updateTaskProjectLedger,
434
+ formatCodexRecoveryBlock,
361
435
  emitJson,
362
436
  } from './lib/runtime-client.mjs';
363
437
 
@@ -368,8 +442,21 @@ const sessionId = inferSessionId(event);
368
442
  const userId = inferUserId(event);
369
443
  const priorState = loadTurnState(sessionId);
370
444
  const traceId = ensureTraceId(priorState);
445
+ const ledgerResult = updateTaskProjectLedger({
446
+ platform: 'codex',
447
+ phase: 'pre_prompt_injection',
448
+ source: 'codex-userprompt-hook',
449
+ event: { ...event, prompt: userText, sessionId, cwd: process.cwd() },
450
+ });
371
451
 
372
452
  try {
453
+ await recordCoachPhase('pre_turn', {
454
+ requestId: traceId,
455
+ sessionId,
456
+ text: userText,
457
+ evidenceRefs: [makeEvidenceRef('user_input', userText, { sessionId, traceId, platform: 'codex' })],
458
+ metadata: { source: 'codex-userprompt-hook', userId },
459
+ });
373
460
  const packet = await client.getHarnessPacket({
374
461
  sessionId,
375
462
  platform: 'codex',
@@ -404,11 +491,19 @@ try {
404
491
  evidenceRefs: [packetRef],
405
492
  },
406
493
  });
494
+ await recordCoachPhase('post_cognition', {
495
+ requestId: traceId,
496
+ sessionId,
497
+ text: userText,
498
+ evidenceRefs: [packetRef, makeEvidenceRef('mizan_pre_receipt', result?.receipt || null, { sessionId, traceId })],
499
+ metadata: { source: 'codex-userprompt-hook', pre_receipt_id: result?.receipt?.receiptId || null },
500
+ });
407
501
  saveTurnState(sessionId, {
408
502
  traceId,
409
503
  userId,
410
504
  userText,
411
505
  preReceiptId: result?.receipt?.receiptId || null,
506
+ taskProjectLedgerId: ledgerResult.ledger.ledgerId,
412
507
  packetTimestamp: packet?.timestamp || null,
413
508
  packetRef,
414
509
  lastEvent: 'UserPromptSubmit',
@@ -417,7 +512,11 @@ try {
417
512
  } catch (error) {
418
513
  emitJson({
419
514
  decision: 'block',
420
- reason: \`Aria cognition gate failed before turn start: \${error instanceof Error ? error.message : String(error)}\`,
515
+ reason: formatCodexRecoveryBlock({
516
+ surface: 'codex-userprompt',
517
+ reason: error instanceof Error ? error.message : String(error),
518
+ next: '6. Re-open the prompt substrate, repair the cognition gate blocker, then submit the prompt again.',
519
+ }),
421
520
  });
422
521
  }
423
522
  `;
@@ -433,8 +532,11 @@ import {
433
532
  readEventFromStdin,
434
533
  loadTurnState,
435
534
  makeEvidenceRef,
535
+ recordCoachPhase,
436
536
  saveTurnState,
437
537
  runGovernanceGate,
538
+ updateTaskProjectLedger,
539
+ formatCodexRecoveryBlock,
438
540
  emitJson,
439
541
  } from './lib/runtime-client.mjs';
440
542
 
@@ -449,17 +551,52 @@ try {
449
551
  if (!state?.preReceiptId && !state?.userText) {
450
552
  emitJson({
451
553
  decision: 'block',
452
- 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.',
554
+ reason: formatCodexRecoveryBlock({
555
+ surface: 'codex-pre-tool',
556
+ reason: 'this turn has no pre-turn Mizan receipt',
557
+ next: '6. Re-submit the prompt so cognition is established before tool use, then request the tool again.',
558
+ }),
559
+ });
560
+ }
561
+ const toolName = String(event?.tool_name || event?.toolName || '').trim() || null;
562
+ const requestRef = makeEvidenceRef('codex_tool_request', { action, toolName, target }, { sessionId });
563
+ const coach = await recordCoachPhase('pre_tool', {
564
+ requestId: state?.traceId || sessionId,
565
+ sessionId,
566
+ text: target,
567
+ action,
568
+ target,
569
+ evidenceRefs: [requestRef],
570
+ metadata: { source: 'codex-pre-tool-hook', toolName, requireVerify: action === 'deploy' || action === 'delete' },
571
+ });
572
+ if (coach?.permitted === false) {
573
+ emitJson({
574
+ decision: 'block',
575
+ reason: formatCodexRecoveryBlock({
576
+ surface: 'codex-pre-tool-coach',
577
+ reason: coach.clientMessage || 'Coach Kernel denied ' + action,
578
+ next: '6. Add the required evidence/cognition contract, then request the tool again.',
579
+ }),
453
580
  });
454
581
  }
455
582
  const actionCheck = await client.checkAction(action, target);
456
583
  if (actionCheck?.allowed === false) {
457
584
  emitJson({
458
585
  decision: 'block',
459
- reason: actionCheck?.reason || \`Aria denied \${action}\`,
586
+ reason: formatCodexRecoveryBlock({
587
+ surface: 'codex-pre-tool-action',
588
+ reason: actionCheck?.reason || \`Aria denied \${action}\`,
589
+ next: '6. Add the required verification/cognition contract for the action, then request the tool again.',
590
+ }),
460
591
  });
461
592
  }
462
- const toolName = String(event?.tool_name || event?.toolName || '').trim() || null;
593
+ updateTaskProjectLedger({
594
+ platform: 'codex',
595
+ phase: 'pre_tool',
596
+ source: 'codex-pre-tool-hook',
597
+ event: { ...event, sessionId, cwd: process.cwd() },
598
+ evidence: { action_ref: requestRef },
599
+ });
463
600
  runGovernanceGate({
464
601
  sessionId,
465
602
  sourceRuntime: 'codex',
@@ -469,7 +606,7 @@ try {
469
606
  toolName,
470
607
  isDeploy: action === 'deploy',
471
608
  isMutation: action === 'write' || action === 'delete',
472
- evidence: makeEvidenceRef('codex_tool_request', { action, toolName, target }, { sessionId }),
609
+ evidence: requestRef,
473
610
  });
474
611
  const tools = Array.isArray(state?.tools) ? state.tools.slice(-24) : [];
475
612
  tools.push({
@@ -487,7 +624,10 @@ try {
487
624
  } catch (error) {
488
625
  emitJson({
489
626
  decision: 'block',
490
- reason: \`Aria pre-tool hook failed closed: \${error instanceof Error ? error.message : String(error)}\`,
627
+ reason: formatCodexRecoveryBlock({
628
+ surface: 'codex-pre-tool-hook',
629
+ reason: error instanceof Error ? error.message : String(error),
630
+ }),
491
631
  });
492
632
  }
493
633
  `;
@@ -500,7 +640,9 @@ import {
500
640
  readEventFromStdin,
501
641
  loadTurnState,
502
642
  makeEvidenceRef,
643
+ recordCoachPhase,
503
644
  saveTurnState,
645
+ updateTaskProjectLedger,
504
646
  } from './lib/runtime-client.mjs';
505
647
 
506
648
  const event = readEventFromStdin();
@@ -514,6 +656,20 @@ try {
514
656
  toolName: event?.tool_name || event?.toolName || null,
515
657
  });
516
658
  const toolOutputs = Array.isArray(state?.toolOutputs) ? state.toolOutputs.slice(-24) : [];
659
+ const verificationText = JSON.stringify(event).slice(0, 8000);
660
+ const verification = !event?.error && /\\b(?:npm\\s+run\\s+(?:check|test|build|lint|typecheck)|(?:npx\\s+)?(?:jest|vitest|tsc|eslint)|check:|test:|build:|passed|exit\\s*0|0\\s*failures?)\\b/i.test(verificationText);
661
+ await recordCoachPhase('post_tool', {
662
+ requestId: state?.traceId || sessionId,
663
+ sessionId,
664
+ text: verificationText,
665
+ evidenceRefs: [evidenceRef],
666
+ metadata: {
667
+ source: 'codex-post-tool-hook',
668
+ toolName: event?.tool_name || event?.toolName || null,
669
+ verification,
670
+ error: event?.error || null,
671
+ },
672
+ });
517
673
  toolOutputs.push({
518
674
  at: new Date().toISOString(),
519
675
  toolName: event?.tool_name || event?.toolName || null,
@@ -524,6 +680,17 @@ try {
524
680
  toolOutputs,
525
681
  lastEvent: 'PostToolUse',
526
682
  });
683
+ updateTaskProjectLedger({
684
+ platform: 'codex',
685
+ phase: 'post_tool',
686
+ source: 'codex-post-tool-hook',
687
+ event: { ...event, sessionId, cwd: process.cwd() },
688
+ evidence: {
689
+ outcome_ref: evidenceRef,
690
+ verification,
691
+ commandResult: verification ? 'success' : '',
692
+ },
693
+ });
527
694
  process.exit(0);
528
695
  } catch {
529
696
  process.exit(0);
@@ -538,11 +705,17 @@ import {
538
705
  extractAssistantText,
539
706
  readEventFromStdin,
540
707
  runtimePost,
708
+ recordCoachPhase,
541
709
  loadTurnState,
542
710
  makeEvidenceRef,
543
711
  clearTurnState,
544
712
  formatValidationFailure,
713
+ formatCodexRecoveryBlock,
714
+ isAriaControlBlock,
545
715
  runGovernanceGate,
716
+ updateTaskProjectLedger,
717
+ evaluateTaskProjectClaim,
718
+ recordBlockedTaskProjectClaim,
546
719
  emitJson,
547
720
  } from './lib/runtime-client.mjs';
548
721
 
@@ -552,11 +725,58 @@ const state = loadTurnState(sessionId);
552
725
  const text = extractAssistantText(event);
553
726
  const outputRef = makeEvidenceRef('assistant_output', text, { sessionId, traceId: state?.traceId || null });
554
727
  const toolRefs = Array.isArray(state?.toolOutputs) ? state.toolOutputs.map((entry) => entry.evidenceRef).filter(Boolean) : [];
728
+ const ledgerResult = updateTaskProjectLedger({
729
+ platform: 'codex',
730
+ phase: 'stop',
731
+ source: 'codex-stop-hook',
732
+ event: { ...event, text, sessionId, cwd: process.cwd() },
733
+ });
555
734
 
556
735
  try {
557
736
  if (!text) {
558
737
  emitJson({ continue: true });
559
738
  }
739
+ if (isAriaControlBlock(text)) {
740
+ clearTurnState(sessionId);
741
+ emitJson({ continue: true });
742
+ }
743
+ const postGenerationCoach = await recordCoachPhase('post_generation', {
744
+ requestId: state?.traceId || sessionId,
745
+ sessionId,
746
+ text,
747
+ evidenceRefs: [outputRef],
748
+ metadata: { source: 'codex-stop-hook', requireCognitionBlock: false, requireAppliedCognition: false },
749
+ });
750
+ if (postGenerationCoach?.permitted === false) {
751
+ emitJson({
752
+ decision: 'block',
753
+ reason: formatCodexRecoveryBlock({
754
+ surface: 'codex-stop-coach-post-generation',
755
+ reason: postGenerationCoach.clientMessage || 'Coach Kernel held Codex output before validation.',
756
+ }),
757
+ });
758
+ }
759
+ const ledgerClaim = evaluateTaskProjectClaim({ text, ledger: ledgerResult.ledger });
760
+ if (!ledgerClaim.ok) {
761
+ recordBlockedTaskProjectClaim({
762
+ ledger: ledgerResult.ledger,
763
+ paths: ledgerResult.paths,
764
+ text,
765
+ evaluation: ledgerClaim,
766
+ });
767
+ emitJson({
768
+ decision: 'block',
769
+ reason: formatCodexRecoveryBlock({
770
+ surface: 'codex-stop-ledger',
771
+ reason: 'task/project ledger rejected an output claim',
772
+ issues: [
773
+ \`missing readiness gates: \${ledgerClaim.missingReadinessGates.join(', ') || 'none'}\`,
774
+ \`open blockers: \${ledgerClaim.openBlockers.length}\`,
775
+ ],
776
+ next: '6. Record real verification evidence or emit bounded status without completion/readiness language, then re-submit.',
777
+ }),
778
+ });
779
+ }
560
780
  runGovernanceGate({
561
781
  sessionId,
562
782
  sourceRuntime: 'codex',
@@ -579,10 +799,31 @@ try {
579
799
  requireCognitionBlock: false,
580
800
  runLayer3: true,
581
801
  });
802
+ const preOutputCoach = await recordCoachPhase('pre_output', {
803
+ requestId: state?.traceId || sessionId,
804
+ sessionId,
805
+ text,
806
+ validation: validation?.validation || null,
807
+ layer3: validation?.layer3 || null,
808
+ evidenceRefs: [outputRef, makeEvidenceRef('runtime_validation', validation, { sessionId, traceId: state?.traceId || null })],
809
+ metadata: { source: 'codex-stop-hook', requireCognitionBlock: false, requireAppliedCognition: false },
810
+ });
811
+ if (preOutputCoach?.permitted === false) {
812
+ emitJson({
813
+ decision: 'block',
814
+ reason: formatCodexRecoveryBlock({
815
+ surface: 'codex-stop-coach-pre-output',
816
+ reason: preOutputCoach.clientMessage || 'Coach Kernel held Codex output before release.',
817
+ }),
818
+ });
819
+ }
582
820
  if (validation?.pass === false || validation?.validation?.passed === false || validation?.layer3?.pass === false) {
583
821
  emitJson({
584
822
  decision: 'block',
585
- reason: \`Aria stop gate blocked output: \${formatValidationFailure(validation)}\`,
823
+ reason: formatCodexRecoveryBlock({
824
+ surface: 'codex-stop-output',
825
+ reason: formatValidationFailure(validation),
826
+ }),
586
827
  });
587
828
  }
588
829
  const post = await runtimePost('/mizan/post', {
@@ -607,6 +848,18 @@ try {
607
848
  },
608
849
  parentReceiptId: state?.preReceiptId || null,
609
850
  });
851
+ await recordCoachPhase('post_output', {
852
+ requestId: state?.traceId || sessionId,
853
+ sessionId,
854
+ text,
855
+ validation: validation?.validation || null,
856
+ layer3: validation?.layer3 || null,
857
+ evidenceRefs: [
858
+ outputRef,
859
+ makeEvidenceRef('mizan_post_receipt', post?.receipt || null, { sessionId, traceId: state?.traceId || null }),
860
+ ],
861
+ metadata: { source: 'codex-stop-hook', requireCognitionBlock: false, requireAppliedCognition: false },
862
+ });
610
863
  await runtimePost('/decision/log', {
611
864
  session_id: sessionId,
612
865
  decision_type: 'operational',
@@ -643,12 +896,51 @@ try {
643
896
  },
644
897
  });
645
898
  }
899
+ updateTaskProjectLedger({
900
+ platform: 'codex',
901
+ phase: 'post_turn',
902
+ source: 'codex-stop-hook',
903
+ event: { ...event, text, sessionId, cwd: process.cwd() },
904
+ evidence: {
905
+ post_turn: true,
906
+ output_ref: outputRef,
907
+ verification: false,
908
+ },
909
+ });
910
+ const releaseCoach = await recordCoachPhase('claim_or_release', {
911
+ requestId: state?.traceId || sessionId,
912
+ sessionId,
913
+ text,
914
+ validation: validation?.validation || null,
915
+ layer3: validation?.layer3 || null,
916
+ evidenceRefs: [outputRef, ...toolRefs],
917
+ metadata: {
918
+ source: 'codex-stop-hook',
919
+ validated_output: true,
920
+ requireCognitionBlock: false,
921
+ requireAppliedCognition: false,
922
+ post_receipt_id: post?.receipt?.receiptId || null,
923
+ },
924
+ });
925
+ if (releaseCoach?.permitted === false) {
926
+ emitJson({
927
+ decision: 'block',
928
+ reason: formatCodexRecoveryBlock({
929
+ surface: 'codex-stop-coach-release',
930
+ reason: releaseCoach.clientMessage || 'Coach Kernel held the release claim.',
931
+ }),
932
+ });
933
+ }
646
934
  clearTurnState(sessionId);
647
935
  emitJson({ continue: true });
648
936
  } catch (error) {
649
937
  emitJson({
650
938
  decision: 'block',
651
- reason: \`Aria stop hook failed closed: \${error instanceof Error ? error.message : String(error)}\`,
939
+ reason: formatCodexRecoveryBlock({
940
+ surface: 'codex-stop-hook',
941
+ reason: error instanceof Error ? error.message : String(error),
942
+ next: '6. Re-open the turn substrate, repair the hook/runtime blocker, and re-submit with this recovery contract if it repeats.',
943
+ }),
652
944
  });
653
945
  }
654
946
  `;
@@ -696,6 +988,10 @@ function installCodexHooksConfig(codexDir: string, logs: string[]): void {
696
988
  function installCodexHooks(codexDir: string, logs: string[]): void {
697
989
  const hooksDir = path.join(codexDir, 'hooks');
698
990
  mkdirSync(path.join(hooksDir, 'lib'), { recursive: true, mode: 0o755 });
991
+ const ledgerHelperSrc = packageTaskProjectLedgerHelperPath();
992
+ if (!existsSync(ledgerHelperSrc)) {
993
+ throw new Error(`Task/project ledger helper missing: ${ledgerHelperSrc}`);
994
+ }
699
995
 
700
996
  const files: Array<[string, string]> = [
701
997
  [path.join(hooksDir, 'lib', 'runtime-client.mjs'), buildCodexHookRuntimeClient()],
@@ -709,6 +1005,8 @@ function installCodexHooks(codexDir: string, logs: string[]): void {
709
1005
  writeFileSync(filePath, content, { mode: 0o755 });
710
1006
  try { chmodSync(filePath, 0o755); } catch {}
711
1007
  }
1008
+ copyFileSync(ledgerHelperSrc, path.join(hooksDir, 'lib', 'task-project-ledger.mjs'));
1009
+ try { chmodSync(path.join(hooksDir, 'lib', 'task-project-ledger.mjs'), 0o755); } catch {}
712
1010
 
713
1011
  logs.push(`Installed Codex native hooks → ${hooksDir}`);
714
1012
  installCodexHooksConfig(codexDir, logs);
@@ -18,6 +18,7 @@ import type { AriaConfig } from '../config.js';
18
18
  import { connectShell } from './shell.js';
19
19
  import { syncDoctrineTriggerMap } from './doctrine-trigger-map.js';
20
20
  import { buildMustReadGuide, mustReadIntro } from './must-read.js';
21
+ import { resolveHarnessToken } from '../auth.js';
21
22
 
22
23
  // ── Bundled OpenCode plugins ────────────────────────────────────────────────
23
24
  //
@@ -33,6 +34,8 @@ import { buildMustReadGuide, mustReadIntro } from './must-read.js';
33
34
  // Both live at <pkg>/opencode-plugins/<name>/. connectOpenCode copies them
34
35
  // into ~/.opencode/plugins/<name>/ and wires the absolute paths into
35
36
  // ~/.opencode/config.json's `plugin` (singular, OpenCode v2 schema) array.
37
+ // Shared hook helpers are also copied into ~/.opencode/hooks/ because the
38
+ // installed plugins resolve their gate helpers from that OpenCode-owned root.
36
39
  //
37
40
  // Compiled location of THIS file (claude-code path applies the same way):
38
41
  // <pkg>/dist/aria-connector/src/connectors/opencode.js
@@ -51,6 +54,16 @@ function packageOpenCodePluginsDir(): string {
51
54
  return found || candidates[0];
52
55
  }
53
56
 
57
+ function packageOpenCodeHooksDir(): string {
58
+ const here = path.dirname(fileURLToPath(import.meta.url));
59
+ const candidates = [
60
+ path.resolve(here, '..', '..', '..', '..', 'hooks'),
61
+ path.resolve(here, '..', '..', '..', 'assets', 'hooks'),
62
+ ];
63
+ const found = candidates.find((candidate) => existsSync(candidate));
64
+ return found || candidates[0];
65
+ }
66
+
54
67
  function copyPluginDir(srcDir: string, dstDir: string, logs: string[]): void {
55
68
  if (!existsSync(dstDir)) {
56
69
  mkdirSync(dstDir, { recursive: true, mode: 0o755 });
@@ -58,8 +71,12 @@ function copyPluginDir(srcDir: string, dstDir: string, logs: string[]): void {
58
71
  for (const name of readdirSync(srcDir)) {
59
72
  const src = path.join(srcDir, name);
60
73
  const stat = statSync(src);
61
- if (!stat.isFile()) continue;
62
74
  const dst = path.join(dstDir, name);
75
+ if (stat.isDirectory()) {
76
+ copyPluginDir(src, dst, logs);
77
+ continue;
78
+ }
79
+ if (!stat.isFile()) continue;
63
80
  copyFileSync(src, dst);
64
81
  if (name.endsWith('.mjs') || name.endsWith('.js')) {
65
82
  try {
@@ -141,11 +158,22 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
141
158
  copyPluginDir(srcDir, dstDir, logs);
142
159
  installedPaths.push(dstDir);
143
160
  }
161
+
162
+ const bundledHooksDir = packageOpenCodeHooksDir();
163
+ if (existsSync(bundledHooksDir)) {
164
+ copyPluginDir(bundledHooksDir, path.join(opencodeDir, 'hooks'), logs);
165
+ } else {
166
+ logs.push(`⚠ Bundled OpenCode hook helpers missing: ${bundledHooksDir} — gate plugins may fail to load`);
167
+ }
144
168
  syncDoctrineTriggerMap(logs);
145
169
 
146
- // ── Wire ~/.opencode/config.json ──────────────────────────────────────────
147
- const configPath = path.join(opencodeDir, 'config.json');
148
- if (existsSync(configPath)) {
170
+ // ── Wire OpenCode config files ────────────────────────────────────────────
171
+ const configPaths = [
172
+ path.join(opencodeDir, 'config.json'),
173
+ path.join(homedir(), '.config', 'opencode', 'config.json'),
174
+ ];
175
+ for (const configPath of configPaths) {
176
+ if (!existsSync(configPath)) continue;
149
177
  try {
150
178
  const opencodeConfig: Record<string, unknown> = JSON.parse(
151
179
  readFileSync(configPath, 'utf-8'),
@@ -191,19 +219,14 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
191
219
  logs.push(`Wired ${installedPaths.length} Aria plugin(s) into OpenCode config.plugin`);
192
220
  }
193
221
 
194
- if (!opencodeConfig.agentsMdPath) {
195
- opencodeConfig.agentsMdPath = path.join(homedir(), '.aria', 'AGENTS.md');
196
- modified = true;
197
- logs.push('Set OpenCode AGENTS.md path to ~/.aria/AGENTS.md');
198
- }
199
-
200
222
  const providerMap = opencodeConfig.provider && typeof opencodeConfig.provider === 'object'
201
223
  ? opencodeConfig.provider as Record<string, unknown>
202
224
  : {};
203
225
  const currentAriaProvider = providerMap.aria && typeof providerMap.aria === 'object'
204
226
  ? providerMap.aria as Record<string, unknown>
205
227
  : {};
206
- const localToken = resolveLocalRuntimeToken();
228
+ const resolvedLocalToken = await resolveHarnessToken({ baseUrl: LOCAL_RUNTIME_V1_URL });
229
+ const localToken = resolvedLocalToken.token || resolveLocalRuntimeToken();
207
230
  const nextAriaProvider = {
208
231
  npm: '@ai-sdk/openai-compatible',
209
232
  name: 'Aria Runtime',
@@ -252,7 +275,7 @@ export async function connectOpenCode(config: AriaConfig): Promise<string[]> {
252
275
  logs.push('OpenCode already configured for Aria');
253
276
  }
254
277
  } catch {
255
- logs.push('Failed to parse OpenCode config.json');
278
+ logs.push(`Failed to parse OpenCode config.json at ${configPath}`);
256
279
  }
257
280
  }
258
281