@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
@@ -31,6 +31,7 @@ const COGNITIVE_SKILLS_SRC = resolve(PKG_ROOT, 'skills', 'aria-cognition');
31
31
  const RUNTIME_DISCIPLINE_DST = resolve(RUNTIME_DST, 'discipline');
32
32
  const CONNECTOR_HOOKS_SRC = resolve(PKG_ROOT, 'hooks');
33
33
  const OPENCODE_PLUGINS_SRC = resolve(PKG_ROOT, 'opencode-plugins');
34
+ const TASK_PROJECT_LEDGER_HELPER_SRC = resolve(OPENCODE_PLUGINS_SRC, 'harness-context', 'task-project-ledger.mjs');
34
35
  const CLIENT_ONBOARDING_SRC = resolve(PKG_ROOT, 'CLIENT-ONBOARDING.md');
35
36
  const DOCTRINE_TRIGGER_MAP_SRC = resolve(CONNECTOR_HOOKS_SRC, 'doctrine_trigger_map.json');
36
37
  const REQUIRED_HOOK_FILES = [
@@ -39,6 +40,7 @@ const REQUIRED_HOOK_FILES = [
39
40
  'aria-preturn-memory-gate.mjs',
40
41
  'aria-stop-gate.mjs',
41
42
  'aria-preprompt-consult.mjs',
43
+ 'aria-first-class-coach.mjs',
42
44
  'aria-trigger-autolearn.mjs',
43
45
  'aria-userprompt-abandon-detect.mjs',
44
46
  'aria-agent-handoff.mjs',
@@ -55,6 +57,10 @@ const REQUIRED_HOOK_HELPERS = [
55
57
  'lib/canonical-lenses.mjs',
56
58
  'lib/gate-audit.mjs',
57
59
  'lib/skill-autoload-gate.mjs',
60
+ 'lib/skill-autoload-gate-impl.mjs',
61
+ 'lib/emergency-gateoff.mjs',
62
+ 'lib/emergency-gateoff-impl.mjs',
63
+ 'lib/first-class-coach.mjs',
58
64
  ];
59
65
  const REQUIRED_OPENCODE_PLUGINS = [
60
66
  'harness-context',
@@ -78,6 +84,7 @@ for (const relativePath of REQUIRED_HOOK_HELPERS) {
78
84
  for (const pluginName of REQUIRED_OPENCODE_PLUGINS) {
79
85
  requirePathExists(join(OPENCODE_PLUGINS_SRC, pluginName), `required OpenCode plugin ${pluginName}`);
80
86
  }
87
+ requirePathExists(TASK_PROJECT_LEDGER_HELPER_SRC, 'task/project ledger helper');
81
88
  requirePathExists(CLIENT_ONBOARDING_SRC, 'client onboarding guide');
82
89
 
83
90
  // Staleness guard — newest src mtime must not exceed newest dist mtime.
@@ -300,6 +307,7 @@ if (existsSync(CONNECTOR_HOOKS_SRC)) {
300
307
  if (existsSync(OPENCODE_PLUGINS_SRC)) {
301
308
  copyTree(OPENCODE_PLUGINS_SRC, join(DIST_ASSETS_DST, 'opencode-plugins'));
302
309
  }
310
+ copyFileSync(TASK_PROJECT_LEDGER_HELPER_SRC, join(RUNTIME_DST, 'task-project-ledger.mjs'));
303
311
 
304
312
  writeFileSync(
305
313
  join(RUNTIME_DST, 'manifest.json'),
@@ -0,0 +1,422 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createServer } from 'node:http';
4
+ import { spawn } from 'node:child_process';
5
+ import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
6
+ import { homedir, tmpdir } from 'node:os';
7
+ import { dirname, join, resolve } from 'node:path';
8
+ import { fileURLToPath } from 'node:url';
9
+
10
+ const here = dirname(fileURLToPath(import.meta.url));
11
+ const repoRoot = resolve(here, '..', '..', '..');
12
+ const packageRoot = resolve(here, '..');
13
+ const home = homedir();
14
+ const failures = [];
15
+
16
+ function fail(check, message, details = {}) {
17
+ failures.push({ check, message, details });
18
+ }
19
+
20
+ function assert(check, condition, message, details = {}) {
21
+ if (!condition) fail(check, message, details);
22
+ }
23
+
24
+ function readIfExists(pathname) {
25
+ return existsSync(pathname) ? readFileSync(pathname, 'utf8') : '';
26
+ }
27
+
28
+ function runtimeAuthHeaders(extra = {}) {
29
+ const token = process.env.ARIA_HARNESS_TOKEN ||
30
+ process.env.ARIA_API_KEY ||
31
+ process.env.ARIA_MASTER_TOKEN ||
32
+ readIfExists(join(home, '.aria', 'owner-token')).trim();
33
+ return {
34
+ 'content-type': 'application/json',
35
+ ...(token ? { 'x-aria-api-key': token } : {}),
36
+ ...extra,
37
+ };
38
+ }
39
+
40
+ function minimalHarnessPacket(message = 'runtime compatibility regression') {
41
+ return {
42
+ packet: {
43
+ axioms: {
44
+ truth_over_deception: true,
45
+ no_harm: true,
46
+ sacred_trust: true,
47
+ power_obligates_service: true,
48
+ reflection_before_action: true,
49
+ },
50
+ frames: {
51
+ first_principle: 'Truth over deception. No harm. Sacred trust. Power obligates service. Reflection before action.',
52
+ message,
53
+ },
54
+ doctrine: {},
55
+ memory: {},
56
+ },
57
+ timestamp: new Date().toISOString(),
58
+ };
59
+ }
60
+
61
+ function executable(pathname) {
62
+ return existsSync(pathname) ? pathname : null;
63
+ }
64
+
65
+ function spawnCapture(command, args, options = {}) {
66
+ const timeoutMs = options.timeoutMs || 45000;
67
+ return new Promise((resolveResult) => {
68
+ const child = spawn(command, args, {
69
+ env: options.env || process.env,
70
+ cwd: options.cwd || home,
71
+ stdio: ['ignore', 'pipe', 'pipe'],
72
+ });
73
+ let stdout = '';
74
+ let stderr = '';
75
+ let timedOut = false;
76
+ const timer = setTimeout(() => {
77
+ timedOut = true;
78
+ child.kill('SIGTERM');
79
+ }, timeoutMs);
80
+ child.stdout.on('data', (chunk) => { stdout += chunk; });
81
+ child.stderr.on('data', (chunk) => { stderr += chunk; });
82
+ child.on('exit', (code, signal) => {
83
+ clearTimeout(timer);
84
+ resolveResult({ code, signal, timedOut, stdout, stderr });
85
+ });
86
+ child.on('error', (error) => {
87
+ clearTimeout(timer);
88
+ resolveResult({ code: null, signal: null, timedOut, stdout, stderr: `${stderr}${error.message}` });
89
+ });
90
+ });
91
+ }
92
+
93
+ function startJsonServer(handler) {
94
+ const requests = [];
95
+ const server = createServer((req, res) => {
96
+ let raw = '';
97
+ req.on('data', (chunk) => { raw += chunk; });
98
+ req.on('end', () => {
99
+ let body = {};
100
+ try {
101
+ body = raw ? JSON.parse(raw) : {};
102
+ } catch {}
103
+ const record = { method: req.method, url: req.url || '/', headers: req.headers, body };
104
+ requests.push(record);
105
+ handler(req, res, record);
106
+ });
107
+ });
108
+ return new Promise((resolveServer) => {
109
+ server.listen(0, '127.0.0.1', () => {
110
+ const address = server.address();
111
+ resolveServer({
112
+ server,
113
+ requests,
114
+ baseUrl: `http://127.0.0.1:${address.port}`,
115
+ close: () => new Promise((resolveClose) => server.close(resolveClose)),
116
+ });
117
+ });
118
+ });
119
+ }
120
+
121
+ function writeSse(res, type, data) {
122
+ res.write(`event: ${type}\n`);
123
+ res.write(`data: ${JSON.stringify({ type, ...data })}\n\n`);
124
+ }
125
+
126
+ function respondCodexStream(res, model) {
127
+ const responseId = 'resp_aria_compat';
128
+ const itemId = 'msg_aria_compat';
129
+ const contentPart = { type: 'output_text', text: 'ok', annotations: [] };
130
+ const item = { id: itemId, type: 'message', status: 'completed', role: 'assistant', content: [contentPart] };
131
+ const response = {
132
+ id: responseId,
133
+ object: 'response',
134
+ created_at: Math.floor(Date.now() / 1000),
135
+ status: 'completed',
136
+ model,
137
+ output: [item],
138
+ usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
139
+ };
140
+ res.writeHead(200, {
141
+ 'content-type': 'text/event-stream',
142
+ 'cache-control': 'no-cache',
143
+ connection: 'keep-alive',
144
+ });
145
+ writeSse(res, 'response.created', { response: { ...response, status: 'in_progress', output: [] } });
146
+ writeSse(res, 'response.output_item.added', { response_id: responseId, output_index: 0, item: { ...item, status: 'in_progress', content: [] } });
147
+ writeSse(res, 'response.content_part.added', { response_id: responseId, item_id: itemId, output_index: 0, content_index: 0, part: { type: 'output_text', text: '', annotations: [] } });
148
+ writeSse(res, 'response.output_text.delta', { response_id: responseId, item_id: itemId, output_index: 0, content_index: 0, delta: 'ok' });
149
+ writeSse(res, 'response.output_text.done', { response_id: responseId, item_id: itemId, output_index: 0, content_index: 0, text: 'ok' });
150
+ writeSse(res, 'response.content_part.done', { response_id: responseId, item_id: itemId, output_index: 0, content_index: 0, part: contentPart });
151
+ writeSse(res, 'response.output_item.done', { response_id: responseId, output_index: 0, item });
152
+ writeSse(res, 'response.completed', { response });
153
+ res.write('data: [DONE]\n\n');
154
+ res.end();
155
+ }
156
+
157
+ function runVersion(command, args = ['--version']) {
158
+ return spawnCapture(command, args, { timeoutMs: 10000 });
159
+ }
160
+
161
+ async function checkWrapperContracts() {
162
+ const sourceShell = readIfExists(join(packageRoot, 'src', 'connectors', 'shell.ts'));
163
+ const claudeWrapperPath = join(home, '.aria', 'wrappers', 'claude');
164
+ const codexWrapperPath = join(home, '.aria', 'wrappers', 'codex');
165
+ const claudeWrapper = readIfExists(claudeWrapperPath);
166
+ const codexWrapper = readIfExists(codexWrapperPath);
167
+ assert('source-wrapper-contract', sourceShell.includes(String.raw`ANTHROPIC_BASE_URL="\${ANTHROPIC_BASE_URL:-$ARIA_RUNTIME_URL}"`), 'Generated wrapper must set Anthropic base at the runtime root.');
168
+ assert('source-wrapper-contract', sourceShell.includes(String.raw`OPENAI_BASE_URL="\${OPENAI_BASE_URL:-$ARIA_RUNTIME_URL/v1}"`), 'Generated wrapper must keep OpenAI base at the v1 root.');
169
+ assert('source-wrapper-contract', !/OPENAI_API_KEY=.*ARIA_API_KEY/.test(sourceShell), 'Generated wrapper must not copy Aria control-plane tokens into provider API keys.');
170
+ assert('active-claude-wrapper-contract', claudeWrapper.includes('ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-$ARIA_RUNTIME_URL}"'), 'Active Claude wrapper must set Anthropic base at the runtime root.', { claudeWrapperPath });
171
+ assert('active-claude-wrapper-contract', !/OPENAI_API_KEY=.*ARIA_API_KEY/.test(claudeWrapper), 'Active Claude wrapper must not poison provider API key env.', { claudeWrapperPath });
172
+ assert('active-codex-wrapper-contract', codexWrapper.includes('ANTHROPIC_BASE_URL="${ANTHROPIC_BASE_URL:-$ARIA_RUNTIME_URL}"'), 'Active Codex wrapper must share the corrected runtime root Anthropic base.', { codexWrapperPath });
173
+ assert('active-codex-wrapper-contract', codexWrapper.includes('case "${1:-}" in') && codexWrapper.includes('app-server|exec-server|exec|review'), 'Active Codex wrapper must preserve bypasses for native non-interactive subcommands.', { codexWrapperPath });
174
+ }
175
+
176
+ async function checkClaudeCli() {
177
+ const claudeBin = process.env.ARIA_COMPAT_CLAUDE_REAL_BIN || executable(join(home, '.local', 'bin', 'claude')) || process.env.ARIA_COMPAT_CLAUDE_BIN || executable(join(home, '.aria', 'wrappers', 'claude'));
178
+ if (!claudeBin) {
179
+ fail('claude-black-box', 'Claude binary is not installed.');
180
+ return;
181
+ }
182
+ const version = await runVersion(claudeBin);
183
+ const fake = await startJsonServer((_req, res, record) => {
184
+ if (record.method === 'HEAD') {
185
+ res.writeHead(200, { 'content-type': 'application/json' });
186
+ res.end('');
187
+ return;
188
+ }
189
+ res.writeHead(200, { 'content-type': 'application/json' });
190
+ res.end(JSON.stringify({
191
+ id: 'msg_aria_compat',
192
+ type: 'message',
193
+ role: 'assistant',
194
+ model: record.body?.model || 'claude-sonnet-4-6',
195
+ content: [{ type: 'text', text: 'ok' }],
196
+ stop_reason: 'end_turn',
197
+ usage: { input_tokens: 1, output_tokens: 1 },
198
+ }));
199
+ });
200
+ try {
201
+ const result = await spawnCapture(claudeBin, [
202
+ '-p', 'Reply with exactly: ok',
203
+ '--model', 'sonnet',
204
+ '--max-turns', '1',
205
+ '--output-format', 'json',
206
+ ], {
207
+ timeoutMs: 60000,
208
+ env: {
209
+ ...process.env,
210
+ ANTHROPIC_BASE_URL: fake.baseUrl,
211
+ OPENAI_BASE_URL: `${fake.baseUrl}/v1`,
212
+ ARIA_SELF_UPDATE: 'off',
213
+ },
214
+ });
215
+ const post = fake.requests.find((request) => request.method === 'POST');
216
+ let output = null;
217
+ try { output = JSON.parse(result.stdout.trim().split('\n').pop() || '{}'); } catch {}
218
+ assert('claude-black-box', result.code === 0 && !result.timedOut, 'Claude CLI smoke must exit successfully without timeout.', { code: result.code, signal: result.signal, stderr: result.stderr.slice(0, 400), version: version.stdout.trim() || version.stderr.trim() });
219
+ assert('claude-black-box', output?.result === 'ok' && output?.is_error !== true, 'Claude CLI smoke must return ok through the fake Anthropic-compatible server.', { stdout: result.stdout.slice(0, 400) });
220
+ assert('claude-request-contract', Boolean(post), 'Claude CLI must make a provider request during compatibility smoke.');
221
+ assert('claude-request-contract', post?.url?.startsWith('/v1/messages'), 'Claude CLI must target the Anthropic messages route from a runtime-root base URL.', { url: post?.url });
222
+ assert('claude-request-contract', /^claude(?:-|$)/i.test(post?.body?.model || ''), 'Claude CLI request must keep Claude model as a client-facing alias.', { model: post?.body?.model });
223
+ assert('claude-request-contract', Array.isArray(post?.body?.tools) && post.body.tools.length > 0 && Boolean(post.body.tools[0]?.input_schema), 'Claude CLI tool schema must remain Anthropic-style input_schema.', { firstToolKeys: Object.keys(post?.body?.tools?.[0] || {}) });
224
+ } finally {
225
+ await fake.close();
226
+ }
227
+ }
228
+
229
+ async function checkCodexCli() {
230
+ const codexBin = process.env.ARIA_COMPAT_CODEX_BIN || executable(join(home, '.aria', 'wrappers', 'codex'));
231
+ if (!codexBin) {
232
+ fail('codex-black-box', 'Codex wrapper is not installed.');
233
+ return;
234
+ }
235
+ const version = await runVersion(codexBin);
236
+ const codexHome = join(tmpdir(), `aria-codex-compat-${process.pid}`);
237
+ rmSync(codexHome, { recursive: true, force: true });
238
+ mkdirSync(codexHome, { recursive: true, mode: 0o700 });
239
+ const fake = await startJsonServer((_req, res, record) => {
240
+ if (record.body?.stream === true) {
241
+ respondCodexStream(res, record.body?.model || 'gpt-5.4');
242
+ return;
243
+ }
244
+ res.writeHead(200, { 'content-type': 'application/json' });
245
+ res.end(JSON.stringify({
246
+ id: 'resp_aria_compat',
247
+ object: 'response',
248
+ created_at: Math.floor(Date.now() / 1000),
249
+ status: 'completed',
250
+ model: record.body?.model || 'gpt-5.4',
251
+ output: [{ id: 'msg_aria_compat', type: 'message', status: 'completed', role: 'assistant', content: [{ type: 'output_text', text: 'ok', annotations: [] }] }],
252
+ usage: { input_tokens: 1, output_tokens: 1, total_tokens: 2 },
253
+ }));
254
+ });
255
+ try {
256
+ const args = [
257
+ 'exec',
258
+ '--skip-git-repo-check',
259
+ '--ignore-rules',
260
+ '--ignore-user-config',
261
+ '--ephemeral',
262
+ '-c', 'model_provider="openai-custom"',
263
+ '-c', 'model_providers.openai-custom.name="OpenAI Custom"',
264
+ '-c', `model_providers.openai-custom.base_url="${fake.baseUrl}/v1"`,
265
+ '-c', 'model_providers.openai-custom.api_key_env="OPENAI_API_KEY"',
266
+ '--model', 'gpt-5.4',
267
+ '--json',
268
+ 'Reply with exactly: ok',
269
+ ];
270
+ const result = await spawnCapture(codexBin, args, {
271
+ timeoutMs: 60000,
272
+ env: {
273
+ ...process.env,
274
+ CODEX_HOME: codexHome,
275
+ OPENAI_API_KEY: 'dummy-key',
276
+ ARIA_CODEX_BYPASS_BRIDGE: '1',
277
+ ARIA_SELF_UPDATE: 'off',
278
+ },
279
+ });
280
+ const post = fake.requests.find((request) => request.method === 'POST');
281
+ assert('codex-black-box', result.code === 0 && !result.timedOut, 'Codex CLI smoke must exit successfully without timeout.', { code: result.code, signal: result.signal, stderr: result.stderr.slice(0, 400), version: version.stdout.trim() || version.stderr.trim() });
282
+ assert('codex-black-box', /"type":"item\.completed"/.test(result.stdout) && /"text":"ok"/.test(result.stdout), 'Codex CLI smoke must emit an ok agent message in JSONL output.', { stdout: result.stdout.slice(0, 600) });
283
+ assert('codex-request-contract', Boolean(post), 'Codex CLI must make a provider request during compatibility smoke.');
284
+ assert('codex-request-contract', post?.url === '/v1/responses', 'Codex CLI must target the OpenAI Responses route.', { url: post?.url });
285
+ assert('codex-request-contract', post?.body?.model === 'gpt-5.4', 'Codex CLI request must preserve the configured model name.', { model: post?.body?.model });
286
+ assert('codex-request-contract', Array.isArray(post?.body?.input) && Array.isArray(post?.body?.tools), 'Codex CLI request must preserve Responses input and tools arrays.', { keys: Object.keys(post?.body || {}) });
287
+ } finally {
288
+ await fake.close();
289
+ rmSync(codexHome, { recursive: true, force: true });
290
+ }
291
+ }
292
+
293
+ async function checkRuntimeCompatibility() {
294
+ if (process.env.ARIA_COMPAT_SKIP_RUNTIME === '1') return;
295
+ const runtimeUrl = process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319';
296
+ const fake = await startJsonServer((_req, res, record) => {
297
+ res.writeHead(200, { 'content-type': 'application/json' });
298
+ res.end(JSON.stringify({
299
+ id: 'chatcmpl_aria_compat',
300
+ object: 'chat.completion',
301
+ created: Math.floor(Date.now() / 1000),
302
+ model: record.body?.model || 'grok-4.3',
303
+ choices: [{ index: 0, message: { role: 'assistant', content: 'ok' }, finish_reason: 'stop' }],
304
+ usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
305
+ }));
306
+ });
307
+ try {
308
+ const body = {
309
+ model: 'claude-sonnet-4-6',
310
+ max_tokens: 16,
311
+ providerConfig: {
312
+ provider: 'xai',
313
+ model: 'grok-4-3',
314
+ baseUrl: `${fake.baseUrl}/v1/chat/completions`,
315
+ apiKey: 'dummy-key',
316
+ },
317
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'Reply exactly: ok' }] }],
318
+ };
319
+ const response = await fetch(`${runtimeUrl.replace(/\/+$/, '')}/v1/v1/messages?beta=true`, {
320
+ method: 'POST',
321
+ headers: { 'content-type': 'application/json', authorization: 'Bearer dummy-provider-key', 'anthropic-version': '2023-06-01' },
322
+ body: JSON.stringify(body),
323
+ });
324
+ const raw = await response.text();
325
+ let data = {};
326
+ try { data = JSON.parse(raw); } catch {}
327
+ assert('runtime-compatibility', response.status === 200, 'Runtime must accept doubled v1 Anthropic-compatible paths for stale clients.', { status: response.status, body: raw.slice(0, 400) });
328
+ assert('runtime-compatibility', data?.content?.[0]?.text === 'ok', 'Runtime stale-client path must return provider content.', { content: data?.content });
329
+ assert('runtime-compatibility', !/malformed token/i.test(raw), 'Runtime stale-client path must not route provider bearer tokens into license validation.');
330
+ assert('runtime-compatibility', fake.requests.some((request) => request.body?.model === 'grok-4.3'), 'Runtime must route Claude client aliases to provider-native upstream model names.', { upstreamRequests: fake.requests.map((request) => ({ url: request.url, model: request.body?.model })) });
331
+ } catch (error) {
332
+ fail('runtime-compatibility', `Runtime compatibility smoke failed: ${error instanceof Error ? error.message : String(error)}`);
333
+ } finally {
334
+ await fake.close();
335
+ }
336
+ }
337
+
338
+ async function checkRuntimeRecoveryContracts() {
339
+ if (process.env.ARIA_COMPAT_SKIP_RUNTIME === '1') return;
340
+ const runtimeUrl = (process.env.ARIA_RUNTIME_URL || 'http://127.0.0.1:4319').replace(/\/+$/, '');
341
+ const packet = minimalHarnessPacket('runtime recovery contract regression');
342
+ const recoveryContract = [
343
+ 'ARIA CODEX RECOVERY CONTRACT',
344
+ 'surface: codex-stop-output',
345
+ 'status: output held for re-authoring',
346
+ '',
347
+ 'Observed blockers:',
348
+ '- completion/readiness claim conflicts with unresolved blocker state',
349
+ '',
350
+ 'Recovery contract:',
351
+ '1. Do not retry the same blocked text.',
352
+ '2. Re-author the answer from the user request, not from this block report.',
353
+ '3. Include <cognition> and <applied_cognition> when the answer is non-trivial.',
354
+ '4. Use bounded status language when evidence is missing; do not use completion/readiness claims without proof.',
355
+ '5. Name a measurable verification predicate, or explicitly state that verification has not run.',
356
+ ].join('\n');
357
+
358
+ try {
359
+ const controlResponse = await fetch(`${runtimeUrl}/validate-output`, {
360
+ method: 'POST',
361
+ headers: runtimeAuthHeaders(),
362
+ body: JSON.stringify({
363
+ text: recoveryContract,
364
+ sessionId: 'aria-compat-recovery-control',
365
+ packet,
366
+ fetchPacket: false,
367
+ }),
368
+ });
369
+ const controlRaw = await controlResponse.text();
370
+ let controlData = {};
371
+ try { controlData = JSON.parse(controlRaw); } catch {}
372
+ assert('runtime-recovery-contract', controlResponse.status === 200, 'Runtime must accept recovery-contract validation requests.', { status: controlResponse.status, body: controlRaw.slice(0, 400) });
373
+ assert('runtime-recovery-contract', controlData?.pass === true, 'Recovery contracts must pass as Aria control output instead of being re-scanned as assistant prose.', { body: controlRaw.slice(0, 600) });
374
+ assert('runtime-recovery-contract', controlData?.validation?.gateTriggers?.includes('aria-control-output'), 'Recovery contract pass-through must be explicitly labeled as control output.', { validation: controlData?.validation });
375
+ assert('runtime-recovery-contract', Array.isArray(controlData?.layer3?.failures) && controlData.layer3.failures.length === 0, 'Control output must not produce layer3 failures.', { layer3: controlData?.layer3 });
376
+
377
+ const doctrineResponse = await fetch(`${runtimeUrl}/validate-output`, {
378
+ method: 'POST',
379
+ headers: runtimeAuthHeaders(),
380
+ body: JSON.stringify({
381
+ text: 'Done but blocked by missing proof.',
382
+ sessionId: 'aria-compat-recovery-sanitized-doctrine',
383
+ packet,
384
+ fetchPacket: false,
385
+ runLayer3: true,
386
+ requireCognitionBlock: false,
387
+ }),
388
+ });
389
+ const doctrineRaw = await doctrineResponse.text();
390
+ let doctrineData = {};
391
+ try { doctrineData = JSON.parse(doctrineRaw); } catch {}
392
+ assert('runtime-sanitized-doctrine', doctrineResponse.status === 200, 'Runtime must return a structured validation body for doctrine-triggered output.', { status: doctrineResponse.status, body: doctrineRaw.slice(0, 400) });
393
+ assert('runtime-sanitized-doctrine', doctrineData?.layer3?.pass === false, 'Premature completion with unresolved blockers must still fail layer3.', { layer3: doctrineData?.layer3 });
394
+ assert('runtime-sanitized-doctrine', !/\(\?:done\|complete|done\\\|complete|completed\\\|ready|verified\\\|fixed/.test(doctrineRaw), 'Doctrine failure detail must not expose raw matcher regex.', { body: doctrineRaw.slice(0, 1000) });
395
+ assert('runtime-sanitized-doctrine', /premature_task_closeout|feedback_no_premature_task_closeout/.test(doctrineRaw), 'Doctrine failure detail must identify the named trigger.', { body: doctrineRaw.slice(0, 1000) });
396
+ } catch (error) {
397
+ fail('runtime-recovery-contract', `Runtime recovery contract smoke failed: ${error instanceof Error ? error.message : String(error)}`);
398
+ }
399
+ }
400
+
401
+ async function main() {
402
+ await checkWrapperContracts();
403
+ await checkClaudeCli();
404
+ await checkCodexCli();
405
+ await checkRuntimeCompatibility();
406
+ await checkRuntimeRecoveryContracts();
407
+
408
+ const result = {
409
+ ok: failures.length === 0,
410
+ checkedAt: new Date().toISOString(),
411
+ packageRoot,
412
+ checks: ['wrapper-contracts', 'claude-black-box', 'codex-black-box', 'runtime-compatibility', 'runtime-recovery-contract'],
413
+ failures,
414
+ };
415
+ console.log(JSON.stringify(result, null, 2));
416
+ process.exit(result.ok ? 0 : 1);
417
+ }
418
+
419
+ main().catch((error) => {
420
+ console.error(JSON.stringify({ ok: false, error: error instanceof Error ? error.message : String(error) }, null, 2));
421
+ process.exit(1);
422
+ });
@@ -0,0 +1,204 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { dirname, resolve } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const here = dirname(fileURLToPath(import.meta.url));
8
+ const repoRoot = resolve(here, '..', '..', '..');
9
+ const packageRoot = resolve(here, '..');
10
+ const failures = [];
11
+
12
+ function fail(check, message, details = {}) {
13
+ failures.push({ check, message, details });
14
+ }
15
+
16
+ function readText(pathname) {
17
+ return existsSync(pathname) ? readFileSync(pathname, 'utf8') : '';
18
+ }
19
+
20
+ function readJson(pathname) {
21
+ try {
22
+ return JSON.parse(readText(pathname));
23
+ } catch (error) {
24
+ fail('json', `invalid JSON at ${pathname}: ${error instanceof Error ? error.message : String(error)}`);
25
+ return null;
26
+ }
27
+ }
28
+
29
+ function requireTokens(label, text, tokens) {
30
+ if (!text) {
31
+ fail(label, `${label} source is missing`);
32
+ return;
33
+ }
34
+ for (const token of tokens) {
35
+ if (!text.includes(token)) fail(label, `missing token ${token}`);
36
+ }
37
+ }
38
+
39
+ const ledgerPath = resolve(repoRoot, 'docs', 'system', 'ARIA_COACH_KERNEL_MICROPHASED_LEDGER.json');
40
+ const coachPath = resolve(packageRoot, 'runtime-src', 'coach-kernel.mjs');
41
+ const servicePath = resolve(packageRoot, 'runtime-src', 'service.mjs');
42
+ const codexBridgePath = resolve(packageRoot, 'runtime-src', 'codex-bridge.mjs');
43
+ const codexConnectorPath = resolve(packageRoot, 'src', 'connectors', 'codex.ts');
44
+ const claudeConnectorPath = resolve(packageRoot, 'src', 'connectors', 'claude-code.ts');
45
+ const claudeCoachHookPath = resolve(packageRoot, 'hooks', 'aria-first-class-coach.mjs');
46
+ const firstClassCoachLibPath = resolve(packageRoot, 'hooks', 'lib', 'first-class-coach.mjs');
47
+ const packageJsonPath = resolve(packageRoot, 'package.json');
48
+
49
+ const ledger = readJson(ledgerPath);
50
+ const coach = readText(coachPath);
51
+ const service = readText(servicePath);
52
+ const codexBridge = readText(codexBridgePath);
53
+ const codexConnector = readText(codexConnectorPath);
54
+ const claudeConnector = readText(claudeConnectorPath);
55
+ const claudeCoachHook = readText(claudeCoachHookPath);
56
+ const firstClassCoachLib = readText(firstClassCoachLibPath);
57
+ const packageJson = readJson(packageJsonPath);
58
+
59
+ const requiredPhases = [
60
+ 'pre_turn',
61
+ 'pre_cognition',
62
+ 'post_cognition',
63
+ 'pre_generation',
64
+ 'post_generation',
65
+ 'pre_tool',
66
+ 'post_tool',
67
+ 'pre_output',
68
+ 'post_output',
69
+ 'background_start',
70
+ 'background_checkpoint',
71
+ 'background_complete',
72
+ 'claim_or_release',
73
+ ];
74
+
75
+ if (!ledger) {
76
+ fail('ledger', 'Coach Kernel ledger is missing or invalid');
77
+ } else {
78
+ if (ledger.schemaVersion !== 1) fail('ledger.schemaVersion', 'schemaVersion must be 1');
79
+ if (ledger.ledgerId !== 'aria-coach-kernel-microphased-ledger') fail('ledger.ledgerId', 'unexpected Coach Kernel ledger id');
80
+ for (const phase of requiredPhases) {
81
+ if (!Array.isArray(ledger.phaseTaxonomy) || !ledger.phaseTaxonomy.includes(phase)) {
82
+ fail('ledger.phaseTaxonomy', `missing phase ${phase}`);
83
+ }
84
+ }
85
+ const gates = new Set((ledger.qualityGates || []).map((gate) => gate?.id).filter(Boolean));
86
+ for (const gate of ['coach_module_gate', 'coach_endpoint_gate', 'provider_phase_gate', 'codex_phase_gate', 'claude_phase_gate', 'repair_block_boundary_gate', 'operator_evidence_gate']) {
87
+ if (!gates.has(gate)) fail('ledger.qualityGates', `missing quality gate ${gate}`);
88
+ }
89
+ if (ledger.readinessClaimAllowed === true && Array.isArray(ledger.openBlockers) && ledger.openBlockers.length > 0) {
90
+ fail('ledger.readinessClaimAllowed', 'readiness cannot be true while open blockers remain');
91
+ }
92
+ }
93
+
94
+ requireTokens('coach-module', coach, [
95
+ 'COACH_EVENT_SCHEMA',
96
+ 'COACH_PHASES',
97
+ 'recordCoachPhase',
98
+ 'normalizeCoachEvent',
99
+ 'evaluateCoachEvent',
100
+ 'repair_once',
101
+ 'hard_block',
102
+ 'warn_operator_only',
103
+ 'coach-events.jsonl',
104
+ 'coach-state.json',
105
+ 'redactPotentialSecrets',
106
+ ]);
107
+
108
+ for (const phase of requiredPhases) {
109
+ if (!coach.includes(`'${phase}'`)) fail('coach-module.phases', `Coach module missing phase ${phase}`);
110
+ }
111
+
112
+ requireTokens('runtime-service', service, [
113
+ './coach-kernel.mjs',
114
+ 'recordRuntimeCoachPhase',
115
+ 'applyCoachRepairDecision',
116
+ "url.pathname === '/coach/event'",
117
+ "url.pathname === '/coach/phase'",
118
+ "url.pathname === '/coach/state'",
119
+ "phase: 'pre_turn'",
120
+ "phase: 'pre_generation'",
121
+ "phase: 'post_generation'",
122
+ "phase: 'pre_output'",
123
+ "phase: 'claim_or_release'",
124
+ 'coachKernel',
125
+ ]);
126
+
127
+ const preProviderIndex = service.indexOf("phase: 'pre_generation'");
128
+ const providerCallIndex = service.indexOf('providerMeta = await callProviderByStyle');
129
+ if (preProviderIndex < 0 || providerCallIndex < 0 || preProviderIndex > providerCallIndex) {
130
+ fail('runtime-service.pre-generation-order', 'Coach pre_generation event must be recorded before upstream provider call');
131
+ }
132
+
133
+ requireTokens('codex-bridge', codexBridge, [
134
+ 'recordCoachPhaseForTurn',
135
+ "'/coach/phase'",
136
+ "'pre_turn'",
137
+ "'pre_tool'",
138
+ "'post_generation'",
139
+ "'pre_output'",
140
+ "'claim_or_release'",
141
+ ]);
142
+
143
+ requireTokens('codex-connector', codexConnector, [
144
+ 'export async function recordCoachPhase',
145
+ "runtimePost('/coach/phase'",
146
+ "recordCoachPhase('pre_turn'",
147
+ "recordCoachPhase('pre_tool'",
148
+ "recordCoachPhase('post_tool'",
149
+ "recordCoachPhase('post_generation'",
150
+ "recordCoachPhase('pre_output'",
151
+ "recordCoachPhase('post_output'",
152
+ "recordCoachPhase('claim_or_release'",
153
+ ]);
154
+
155
+ requireTokens('claude-connector', claudeConnector, [
156
+ 'aria-first-class-coach.mjs',
157
+ 'ARIA_COACH_PLATFORM=claude HOOK_EVENT_NAME=SessionStart',
158
+ 'ARIA_COACH_PLATFORM=claude HOOK_EVENT_NAME=UserPromptSubmit',
159
+ 'ARIA_COACH_PLATFORM=claude HOOK_EVENT_NAME=PreToolUse',
160
+ 'ARIA_COACH_PLATFORM=claude HOOK_EVENT_NAME=PostToolUse',
161
+ 'ARIA_COACH_PLATFORM=claude HOOK_EVENT_NAME=Stop',
162
+ ]);
163
+
164
+ requireTokens('claude-coach-hook', claudeCoachHook, [
165
+ 'recordRuntimeCoachForLifecycle',
166
+ 'runtimeCoachBlocksLocalPhase',
167
+ 'formatRuntimeCoachBlock',
168
+ "'claim_or_release'",
169
+ "'post_output'",
170
+ ]);
171
+
172
+ requireTokens('first-class-coach-lib', firstClassCoachLib, [
173
+ 'RUNTIME_COACH_BRIDGE_VERSION',
174
+ "'/coach/phase'",
175
+ 'recordRuntimeCoachForLifecycle',
176
+ 'runtimeCoachBlocksLocalPhase',
177
+ 'formatRuntimeCoachBlock',
178
+ "session_start: ['pre_turn']",
179
+ "pre_prompt_injection: ['pre_turn', 'pre_cognition']",
180
+ "pre_tool: ['pre_tool']",
181
+ "post_tool: ['post_tool']",
182
+ "stop: ['post_generation', 'pre_output']",
183
+ "'claim_or_release'",
184
+ "'post_output'",
185
+ ]);
186
+
187
+ const scripts = packageJson?.scripts || {};
188
+ if (!scripts['check:coach-kernel']) {
189
+ fail('package.scripts.check:coach-kernel', 'package must expose Coach Kernel check');
190
+ }
191
+ if (!String(scripts['check:hooks'] || '').includes('check-coach-kernel.mjs')) {
192
+ fail('package.scripts.check:hooks', 'check:hooks must include Coach Kernel check');
193
+ }
194
+
195
+ const result = {
196
+ ok: failures.length === 0,
197
+ checkedAt: new Date().toISOString(),
198
+ ledgerPath,
199
+ coachPath,
200
+ servicePath,
201
+ failures,
202
+ };
203
+ console.log(JSON.stringify(result, null, 2));
204
+ process.exit(result.ok ? 0 : 1);