@aria_asi/cli 0.2.35 → 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 +312 -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 +310 -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
  `;
@@ -426,18 +525,23 @@ try {
426
525
  function buildCodexPreToolHook(): string {
427
526
  return `#!/usr/bin/env node
428
527
  import {
528
+ getHarnessClient,
429
529
  inferSessionId,
430
530
  classifyAction,
431
531
  summarizeTarget,
432
532
  readEventFromStdin,
433
533
  loadTurnState,
434
534
  makeEvidenceRef,
535
+ recordCoachPhase,
435
536
  saveTurnState,
436
537
  runGovernanceGate,
538
+ updateTaskProjectLedger,
539
+ formatCodexRecoveryBlock,
437
540
  emitJson,
438
541
  } from './lib/runtime-client.mjs';
439
542
 
440
543
  const event = readEventFromStdin();
544
+ const client = getHarnessClient();
441
545
  const sessionId = inferSessionId(event);
442
546
  const action = classifyAction(event);
443
547
  const target = summarizeTarget(event);
@@ -447,17 +551,52 @@ try {
447
551
  if (!state?.preReceiptId && !state?.userText) {
448
552
  emitJson({
449
553
  decision: 'block',
450
- 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
+ }),
451
580
  });
452
581
  }
453
582
  const actionCheck = await client.checkAction(action, target);
454
583
  if (actionCheck?.allowed === false) {
455
584
  emitJson({
456
585
  decision: 'block',
457
- 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
+ }),
458
591
  });
459
592
  }
460
- 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
+ });
461
600
  runGovernanceGate({
462
601
  sessionId,
463
602
  sourceRuntime: 'codex',
@@ -467,7 +606,7 @@ try {
467
606
  toolName,
468
607
  isDeploy: action === 'deploy',
469
608
  isMutation: action === 'write' || action === 'delete',
470
- evidence: makeEvidenceRef('codex_tool_request', { action, toolName, target }, { sessionId }),
609
+ evidence: requestRef,
471
610
  });
472
611
  const tools = Array.isArray(state?.tools) ? state.tools.slice(-24) : [];
473
612
  tools.push({
@@ -485,7 +624,10 @@ try {
485
624
  } catch (error) {
486
625
  emitJson({
487
626
  decision: 'block',
488
- 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
+ }),
489
631
  });
490
632
  }
491
633
  `;
@@ -498,7 +640,9 @@ import {
498
640
  readEventFromStdin,
499
641
  loadTurnState,
500
642
  makeEvidenceRef,
643
+ recordCoachPhase,
501
644
  saveTurnState,
645
+ updateTaskProjectLedger,
502
646
  } from './lib/runtime-client.mjs';
503
647
 
504
648
  const event = readEventFromStdin();
@@ -512,6 +656,20 @@ try {
512
656
  toolName: event?.tool_name || event?.toolName || null,
513
657
  });
514
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
+ });
515
673
  toolOutputs.push({
516
674
  at: new Date().toISOString(),
517
675
  toolName: event?.tool_name || event?.toolName || null,
@@ -522,6 +680,17 @@ try {
522
680
  toolOutputs,
523
681
  lastEvent: 'PostToolUse',
524
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
+ });
525
694
  process.exit(0);
526
695
  } catch {
527
696
  process.exit(0);
@@ -536,11 +705,17 @@ import {
536
705
  extractAssistantText,
537
706
  readEventFromStdin,
538
707
  runtimePost,
708
+ recordCoachPhase,
539
709
  loadTurnState,
540
710
  makeEvidenceRef,
541
711
  clearTurnState,
542
712
  formatValidationFailure,
713
+ formatCodexRecoveryBlock,
714
+ isAriaControlBlock,
543
715
  runGovernanceGate,
716
+ updateTaskProjectLedger,
717
+ evaluateTaskProjectClaim,
718
+ recordBlockedTaskProjectClaim,
544
719
  emitJson,
545
720
  } from './lib/runtime-client.mjs';
546
721
 
@@ -550,11 +725,58 @@ const state = loadTurnState(sessionId);
550
725
  const text = extractAssistantText(event);
551
726
  const outputRef = makeEvidenceRef('assistant_output', text, { sessionId, traceId: state?.traceId || null });
552
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
+ });
553
734
 
554
735
  try {
555
736
  if (!text) {
556
737
  emitJson({ continue: true });
557
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
+ }
558
780
  runGovernanceGate({
559
781
  sessionId,
560
782
  sourceRuntime: 'codex',
@@ -577,10 +799,31 @@ try {
577
799
  requireCognitionBlock: false,
578
800
  runLayer3: true,
579
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
+ }
580
820
  if (validation?.pass === false || validation?.validation?.passed === false || validation?.layer3?.pass === false) {
581
821
  emitJson({
582
822
  decision: 'block',
583
- reason: \`Aria stop gate blocked output: \${formatValidationFailure(validation)}\`,
823
+ reason: formatCodexRecoveryBlock({
824
+ surface: 'codex-stop-output',
825
+ reason: formatValidationFailure(validation),
826
+ }),
584
827
  });
585
828
  }
586
829
  const post = await runtimePost('/mizan/post', {
@@ -605,6 +848,18 @@ try {
605
848
  },
606
849
  parentReceiptId: state?.preReceiptId || null,
607
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
+ });
608
863
  await runtimePost('/decision/log', {
609
864
  session_id: sessionId,
610
865
  decision_type: 'operational',
@@ -641,12 +896,51 @@ try {
641
896
  },
642
897
  });
643
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
+ }
644
934
  clearTurnState(sessionId);
645
935
  emitJson({ continue: true });
646
936
  } catch (error) {
647
937
  emitJson({
648
938
  decision: 'block',
649
- 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
+ }),
650
944
  });
651
945
  }
652
946
  `;
@@ -694,6 +988,10 @@ function installCodexHooksConfig(codexDir: string, logs: string[]): void {
694
988
  function installCodexHooks(codexDir: string, logs: string[]): void {
695
989
  const hooksDir = path.join(codexDir, 'hooks');
696
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
+ }
697
995
 
698
996
  const files: Array<[string, string]> = [
699
997
  [path.join(hooksDir, 'lib', 'runtime-client.mjs'), buildCodexHookRuntimeClient()],
@@ -707,6 +1005,8 @@ function installCodexHooks(codexDir: string, logs: string[]): void {
707
1005
  writeFileSync(filePath, content, { mode: 0o755 });
708
1006
  try { chmodSync(filePath, 0o755); } catch {}
709
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 {}
710
1010
 
711
1011
  logs.push(`Installed Codex native hooks → ${hooksDir}`);
712
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