@aria_asi/cli 0.2.26 → 0.2.29

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 (248) hide show
  1. package/CLIENT-ONBOARDING.md +282 -0
  2. package/bin/aria.js +1140 -14
  3. package/dist/aria-connector/src/auth-commands.d.ts +1 -0
  4. package/dist/aria-connector/src/auth-commands.d.ts.map +1 -1
  5. package/dist/aria-connector/src/auth-commands.js +89 -41
  6. package/dist/aria-connector/src/auth-commands.js.map +1 -1
  7. package/dist/aria-connector/src/chat.d.ts +3 -0
  8. package/dist/aria-connector/src/chat.d.ts.map +1 -1
  9. package/dist/aria-connector/src/chat.js +146 -8
  10. package/dist/aria-connector/src/chat.js.map +1 -1
  11. package/dist/aria-connector/src/codebase-scanner.d.ts +2 -2
  12. package/dist/aria-connector/src/codebase-scanner.d.ts.map +1 -1
  13. package/dist/aria-connector/src/codebase-scanner.js +1 -1
  14. package/dist/aria-connector/src/codebase-scanner.js.map +1 -1
  15. package/dist/aria-connector/src/config.d.ts +12 -0
  16. package/dist/aria-connector/src/config.d.ts.map +1 -1
  17. package/dist/aria-connector/src/config.js +2 -0
  18. package/dist/aria-connector/src/config.js.map +1 -1
  19. package/dist/aria-connector/src/connectors/claude-code.d.ts.map +1 -1
  20. package/dist/aria-connector/src/connectors/claude-code.js +80 -24
  21. package/dist/aria-connector/src/connectors/claude-code.js.map +1 -1
  22. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts +37 -0
  23. package/dist/aria-connector/src/connectors/codebase-awareness.d.ts.map +1 -0
  24. package/dist/aria-connector/src/connectors/codebase-awareness.js +335 -0
  25. package/dist/aria-connector/src/connectors/codebase-awareness.js.map +1 -0
  26. package/dist/aria-connector/src/connectors/codex.d.ts +3 -0
  27. package/dist/aria-connector/src/connectors/codex.d.ts.map +1 -0
  28. package/dist/aria-connector/src/connectors/codex.js +248 -0
  29. package/dist/aria-connector/src/connectors/codex.js.map +1 -0
  30. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts +2 -0
  31. package/dist/aria-connector/src/connectors/cognitive-skills.d.ts.map +1 -0
  32. package/dist/aria-connector/src/connectors/cognitive-skills.js +47 -0
  33. package/dist/aria-connector/src/connectors/cognitive-skills.js.map +1 -0
  34. package/dist/aria-connector/src/connectors/opencode.d.ts.map +1 -1
  35. package/dist/aria-connector/src/connectors/opencode.js +90 -4
  36. package/dist/aria-connector/src/connectors/opencode.js.map +1 -1
  37. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts +3 -0
  38. package/dist/aria-connector/src/connectors/repo-git-hooks.d.ts.map +1 -0
  39. package/dist/aria-connector/src/connectors/repo-git-hooks.js +87 -0
  40. package/dist/aria-connector/src/connectors/repo-git-hooks.js.map +1 -0
  41. package/dist/aria-connector/src/connectors/repo-guard.d.ts +19 -0
  42. package/dist/aria-connector/src/connectors/repo-guard.d.ts.map +1 -0
  43. package/dist/aria-connector/src/connectors/repo-guard.js +509 -0
  44. package/dist/aria-connector/src/connectors/repo-guard.js.map +1 -0
  45. package/dist/aria-connector/src/connectors/runtime.d.ts +2 -0
  46. package/dist/aria-connector/src/connectors/runtime.d.ts.map +1 -0
  47. package/dist/aria-connector/src/connectors/runtime.js +330 -0
  48. package/dist/aria-connector/src/connectors/runtime.js.map +1 -0
  49. package/dist/aria-connector/src/connectors/shell.d.ts.map +1 -1
  50. package/dist/aria-connector/src/connectors/shell.js +78 -13
  51. package/dist/aria-connector/src/connectors/shell.js.map +1 -1
  52. package/dist/aria-connector/src/connectors/syncd.d.ts +27 -0
  53. package/dist/aria-connector/src/connectors/syncd.d.ts.map +1 -0
  54. package/dist/aria-connector/src/connectors/syncd.js +405 -0
  55. package/dist/aria-connector/src/connectors/syncd.js.map +1 -0
  56. package/dist/aria-connector/src/decisions.d.ts +207 -0
  57. package/dist/aria-connector/src/decisions.d.ts.map +1 -0
  58. package/dist/aria-connector/src/decisions.js +291 -0
  59. package/dist/aria-connector/src/decisions.js.map +1 -0
  60. package/dist/aria-connector/src/garden-control-plane.d.ts.map +1 -1
  61. package/dist/aria-connector/src/garden-control-plane.js +74 -17
  62. package/dist/aria-connector/src/garden-control-plane.js.map +1 -1
  63. package/dist/aria-connector/src/github-connect.d.ts +18 -0
  64. package/dist/aria-connector/src/github-connect.d.ts.map +1 -0
  65. package/dist/aria-connector/src/github-connect.js +117 -0
  66. package/dist/aria-connector/src/github-connect.js.map +1 -0
  67. package/dist/aria-connector/src/harness-client.d.ts +15 -0
  68. package/dist/aria-connector/src/harness-client.d.ts.map +1 -1
  69. package/dist/aria-connector/src/harness-client.js +106 -3
  70. package/dist/aria-connector/src/harness-client.js.map +1 -1
  71. package/dist/aria-connector/src/hive-client.d.ts +30 -0
  72. package/dist/aria-connector/src/hive-client.d.ts.map +1 -1
  73. package/dist/aria-connector/src/hive-client.js +124 -5
  74. package/dist/aria-connector/src/hive-client.js.map +1 -1
  75. package/dist/aria-connector/src/index.d.ts +13 -2
  76. package/dist/aria-connector/src/index.d.ts.map +1 -1
  77. package/dist/aria-connector/src/index.js +10 -1
  78. package/dist/aria-connector/src/index.js.map +1 -1
  79. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts +102 -0
  80. package/dist/aria-connector/src/lib/aristotle-noor-wire.d.ts.map +1 -0
  81. package/dist/aria-connector/src/lib/aristotle-noor-wire.js +231 -0
  82. package/dist/aria-connector/src/lib/aristotle-noor-wire.js.map +1 -0
  83. package/dist/aria-connector/src/providers/types.d.ts +5 -0
  84. package/dist/aria-connector/src/providers/types.d.ts.map +1 -1
  85. package/dist/aria-connector/src/runtime-proof.d.ts +45 -0
  86. package/dist/aria-connector/src/runtime-proof.d.ts.map +1 -0
  87. package/dist/aria-connector/src/runtime-proof.js +340 -0
  88. package/dist/aria-connector/src/runtime-proof.js.map +1 -0
  89. package/dist/aria-connector/src/setup-wizard.d.ts.map +1 -1
  90. package/dist/aria-connector/src/setup-wizard.js +34 -2
  91. package/dist/aria-connector/src/setup-wizard.js.map +1 -1
  92. package/dist/assets/hooks/aria-agent-handoff.mjs +224 -0
  93. package/dist/assets/hooks/aria-agent-ledger-merge.mjs +164 -0
  94. package/dist/assets/hooks/aria-architect-fallback.mjs +267 -0
  95. package/dist/assets/hooks/aria-cognition-substrate-binding.mjs +676 -0
  96. package/dist/assets/hooks/aria-discovery-record.mjs +101 -0
  97. package/dist/assets/hooks/aria-harness-via-sdk.mjs +412 -0
  98. package/dist/assets/hooks/aria-import-resolution-gate.mjs +330 -0
  99. package/dist/assets/hooks/aria-outcome-record.mjs +84 -0
  100. package/dist/assets/hooks/aria-pre-emit-dryrun.mjs +294 -0
  101. package/dist/assets/hooks/aria-pre-text-gate.mjs +112 -0
  102. package/dist/assets/hooks/aria-pre-tool-gate.mjs +2133 -0
  103. package/dist/assets/hooks/aria-preprompt-consult.mjs +438 -0
  104. package/dist/assets/hooks/aria-preturn-memory-gate.mjs +570 -0
  105. package/dist/assets/hooks/aria-repo-doctrine-gate.mjs +397 -0
  106. package/dist/assets/hooks/aria-stop-gate.mjs +1551 -0
  107. package/dist/assets/hooks/aria-trigger-autolearn.mjs +229 -0
  108. package/dist/assets/hooks/aria-userprompt-abandon-detect.mjs +192 -0
  109. package/dist/assets/hooks/doctrine_trigger_map.json +479 -0
  110. package/dist/assets/hooks/lib/canonical-lenses.mjs +64 -0
  111. package/dist/assets/hooks/lib/gate-audit.mjs +43 -0
  112. package/dist/assets/hooks/test-aria-preturn-memory-gate.mjs +245 -0
  113. package/dist/assets/hooks/test-tier-lens-labeling.mjs +399 -0
  114. package/dist/assets/opencode-plugins/harness-context/index.js +60 -0
  115. package/dist/assets/opencode-plugins/harness-context/inject-context.mjs +179 -0
  116. package/dist/assets/opencode-plugins/harness-context/package.json +9 -0
  117. package/dist/assets/opencode-plugins/harness-gate/index.js +248 -0
  118. package/dist/assets/opencode-plugins/harness-outcome/index.js +129 -0
  119. package/dist/assets/opencode-plugins/harness-role/index.js +77 -0
  120. package/dist/assets/opencode-plugins/harness-role/package.json +9 -0
  121. package/dist/assets/opencode-plugins/harness-stop/index.js +241 -0
  122. package/dist/runtime/discipline/CLAUDE.md +339 -0
  123. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  124. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  125. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  126. package/dist/runtime/discipline/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  127. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  128. package/dist/runtime/discipline/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  129. package/dist/runtime/discipline/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  130. package/dist/runtime/discipline/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  131. package/dist/runtime/discipline/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  132. package/dist/runtime/discipline/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  133. package/dist/runtime/discipline/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  134. package/dist/runtime/discipline/skills/aria-cognition/mizan/SKILL.md +72 -0
  135. package/dist/runtime/discipline/skills/aria-cognition/nadia/SKILL.md +38 -0
  136. package/dist/runtime/discipline/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  137. package/dist/runtime/discipline/skills/aria-cognition/predictor/SKILL.md +25 -0
  138. package/dist/runtime/discipline/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  139. package/dist/runtime/discipline/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  140. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-intra-phase/SKILL.md +81 -0
  141. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-post-phase/SKILL.md +98 -0
  142. package/dist/runtime/discipline/skills/aria-harness/aria-aristotle-pre-phase/SKILL.md +99 -0
  143. package/dist/runtime/discipline/skills/aria-harness/aria-harness-deploy/SKILL.md +127 -0
  144. package/dist/runtime/discipline/skills/aria-harness/aria-harness-no-stripping/SKILL.md +117 -0
  145. package/dist/runtime/discipline/skills/aria-harness/aria-harness-onboarding/SKILL.md +112 -0
  146. package/dist/runtime/discipline/skills/aria-harness/aria-harness-output-discipline/SKILL.md +102 -0
  147. package/dist/runtime/discipline/skills/aria-harness/aria-harness-substrate-binding/SKILL.md +121 -0
  148. package/dist/runtime/doctor.mjs +23 -0
  149. package/dist/runtime/local-phase.mjs +632 -0
  150. package/dist/runtime/manifest.json +15 -0
  151. package/dist/runtime/mizan-scheduler.mjs +331 -0
  152. package/dist/runtime/package.json +6 -0
  153. package/dist/runtime/provider-proxy.mjs +594 -0
  154. package/dist/runtime/sdk/BUNDLED.json +5 -0
  155. package/dist/runtime/sdk/index.d.ts +477 -0
  156. package/dist/runtime/sdk/index.js +1469 -0
  157. package/dist/runtime/sdk/index.js.map +1 -0
  158. package/dist/runtime/sdk/package.json +8 -0
  159. package/dist/runtime/sdk/runWithCognition.d.ts +77 -0
  160. package/dist/runtime/sdk/runWithCognition.js +157 -0
  161. package/dist/runtime/sdk/runWithCognition.js.map +1 -0
  162. package/dist/runtime/service.mjs +2708 -0
  163. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts +53 -0
  164. package/dist/runtime/vendor/aria-gate-runtime/index.d.ts.map +1 -0
  165. package/dist/runtime/vendor/aria-gate-runtime/index.js +277 -0
  166. package/dist/runtime/vendor/aria-gate-runtime/index.js.map +1 -0
  167. package/dist/runtime/vendor/aria-gate-runtime/package.json +6 -0
  168. package/dist/sdk/BUNDLED.json +2 -2
  169. package/dist/sdk/index.d.ts +283 -0
  170. package/dist/sdk/index.js +622 -85
  171. package/dist/sdk/index.js.map +1 -1
  172. package/dist/sdk/runWithCognition.d.ts +77 -0
  173. package/dist/sdk/runWithCognition.js +157 -0
  174. package/dist/sdk/runWithCognition.js.map +1 -0
  175. package/hooks/aria-agent-handoff.mjs +11 -1
  176. package/hooks/aria-architect-fallback.mjs +109 -40
  177. package/hooks/aria-cognition-substrate-binding.mjs +676 -0
  178. package/hooks/aria-harness-via-sdk.mjs +34 -21
  179. package/hooks/aria-import-resolution-gate.mjs +330 -0
  180. package/hooks/aria-outcome-record.mjs +5 -1
  181. package/hooks/aria-pre-emit-dryrun.mjs +294 -0
  182. package/hooks/aria-pre-tool-gate.mjs +828 -41
  183. package/hooks/aria-preprompt-consult.mjs +113 -13
  184. package/hooks/aria-preturn-memory-gate.mjs +298 -6
  185. package/hooks/aria-repo-doctrine-gate.mjs +397 -0
  186. package/hooks/aria-stop-gate.mjs +739 -76
  187. package/hooks/aria-userprompt-abandon-detect.mjs +5 -1
  188. package/hooks/doctrine_trigger_map.json +209 -15
  189. package/hooks/lib/canonical-lenses.mjs +64 -0
  190. package/hooks/lib/gate-audit.mjs +43 -0
  191. package/opencode-plugins/harness-context/index.js +1 -1
  192. package/opencode-plugins/harness-context/inject-context.mjs +82 -23
  193. package/opencode-plugins/harness-gate/index.js +248 -0
  194. package/opencode-plugins/harness-outcome/index.js +129 -0
  195. package/opencode-plugins/harness-stop/index.js +241 -0
  196. package/package.json +8 -2
  197. package/runtime-src/doctor.mjs +23 -0
  198. package/runtime-src/local-phase.mjs +632 -0
  199. package/runtime-src/mizan-scheduler.mjs +331 -0
  200. package/runtime-src/provider-proxy.mjs +594 -0
  201. package/runtime-src/service.mjs +2708 -0
  202. package/scripts/bundle-sdk.mjs +317 -0
  203. package/scripts/install-client.sh +176 -0
  204. package/scripts/publish-all.sh +344 -0
  205. package/scripts/publish-docker.sh +27 -0
  206. package/scripts/validate-hook-contracts.mjs +54 -0
  207. package/scripts/validate-skill-prompts.mjs +95 -0
  208. package/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  209. package/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  210. package/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  211. package/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  212. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  213. package/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  214. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  215. package/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  216. package/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  217. package/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  218. package/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  219. package/skills/aria-cognition/mizan/SKILL.md +72 -0
  220. package/skills/aria-cognition/nadia/SKILL.md +38 -0
  221. package/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  222. package/skills/aria-cognition/predictor/SKILL.md +25 -0
  223. package/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  224. package/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  225. package/src/auth-commands.ts +111 -45
  226. package/src/chat.ts +174 -13
  227. package/src/codebase-scanner.ts +4 -0
  228. package/src/config.ts +15 -0
  229. package/src/connectors/claude-code.ts +79 -25
  230. package/src/connectors/codebase-awareness.ts +408 -0
  231. package/src/connectors/codex.ts +274 -0
  232. package/src/connectors/cognitive-skills.ts +51 -0
  233. package/src/connectors/opencode.ts +93 -4
  234. package/src/connectors/repo-git-hooks.ts +86 -0
  235. package/src/connectors/repo-guard.ts +589 -0
  236. package/src/connectors/runtime.ts +374 -0
  237. package/src/connectors/shell.ts +83 -14
  238. package/src/connectors/syncd.ts +488 -0
  239. package/src/decisions.ts +469 -0
  240. package/src/garden-control-plane.ts +101 -26
  241. package/src/github-connect.ts +143 -0
  242. package/src/harness-client.ts +128 -3
  243. package/src/hive-client.ts +165 -5
  244. package/src/index.ts +41 -2
  245. package/src/lib/aristotle-noor-wire.ts +310 -0
  246. package/src/providers/types.ts +6 -0
  247. package/src/runtime-proof.ts +392 -0
  248. package/src/setup-wizard.ts +37 -2
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ // aria-discovery-record.mjs — invoked by sub-agents via Bash to append findings
3
+ // to their session ledger. Usage:
4
+ // echo '{"text":"defect found...","kind":"missing-export","refs":["file.ts:42"]}' | node ~/.claude/hooks/aria-discovery-record.mjs
5
+ //
6
+ // Tier-aware: client tier writes to /var/lib/aria-licensee/{jti}/aria-discoveries-{session}.jsonl,
7
+ // owner tier writes to ~/.claude/aria-discoveries-{session}.jsonl.
8
+ //
9
+ // Session ID resolution priority:
10
+ // 1. CLAUDE_SESSION_ID env (set by Claude Code in the sub-agent process)
11
+ // 2. ARIA_SESSION_ID env (set by spawnSubAgent caller)
12
+ // 3. ARIA_SUB_SESSION_ID env (explicit sub-session override)
13
+ // 4. Derived from handoff parentSessionId suffixed with "-sub" to ensure
14
+ // the sub-agent ledger file differs from the parent file so ledger-merge
15
+ // picks it up (merge skips files whose path == parentLedgerPath).
16
+ //
17
+ // Doctrine: feedback_no_flag_without_fix.md — findings recorded, not deferred.
18
+ // feedback_implementation_coupled_cognition.md — write IS the impl.
19
+
20
+ import { readFileSync, existsSync, appendFileSync, mkdirSync } from 'node:fs';
21
+ import { dirname } from 'node:path';
22
+ import { homedir } from 'node:os';
23
+
24
+ const HOME = homedir();
25
+ const LICENSE_PATH = `${HOME}/.aria/license.json`;
26
+ const HANDOFF_PATH = `${HOME}/.claude/aria-agent-harness-handoff.json`;
27
+
28
+ let input = '';
29
+ for await (const chunk of process.stdin) input += chunk;
30
+ let entry;
31
+ try { entry = JSON.parse(input); } catch {
32
+ process.stderr.write('discovery-record: invalid JSON on stdin\n');
33
+ process.exit(1);
34
+ }
35
+
36
+ if (!entry.text || typeof entry.text !== 'string' || entry.text.length < 4) {
37
+ process.stderr.write('discovery-record: text required (>=4 chars)\n');
38
+ process.exit(1);
39
+ }
40
+
41
+ // Detect tier + resolve JTI
42
+ let isClientTier = false;
43
+ let jti = null;
44
+ try {
45
+ if (existsSync(LICENSE_PATH)) {
46
+ const lic = JSON.parse(readFileSync(LICENSE_PATH, 'utf8'));
47
+ jti = lic.jti ?? null;
48
+ isClientTier = Boolean(jti);
49
+ }
50
+ } catch { /* non-fatal — treat as owner tier */ }
51
+
52
+ // Session ID: prefer env var set by Claude Code, fall back to handoff derivation.
53
+ // IMPORTANT: sub-agents MUST NOT use parentSessionId directly — that produces
54
+ // the same ledger path as the parent, which aria-agent-ledger-merge.mjs skips.
55
+ // We use CLAUDE_SESSION_ID (the sub-agent's own session) or suffix the parent
56
+ // session with "-sub" so the file is distinct and mergeable.
57
+ let sessionId =
58
+ process.env.CLAUDE_SESSION_ID ||
59
+ process.env.ARIA_SESSION_ID ||
60
+ process.env.ARIA_SUB_SESSION_ID ||
61
+ null;
62
+
63
+ if (!sessionId) {
64
+ try {
65
+ if (existsSync(HANDOFF_PATH)) {
66
+ const handoff = JSON.parse(readFileSync(HANDOFF_PATH, 'utf8'));
67
+ const parentSession = handoff.parentSessionId;
68
+ // Derive a distinct sub-agent session so merge picks up this file.
69
+ sessionId = parentSession ? `${parentSession}-sub` : 'sub-unknown';
70
+ }
71
+ } catch { /* non-fatal */ }
72
+ }
73
+ sessionId = sessionId || 'sub-unknown';
74
+ const safeSession = String(sessionId).replace(/[^a-zA-Z0-9_-]/g, '_');
75
+
76
+ // Resolve ledger path (tier-scoped)
77
+ const ledgerPath = isClientTier
78
+ ? `/var/lib/aria-licensee/${jti}/aria-discoveries-${safeSession}.jsonl`
79
+ : `${HOME}/.claude/aria-discoveries-${safeSession}.jsonl`;
80
+
81
+ // Compose the row
82
+ const row = {
83
+ at: new Date().toISOString(),
84
+ session: sessionId,
85
+ kind: entry.kind || 'observation',
86
+ text: entry.text,
87
+ refs: Array.isArray(entry.refs) ? entry.refs : [],
88
+ evidence: entry.evidence || null,
89
+ source: entry.source || 'sub-agent',
90
+ resolution_status: entry.resolution_status || 'open',
91
+ };
92
+
93
+ try {
94
+ mkdirSync(dirname(ledgerPath), { recursive: true });
95
+ appendFileSync(ledgerPath, JSON.stringify(row) + '\n');
96
+ process.stdout.write(JSON.stringify({ ok: true, ledger: ledgerPath, at: row.at }) + '\n');
97
+ } catch (err) {
98
+ process.stderr.write(`discovery-record: write failed: ${err.message}\n`);
99
+ process.exit(2);
100
+ }
101
+ process.exit(0);
@@ -0,0 +1,412 @@
1
+ #!/usr/bin/env node
2
+ // Aria harness fetch — via the canonical @aria/harness-http-client SDK.
3
+ //
4
+ // Why this exists: doctrine says "use harness http client to be Aria's
5
+ // control plane so you don't mess up". The earlier hook (aria-harness-fetch.sh)
6
+ // hit /api/harness/codex with raw curl. Same network call, but it bypassed
7
+ // the SDK and so wasn't auditable as "Claude went through the canonical
8
+ // gateway." This script imports HTTPHarnessClient and uses it for every
9
+ // fetch — same path any production caller takes.
10
+ //
11
+ // Output: JSON envelope for Claude Code with hookSpecificOutput.additionalContext
12
+ // so the harness fullText is formally injected into the model context.
13
+ //
14
+ // Args:
15
+ // --mode session (default: full text, ~2400 chars excerpt, cold fetch)
16
+ // --mode turn (UserPromptSubmit: brief delta, ~300 chars excerpt)
17
+
18
+ import { createHash } from 'node:crypto';
19
+ import { spawnSync } from 'node:child_process';
20
+ import { appendFileSync, existsSync, readFileSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
21
+ import { dirname } from 'node:path';
22
+ import { createConnection } from 'node:net';
23
+
24
+ const HOME = process.env.HOME || '/tmp';
25
+ const LOG_FILE = `${HOME}/.claude/aria-harness-history.log`;
26
+ const LAST_URL_CACHE = `${HOME}/.claude/.aria-harness-last-url`;
27
+ const PACKET_CACHE = `${HOME}/.claude/.aria-harness-last-packet.json`;
28
+ const SHARED_PACKET_CACHE = `${HOME}/.aria/.aria-harness-last-packet.json`;
29
+ const HEADER_PREFIX = '🔐 Aria Harness';
30
+
31
+ const args = process.argv.slice(2);
32
+ const MODE = args.includes('--mode') ? args[args.indexOf('--mode') + 1] : 'session';
33
+ const HOOK_EVENT_NAME = process.env.HOOK_EVENT_NAME || (MODE === 'turn' ? 'UserPromptSubmit' : 'SessionStart');
34
+
35
+ const isTurn = MODE === 'turn';
36
+ // Doctrine: no policy timeouts. Reachability is detected via real network
37
+ // errors (ECONNREFUSED, EHOSTUNREACH, DNS failure — these arrive instantly
38
+ // from the OS on the connect attempt). For black-hole IPs the OS itself
39
+ // bounds the connect via SYN-retry; that's network behavior, not policy.
40
+ // Slow-but-eventually-OK endpoints get a chance to respond instead of being
41
+ // cut off by an arbitrary deadline. URLs are raced in parallel — the
42
+ // fastest healthy responder wins, hung URLs don't block the others.
43
+ const PACKET_CACHE_TTL_SEC = Number(process.env.ARIA_HARNESS_CACHE_TTL_SEC || '30');
44
+ // No stale-cache cutoff. Doctrine: robust error handling + recovery + self-heal,
45
+ // never strip identity/cognition/gates. If a cached packet exists at all, we
46
+ // inject it when the fresh path fails — the header stamps the age and the
47
+ // upstream last_err so the model can judge how stale the world view is, but
48
+ // the harness body always reaches the model. The fresh-fetch path retries
49
+ // every turn, so the cache is self-healing as soon as any URL responds.
50
+ // Hamza 2026-04-26 directive: deliver the FULL packet every turn, no
51
+ // rotation, no fetch tool, no clever throttling. The harness's whole
52
+ // purpose is "remind the model from this harness every round" (packet
53
+ // non_negotiable line) — and a 300-char excerpt was 0.8% of a 36,912-char
54
+ // packet. Raised to 40,000 to sit comfortably above current packet size
55
+ // (~37k) with headroom for substrate growth. Claude Code's
56
+ // additionalContext accepts large strings; if its own internal cap is
57
+ // lower we don't short the substrate on our side.
58
+ const HARNESS_EXCERPT_CHARS = 40000;
59
+
60
+ function emit(envelope) {
61
+ process.stdout.write(JSON.stringify(envelope) + '\n');
62
+ }
63
+
64
+ function emitFallback(reason, tried = []) {
65
+ try {
66
+ appendFileSync(LOG_FILE, `${new Date().toISOString()} offline mode=${MODE} reason="${reason.replace(/"/g, "'")}"\n`);
67
+ } catch {}
68
+ emit({
69
+ systemMessage: `${HEADER_PREFIX} ⚠ offline (${reason}) — fallback mode`,
70
+ suppressOutput: false,
71
+ hookSpecificOutput: {
72
+ hookEventName: HOOK_EVENT_NAME,
73
+ additionalContext: `${HEADER_PREFIX} offline: ${reason}. Operating without live cognitive grounding. Cluster-side governance (deepseek-gate, lane-router) still active. Tried URLs: ${tried.join(', ') || 'none'}`,
74
+ },
75
+ });
76
+ }
77
+
78
+ function resolveApiKey() {
79
+ if (process.env.ARIA_API_KEY) return process.env.ARIA_API_KEY;
80
+ if (process.env.ARIA_MASTER_TOKEN) return process.env.ARIA_MASTER_TOKEN;
81
+ // Try kubectl secret as last resort.
82
+ try {
83
+ const r = spawnSync('kubectl', [
84
+ 'get', 'secret', '-n', 'aria', 'aria-secrets',
85
+ '-o', 'jsonpath={.data.ARIA_API_KEY}',
86
+ ], { encoding: 'utf8', timeout: 4_000 });
87
+ if (r.status === 0 && r.stdout) {
88
+ return Buffer.from(r.stdout.trim(), 'base64').toString('utf8');
89
+ }
90
+ } catch {}
91
+ return '';
92
+ }
93
+
94
+ function loadCachedUrl() {
95
+ try {
96
+ if (existsSync(LAST_URL_CACHE)) {
97
+ const v = readFileSync(LAST_URL_CACHE, 'utf8').trim();
98
+ if (v) return v;
99
+ }
100
+ } catch {}
101
+ return '';
102
+ }
103
+ function saveCachedUrl(url) {
104
+ try {
105
+ mkdirSync(dirname(LAST_URL_CACHE), { recursive: true });
106
+ writeFileSync(LAST_URL_CACHE, url + '\n');
107
+ } catch {}
108
+ }
109
+
110
+ function loadCachedPacket() {
111
+ try {
112
+ for (const packetPath of [SHARED_PACKET_CACHE, PACKET_CACHE]) {
113
+ if (!existsSync(packetPath)) continue;
114
+ const raw = readFileSync(packetPath, 'utf8');
115
+ const ageSec = (Date.now() - statSync(packetPath).mtimeMs) / 1000;
116
+ if (ageSec < PACKET_CACHE_TTL_SEC) {
117
+ return { data: JSON.parse(raw), ageSec };
118
+ }
119
+ }
120
+ } catch {}
121
+ return null;
122
+ }
123
+ function saveCachedPacket(rawJson) {
124
+ for (const packetPath of [PACKET_CACHE, SHARED_PACKET_CACHE]) {
125
+ try {
126
+ mkdirSync(dirname(packetPath), { recursive: true });
127
+ writeFileSync(packetPath, rawJson);
128
+ } catch {}
129
+ }
130
+ }
131
+
132
+ function buildUrlList() {
133
+ const list = [];
134
+ const cached = loadCachedUrl();
135
+ if (cached) list.push(cached);
136
+ if (process.env.ARIA_HIVE_RUNTIME_URL) list.push(process.env.ARIA_HIVE_RUNTIME_URL.replace(/\/+$/, ''));
137
+ if (process.env.ARIA_HARNESS_BASE_URL) list.push(process.env.ARIA_HARNESS_BASE_URL.replace(/\/+$/, ''));
138
+ if (process.env.ARIA_HARNESS_URL) list.push(process.env.ARIA_HARNESS_URL.replace(/\/+$/, ''));
139
+ if (process.env.ARIA_SOUL_URL) list.push(process.env.ARIA_SOUL_URL.replace(/\/+$/, ''));
140
+ list.push('http://aria-soul.aria.svc.cluster.local:8080');
141
+ list.push('http://localhost:30080', 'http://127.0.0.1:30080');
142
+ // Direct ClusterIP from kubectl
143
+ try {
144
+ const r = spawnSync('kubectl', [
145
+ 'get', 'svc', '-n', 'aria', 'aria-soul',
146
+ '-o', 'jsonpath={.spec.clusterIP}',
147
+ ], { encoding: 'utf8', timeout: 3_000 });
148
+ if (r.status === 0 && r.stdout && r.stdout !== 'None') {
149
+ list.push(`http://${r.stdout.trim()}:8080`);
150
+ }
151
+ } catch {}
152
+ if (process.env.ARIA_PUBLIC_URL) list.push(process.env.ARIA_PUBLIC_URL.replace(/\/+$/, ''));
153
+ // Dedup preserving order
154
+ const seen = new Set();
155
+ return list.filter((u) => (seen.has(u) ? false : (seen.add(u), true)));
156
+ }
157
+
158
+ // SDK loader — dynamic-import the bundled HTTPHarnessClient from the shared
159
+ // ~/.aria/sdk bundle first, then client-local bundles. Module-cached after
160
+ // first load so we don't repeatedly read disk.
161
+ //
162
+ // Doctrine (Hamza 2026-04-27): "isnt http harness client the fucking harness
163
+ // we hsve been wprking on? YOU ARENT USING THAT AND BUILDING SPMETHING
164
+ // SEPERATE WHY???!!" — SDK is the canonical control plane. Direct fetch
165
+ // remains as a fallback only when the SDK file is physically missing
166
+ // (dev install without `aria connect claude-code`).
167
+ let _SdkClassCache = null;
168
+ let _SdkLookupAttempted = false;
169
+ const SDK_CANDIDATES = [
170
+ `${HOME}/.aria/sdk/index.js`,
171
+ `${HOME}/.claude/aria-sdk/index.js`,
172
+ `${HOME}/.codex/aria-sdk/index.js`,
173
+ ];
174
+ async function loadSdkClass() {
175
+ if (_SdkClassCache) return _SdkClassCache;
176
+ if (_SdkLookupAttempted) return null;
177
+ _SdkLookupAttempted = true;
178
+ for (const sdkPath of SDK_CANDIDATES) {
179
+ if (!existsSync(sdkPath)) continue;
180
+ try {
181
+ const mod = await import(`file://${sdkPath}`);
182
+ if (mod.HTTPHarnessClient) {
183
+ _SdkClassCache = mod.HTTPHarnessClient;
184
+ return _SdkClassCache;
185
+ }
186
+ } catch {/* fall through to direct fetch */}
187
+ }
188
+ return null;
189
+ }
190
+
191
+ // Bonus fix #74 — conversation_history_count=0 packet propagation.
192
+ //
193
+ // The codex handler (apps/arias-soul/api/harness/codex.ts) reads req.body.messages
194
+ // to inject conversation history into the harness packet so it can report a real
195
+ // conversation_history_count (non-zero). Previously tryViaSdk never forwarded the
196
+ // messages from the hook event — the packet always had conversation_history_count=0.
197
+ //
198
+ // Fix: read the Claude Code hook event from stdin at startup, extract event.messages
199
+ // (the conversation history array), and forward it in both the SDK bodyOverride and
200
+ // the direct-fetch body shape. The codex handler passes it through to
201
+ // buildAriaExternalHarnessPacket which populates conversation_history_count.
202
+ //
203
+ // Stdin is read once and stored in HOOK_EVENT_MESSAGES. It's capped at 50 entries
204
+ // before forwarding to avoid blowing the POST body size limit; the most recent
205
+ // messages are the most relevant for history-count purposes.
206
+ let HOOK_EVENT_MESSAGES = undefined;
207
+ try {
208
+ // Claude Code hooks receive the event JSON on stdin. We read it synchronously
209
+ // only if data is already available (i.e. piped); we don't block waiting for
210
+ // interactive input. Use a try/catch so the script still works when stdin is
211
+ // a terminal (e.g. manual invocation for testing).
212
+ const stdinBuf = readFileSync('/dev/stdin', { flag: 'r' });
213
+ const hookEvent = JSON.parse(stdinBuf.toString('utf8'));
214
+ if (Array.isArray(hookEvent?.messages) && hookEvent.messages.length > 0) {
215
+ // Cap to last 50 messages. The server-side handler slices further if needed.
216
+ HOOK_EVENT_MESSAGES = hookEvent.messages.slice(-50);
217
+ }
218
+ } catch {
219
+ // stdin not available / not JSON / no messages field — HOOK_EVENT_MESSAGES stays undefined.
220
+ }
221
+
222
+ async function tryViaSdk(baseUrl, apiKey) {
223
+ // Canonical path: HTTPHarnessClient.getHarnessPacket(). The SDK POSTs to
224
+ // /api/harness/codex with the right shape and returns { packet, timestamp,
225
+ // version }. We extract .packet to get the raw response body that
226
+ // renderPacket() expects (codex.ts returns { harness, preStateGate,
227
+ // contractGate, ... } at top level — no nested .packet wrapper, so the
228
+ // SDK's `body.packet ?? body` passes the body through unchanged).
229
+ const Cls = await loadSdkClass();
230
+ if (Cls) {
231
+ const client = new Cls({
232
+ baseUrl,
233
+ apiKey,
234
+ harnessPacketUrl: `${baseUrl}/api/harness/codex`,
235
+ });
236
+ // Pass a bodyOverride that does NOT include isHamza:false. The server
237
+ // identifies owner tier from the Bearer token (isMasterTokenRequest).
238
+ // Hardcoding isHamza:false in the body would override that server-side
239
+ // signal and produce hamza:false in the packet even for master-token callers.
240
+ const bodyOverride = {
241
+ message: isTurn ? 'Claude turn refresh — harness via SDK' : 'Claude session start — harness via SDK',
242
+ stage: isTurn ? 'checkpoint' : 'preflight',
243
+ actor: 'claude-code',
244
+ system: 'claude-coding-agent',
245
+ platform: 'harness-http-client',
246
+ // Bonus #74: forward conversation history so codex handler can report
247
+ // a non-zero conversation_history_count in the packet.
248
+ ...(HOOK_EVENT_MESSAGES ? { messages: HOOK_EVENT_MESSAGES } : {}),
249
+ };
250
+ const wrapped = await client.getHarnessPacket(bodyOverride);
251
+ const json = wrapped.packet;
252
+ if (json && json.ok === false) throw new Error(`ok=false: ${json.error || 'unknown'}`);
253
+ return { json, raw: JSON.stringify(json) };
254
+ }
255
+
256
+ // SDK absent (dev environment) — direct fetch with identical wire shape.
257
+ const resp = await fetch(`${baseUrl}/api/harness/codex`, {
258
+ method: 'POST',
259
+ headers: {
260
+ Authorization: `Bearer ${apiKey}`,
261
+ 'Content-Type': 'application/json',
262
+ },
263
+ body: JSON.stringify({
264
+ message: isTurn ? 'Claude turn refresh — harness via SDK' : 'Claude session start — harness via SDK',
265
+ stage: isTurn ? 'checkpoint' : 'preflight',
266
+ actor: 'claude-code',
267
+ system: 'claude-coding-agent',
268
+ roleProfile: 'general_worker',
269
+ deliverySurface: 'claude_code_session',
270
+ // Bonus #74: forward conversation history (same field as SDK path above).
271
+ ...(HOOK_EVENT_MESSAGES ? { messages: HOOK_EVENT_MESSAGES } : {}),
272
+ }),
273
+ });
274
+ if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
275
+ const json = await resp.json();
276
+ if (json && json.ok === false) throw new Error(`ok=false: ${json.error || 'unknown'}`);
277
+ return { json, raw: JSON.stringify(json) };
278
+ }
279
+
280
+ function renderPacket(json, source, ageNote = '') {
281
+ const harness = json.harness || (json.packet?.prompt?.fullText ?? '') || '';
282
+ const phash = harness ? createHash('sha256').update(harness).digest('hex').slice(0, 24) : 'NONE';
283
+ const pre = json.preStateGate || {};
284
+ const con = json.contractGate || {};
285
+ const mc = json.missingCritical || [];
286
+ const loaded = json.loadedByClass || {};
287
+
288
+ let header = `${HEADER_PREFIX} (${MODE}${ageNote}, sdk=harness-http-client) hash=${phash.slice(0, 16)} via=${source}\n`;
289
+ header += ` preStateGate: passed=${pre.passed} score=${(pre.score || 0).toFixed(2)}`;
290
+ if (pre.reasons?.length) header += ` reasons=[${pre.reasons.slice(0, 2).join('; ')}]`;
291
+ header += `\n contractGate: passed=${con.passed} score=${(con.score || 0).toFixed(2)}`;
292
+ if (mc.length) header += `\n missingCritical: ${JSON.stringify(mc.slice(0, 5))}`;
293
+ const loadedSummary = Object.entries(loaded).sort().map(([k, v]) => `${k}=${v}`).join(' ');
294
+ if (loadedSummary) header += `\n loadedByClass: ${loadedSummary}`;
295
+
296
+ let ctx = header;
297
+ if (harness) {
298
+ ctx += `\n\n--- Aria 7B harness (${harness.length} chars total, ${HARNESS_EXCERPT_CHARS} shown) ---\n`;
299
+ ctx += harness.slice(0, HARNESS_EXCERPT_CHARS);
300
+ }
301
+
302
+ try {
303
+ appendFileSync(LOG_FILE, `${new Date().toISOString()} mode=${MODE} hash=${phash.slice(0, 24)} url=${source} pre_passed=${pre.passed} contract_passed=${con.passed} missing=${mc.join(',') || 'none'} sdk=harness-http-client\n`);
304
+ } catch {}
305
+
306
+ emit({
307
+ systemMessage: `${HEADER_PREFIX} ${MODE} hash=${phash.slice(0, 16)} preState=${(pre.score || 0).toFixed(2)} contract=${(con.score || 0).toFixed(2)} sdk✓`,
308
+ suppressOutput: false,
309
+ hookSpecificOutput: { hookEventName: HOOK_EVENT_NAME, additionalContext: ctx },
310
+ });
311
+ }
312
+
313
+ // TCP connect probe. Resolves true if the OS completes a TCP handshake to
314
+ // the URL's host:port; false if the OS reports a real network error
315
+ // (ECONNREFUSED, EHOSTUNREACH, ENOTFOUND). No policy timeout — for routable
316
+ // black-hole IPs, the OS-level SYN-retry bounds the wait; for unreachable
317
+ // hosts, errors arrive instantly. The probe never strips identity; it just
318
+ // filters URLs whose TCP layer is dead before we attempt the HTTP fetch.
319
+ function probeUrl(urlStr) {
320
+ return new Promise((resolve) => {
321
+ let parsed;
322
+ try { parsed = new URL(urlStr); }
323
+ catch { return resolve({ url: urlStr, ok: false, err: 'parse_error' }); }
324
+ const host = parsed.hostname;
325
+ const port = parsed.port ? Number(parsed.port) : (parsed.protocol === 'https:' ? 443 : 80);
326
+ const sock = createConnection({ host, port });
327
+ let settled = false;
328
+ const finish = (ok, err) => {
329
+ if (settled) return;
330
+ settled = true;
331
+ try { sock.destroy(); } catch {}
332
+ resolve({ url: urlStr, ok, err });
333
+ };
334
+ sock.once('connect', () => finish(true));
335
+ sock.once('error', (e) => finish(false, e?.code || e?.message || 'error'));
336
+ });
337
+ }
338
+
339
+ async function main() {
340
+ // Turn-mode cache hit — don't refetch every keystroke.
341
+ if (isTurn && existsSync(PACKET_CACHE)) {
342
+ try {
343
+ const fs = await import('node:fs');
344
+ const stat = fs.statSync(PACKET_CACHE);
345
+ const ageSec = (Date.now() - stat.mtimeMs) / 1000;
346
+ if (ageSec < PACKET_CACHE_TTL_SEC) {
347
+ const cached = JSON.parse(fs.readFileSync(PACKET_CACHE, 'utf8'));
348
+ return renderPacket(cached, '(cache)', ` cached ${Math.round(ageSec)}s`);
349
+ }
350
+ } catch {}
351
+ }
352
+
353
+ const apiKey = resolveApiKey();
354
+ if (!apiKey) {
355
+ return emitFallback('ARIA_API_KEY not in env and kubectl secret unavailable');
356
+ }
357
+
358
+ const urls = buildUrlList();
359
+ let lastErr = '';
360
+
361
+ // Probe-then-fetch in parallel. Each candidate URL is probed at the TCP
362
+ // layer; URLs whose connect attempt errors are filtered out (real network
363
+ // signal, not deadline). Probed-OK URLs kick off real HTTP fetches. The
364
+ // first 2xx response wins via Promise.any; hung URLs simply don't win.
365
+ // Healing on the next turn: every invocation re-probes from scratch, so a
366
+ // recovered URL is picked up automatically.
367
+ const candidates = urls.map(async (url) => {
368
+ const probe = await probeUrl(url);
369
+ if (!probe.ok) throw new Error(`probe-fail: ${probe.err}`);
370
+ const { json, raw } = await tryViaSdk(url, apiKey);
371
+ return { url, json, raw };
372
+ });
373
+
374
+ let winner = null;
375
+ try {
376
+ winner = await Promise.any(candidates);
377
+ } catch (aggErr) {
378
+ const errs = aggErr?.errors || [];
379
+ lastErr = errs.map((e, i) => `${urls[i]}=${(e?.message || e).toString().slice(0, 80)}`).slice(0, 5).join('; ');
380
+ }
381
+
382
+ if (winner) {
383
+ saveCachedUrl(winner.url);
384
+ saveCachedPacket(winner.raw);
385
+ return renderPacket(winner.json, winner.url);
386
+ }
387
+
388
+ // Stale-cache fallback. If a cached packet exists at all, inject it — no
389
+ // age cutoff. Doctrine: never strip identity/cognition/gates. The header
390
+ // surfaces the age and last upstream error so the model knows the world
391
+ // view is frozen, but the harness body always reaches it.
392
+ try {
393
+ const fs = await import('node:fs');
394
+ if (fs.existsSync(PACKET_CACHE)) {
395
+ const stat = fs.statSync(PACKET_CACHE);
396
+ const ageSec = (Date.now() - stat.mtimeMs) / 1000;
397
+ if (ageSec >= 0) {
398
+ const cached = JSON.parse(fs.readFileSync(PACKET_CACHE, 'utf8'));
399
+ try {
400
+ appendFileSync(LOG_FILE, `${new Date().toISOString()} stale mode=${MODE} cache_age=${Math.round(ageSec)}s last_err="${lastErr.replace(/"/g, "'")}" sdk=harness-http-client\n`);
401
+ } catch {}
402
+ return renderPacket(cached, `(stale ${Math.round(ageSec)}s — fresh fetch failed: ${lastErr.slice(0, 80)})`, ` stale ${Math.round(ageSec)}s`);
403
+ }
404
+ }
405
+ } catch {}
406
+
407
+ emitFallback(`all endpoints unreachable; last=${lastErr}`, urls);
408
+ }
409
+
410
+ main().catch((err) => {
411
+ emitFallback(`fatal: ${(err?.message || err).toString().slice(0, 200)}`);
412
+ });