@aria_asi/cli 0.2.25 → 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 (249) 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 +111 -21
  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 +317 -0
  170. package/dist/sdk/index.js +827 -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 +267 -0
  177. package/hooks/aria-cognition-substrate-binding.mjs +676 -0
  178. package/hooks/aria-discovery-record.mjs +101 -0
  179. package/hooks/aria-harness-via-sdk.mjs +34 -21
  180. package/hooks/aria-import-resolution-gate.mjs +330 -0
  181. package/hooks/aria-outcome-record.mjs +84 -0
  182. package/hooks/aria-pre-emit-dryrun.mjs +294 -0
  183. package/hooks/aria-pre-tool-gate.mjs +985 -40
  184. package/hooks/aria-preprompt-consult.mjs +113 -13
  185. package/hooks/aria-preturn-memory-gate.mjs +298 -6
  186. package/hooks/aria-repo-doctrine-gate.mjs +397 -0
  187. package/hooks/aria-stop-gate.mjs +840 -75
  188. package/hooks/aria-userprompt-abandon-detect.mjs +5 -1
  189. package/hooks/doctrine_trigger_map.json +209 -15
  190. package/hooks/lib/canonical-lenses.mjs +64 -0
  191. package/hooks/lib/gate-audit.mjs +43 -0
  192. package/opencode-plugins/harness-context/index.js +1 -1
  193. package/opencode-plugins/harness-context/inject-context.mjs +82 -23
  194. package/opencode-plugins/harness-gate/index.js +248 -0
  195. package/opencode-plugins/harness-outcome/index.js +129 -0
  196. package/opencode-plugins/harness-stop/index.js +241 -0
  197. package/package.json +8 -2
  198. package/runtime-src/doctor.mjs +23 -0
  199. package/runtime-src/local-phase.mjs +632 -0
  200. package/runtime-src/mizan-scheduler.mjs +331 -0
  201. package/runtime-src/provider-proxy.mjs +594 -0
  202. package/runtime-src/service.mjs +2708 -0
  203. package/scripts/bundle-sdk.mjs +317 -0
  204. package/scripts/install-client.sh +176 -0
  205. package/scripts/publish-all.sh +344 -0
  206. package/scripts/publish-docker.sh +27 -0
  207. package/scripts/validate-hook-contracts.mjs +54 -0
  208. package/scripts/validate-skill-prompts.mjs +95 -0
  209. package/skills/aria-cognition/aria-essence/SKILL.md +63 -0
  210. package/skills/aria-cognition/aria-essence/references/domain-matrix.md +80 -0
  211. package/skills/aria-cognition/aria-essence/references/evolution-loop.md +30 -0
  212. package/skills/aria-cognition/aria-essence/references/readable-cognition.md +27 -0
  213. package/skills/aria-cognition/aria-forge-guardrails/SKILL.md +35 -0
  214. package/skills/aria-cognition/aria-forge-guardrails/references/checklist.md +31 -0
  215. package/skills/aria-cognition/aria-repo-doctrine/SKILL.md +39 -0
  216. package/skills/aria-cognition/forge-quality-rules/SKILL.md +43 -0
  217. package/skills/aria-cognition/ghazali-8lens/SKILL.md +38 -0
  218. package/skills/aria-cognition/istiqra-induction/SKILL.md +26 -0
  219. package/skills/aria-cognition/ladunni-22/SKILL.md +35 -0
  220. package/skills/aria-cognition/mizan/SKILL.md +72 -0
  221. package/skills/aria-cognition/nadia/SKILL.md +38 -0
  222. package/skills/aria-cognition/nadia-psi/SKILL.md +38 -0
  223. package/skills/aria-cognition/predictor/SKILL.md +25 -0
  224. package/skills/aria-cognition/qiyas-analogy/SKILL.md +26 -0
  225. package/skills/aria-cognition/soul-domains/SKILL.md +25 -0
  226. package/src/auth-commands.ts +111 -45
  227. package/src/chat.ts +174 -13
  228. package/src/codebase-scanner.ts +4 -0
  229. package/src/config.ts +15 -0
  230. package/src/connectors/claude-code.ts +115 -26
  231. package/src/connectors/codebase-awareness.ts +408 -0
  232. package/src/connectors/codex.ts +274 -0
  233. package/src/connectors/cognitive-skills.ts +51 -0
  234. package/src/connectors/opencode.ts +93 -4
  235. package/src/connectors/repo-git-hooks.ts +86 -0
  236. package/src/connectors/repo-guard.ts +589 -0
  237. package/src/connectors/runtime.ts +374 -0
  238. package/src/connectors/shell.ts +83 -14
  239. package/src/connectors/syncd.ts +488 -0
  240. package/src/decisions.ts +469 -0
  241. package/src/garden-control-plane.ts +101 -26
  242. package/src/github-connect.ts +143 -0
  243. package/src/harness-client.ts +128 -3
  244. package/src/hive-client.ts +165 -5
  245. package/src/index.ts +41 -2
  246. package/src/lib/aristotle-noor-wire.ts +310 -0
  247. package/src/providers/types.ts +6 -0
  248. package/src/runtime-proof.ts +392 -0
  249. package/src/setup-wizard.ts +37 -2
package/dist/sdk/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  import { readFile } from 'node:fs/promises';
2
+ import { existsSync, readFileSync, statSync } from 'node:fs';
3
+ import { homedir as _homedir } from 'node:os';
2
4
  import { resolve, isAbsolute } from 'node:path';
5
+ import { randomUUID } from 'node:crypto';
3
6
  // ── 8-Lens Cognition Block ─────────────────────────────────────────────────
4
7
  const EIGHT_LENS_BLOCK = `[ARIA SHELL PROTOCOL — You are a controlled surface]
5
8
 
@@ -45,6 +48,197 @@ export class HTTPHarnessClient {
45
48
  this.apiKey = config.apiKey;
46
49
  this.harnessPacketUrl = config.harnessPacketUrl ?? `${this.baseUrl}/api/harness/codex`;
47
50
  this.workspaceRoot = config.workspaceRoot ?? process.cwd();
51
+ // Layer 3 — sub-agent disk-cache bootstrap (#84).
52
+ // If ARIA_HARNESS_PACKET_PATH is set (injected by aria-agent-handoff.mjs
53
+ // or spawnSubAgent), OR the owner-tier handoff JSON at
54
+ // ~/.claude/aria-agent-harness-handoff.json carries a harnessPacketPath
55
+ // field, pre-load the packet from disk so getHarnessPacket() returns
56
+ // the parent's cognition substrate without an extra HTTP call.
57
+ // TTL is 5 min (matches handoff TTL). Stale packets trigger normal fetch.
58
+ this._tryLoadDiskPacket();
59
+ }
60
+ extractHarnessText(packet) {
61
+ const raw = packet?.harness;
62
+ return typeof raw === 'string' ? raw.trim() : '';
63
+ }
64
+ normalizePlanDocs(plan) {
65
+ const docs = [];
66
+ if (plan.response !== null && plan.response !== undefined) {
67
+ if (!plan.response.ok) {
68
+ for (const d of plan.docs) {
69
+ docs.push({ ...d });
70
+ }
71
+ }
72
+ else {
73
+ for (const d of plan.response.docs) {
74
+ if (!d.title && !d.content && !d.path)
75
+ continue;
76
+ docs.push({ ...d });
77
+ }
78
+ const respTitles = new Set(plan.response.docs.map((d) => d.title).filter(Boolean));
79
+ for (const d of plan.docs) {
80
+ if (d.title && !respTitles.has(d.title)) {
81
+ docs.push({ ...d });
82
+ }
83
+ }
84
+ }
85
+ return docs;
86
+ }
87
+ for (const d of plan.docs) {
88
+ if (!d.title && !d.content && !d.path)
89
+ continue;
90
+ docs.push({ ...d });
91
+ }
92
+ return docs;
93
+ }
94
+ collectPlanFilePaths(plan, docs) {
95
+ const filePathsToLoad = new Set();
96
+ if (plan.response !== null && plan.response !== undefined && plan.response.ok) {
97
+ for (const f of plan.response.files) {
98
+ filePathsToLoad.add(f);
99
+ }
100
+ }
101
+ for (const f of plan.files) {
102
+ filePathsToLoad.add(f);
103
+ }
104
+ for (const doc of docs) {
105
+ if (doc.path) {
106
+ filePathsToLoad.add(doc.path);
107
+ }
108
+ if (doc.references) {
109
+ for (const ref of doc.references) {
110
+ filePathsToLoad.add(ref);
111
+ }
112
+ }
113
+ if (doc.content) {
114
+ const pathMatches = doc.content.match(/`([^`]+\.[a-z]{1,6})`/g);
115
+ if (pathMatches) {
116
+ for (const match of pathMatches) {
117
+ const path = match.slice(1, -1);
118
+ if (path.includes('/') || path.includes('.')) {
119
+ filePathsToLoad.add(path);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ }
125
+ return filePathsToLoad;
126
+ }
127
+ async loadFilesByPath(filePaths) {
128
+ const files = {};
129
+ const loadPromises = Array.from(filePaths).map(async (filePath) => {
130
+ const resolved = isAbsolute(filePath) ? filePath : resolve(this.workspaceRoot, filePath);
131
+ try {
132
+ const content = await readFile(resolved, 'utf8');
133
+ return { path: filePath, content };
134
+ }
135
+ catch {
136
+ return { path: filePath, content: `[unable to load: ${filePath}]` };
137
+ }
138
+ });
139
+ const loadedFiles = await Promise.all(loadPromises);
140
+ for (const { path, content } of loadedFiles) {
141
+ files[path] = content;
142
+ }
143
+ return files;
144
+ }
145
+ async buildHarnessInjection(harness, plan, options) {
146
+ const docs = this.normalizePlanDocs(plan);
147
+ const files = await this.loadFilesByPath(this.collectPlanFilePaths(plan, docs));
148
+ const shouldLoadAegis = options?.includeAegisLearnings !== false;
149
+ const aegisLearnings = shouldLoadAegis ? await this.getAegisLearnings(20).catch(() => null) : null;
150
+ return {
151
+ harness,
152
+ docs,
153
+ files,
154
+ task: plan.task ?? plan.response?.task ?? '',
155
+ loadedAt: new Date().toISOString(),
156
+ aegisLearnings: aegisLearnings ?? undefined,
157
+ };
158
+ }
159
+ buildHarnessBindingBody(context) {
160
+ const body = {
161
+ message: context.message?.trim()
162
+ || context.currentIssue?.trim()
163
+ || [context.intendedAction, context.rationale].filter(Boolean).join('. ').trim()
164
+ || 'harness packet preflight',
165
+ stage: context.stage,
166
+ actor: context.role,
167
+ system: context.role,
168
+ platform: context.platform || 'harness-http-client',
169
+ roleProfile: context.roleProfile,
170
+ sessionId: context.sessionId,
171
+ userId: context.userId,
172
+ userName: context.userName,
173
+ currentIssue: context.currentIssue,
174
+ linearIssueId: context.linearIssueId,
175
+ linearIssueUrl: context.linearIssueUrl,
176
+ linearState: context.linearState,
177
+ workingDirectory: context.workingDirectory,
178
+ filesTouched: context.filesTouched,
179
+ recentProgress: context.recentProgress,
180
+ openRisks: context.openRisks,
181
+ nextActions: context.nextActions,
182
+ checkpoint: context.checkpoint,
183
+ requireGarden: context.requireGarden ?? true,
184
+ researchMode: context.researchMode,
185
+ intendedAction: context.intendedAction,
186
+ rationale: context.rationale,
187
+ correlationId: context.correlationId || randomUUID(),
188
+ ...context.extraBody,
189
+ };
190
+ return Object.fromEntries(Object.entries(body).filter(([, value]) => {
191
+ if (value == null)
192
+ return false;
193
+ if (typeof value === 'string')
194
+ return value.trim().length > 0;
195
+ if (Array.isArray(value))
196
+ return value.length > 0;
197
+ return true;
198
+ }));
199
+ }
200
+ _tryLoadDiskPacket() {
201
+ try {
202
+ // Path 1: direct env var (set by spawnSubAgent / Agent dispatch)
203
+ let packetPath = process.env.ARIA_HARNESS_PACKET_PATH || null;
204
+ // Path 2: owner-tier handoff JSON
205
+ if (!packetPath) {
206
+ const HOME = _homedir();
207
+ const handoffPath = `${HOME}/.claude/aria-agent-harness-handoff.json`;
208
+ if (existsSync(handoffPath)) {
209
+ try {
210
+ const handoff = JSON.parse(readFileSync(handoffPath, 'utf8'));
211
+ const ageMs = handoff.writtenAt
212
+ ? Date.now() - new Date(handoff.writtenAt).getTime()
213
+ : Infinity;
214
+ const ttl = handoff.ttlMs ?? (5 * 60 * 1000);
215
+ if (ageMs < ttl && handoff.harnessPacketPath) {
216
+ packetPath = handoff.harnessPacketPath;
217
+ }
218
+ }
219
+ catch { /* malformed handoff — skip */ }
220
+ }
221
+ }
222
+ if (!packetPath)
223
+ return;
224
+ if (!existsSync(packetPath))
225
+ return;
226
+ // Check packet file age against TTL (5 min)
227
+ const stat = statSync(packetPath);
228
+ if (Date.now() - stat.mtimeMs > 5 * 60 * 1000)
229
+ return; // expired
230
+ const raw = JSON.parse(readFileSync(packetPath, 'utf8'));
231
+ // Packet on disk may be the raw API response (has .packet wrapper) or
232
+ // a bare packet object. Normalise to HarnessPacket shape.
233
+ const packetData = (raw.packet ?? raw);
234
+ this.cachedPacket = {
235
+ packet: packetData,
236
+ timestamp: new Date().toISOString(),
237
+ version: '1.0.0',
238
+ };
239
+ this.packetLastFetched = stat.mtimeMs; // use file mtime as origin time
240
+ }
241
+ catch { /* disk errors are non-fatal — normal HTTP fetch will run */ }
48
242
  }
49
243
  static getInstance(config) {
50
244
  if (!HTTPHarnessClient.instance && config) {
@@ -135,96 +329,98 @@ export class HTTPHarnessClient {
135
329
  // ── Inject ──────────────────────────────────────────────────────────────
136
330
  async inject(plan) {
137
331
  const harness = await this.fetchHarnessPacket();
138
- const docs = [];
139
- if (plan.response !== null && plan.response !== undefined) {
140
- if (!plan.response.ok) {
141
- for (const d of plan.docs) {
142
- docs.push({ ...d });
143
- }
144
- }
145
- else {
146
- for (const d of plan.response.docs) {
147
- if (!d.title && !d.content && !d.path)
148
- continue;
149
- docs.push({ ...d });
150
- }
151
- const respTitles = new Set(plan.response.docs.map((d) => d.title).filter(Boolean));
152
- for (const d of plan.docs) {
153
- if (d.title && !respTitles.has(d.title)) {
154
- docs.push({ ...d });
155
- }
156
- }
157
- }
158
- }
159
- else {
160
- for (const d of plan.docs) {
161
- if (!d.title && !d.content && !d.path)
162
- continue;
163
- docs.push({ ...d });
164
- }
165
- }
166
- const files = {};
167
- const filePathsToLoad = new Set();
168
- if (plan.response !== null && plan.response !== undefined && plan.response.ok) {
169
- for (const f of plan.response.files) {
170
- filePathsToLoad.add(f);
171
- }
172
- }
173
- for (const f of plan.files) {
174
- filePathsToLoad.add(f);
175
- }
176
- for (const doc of docs) {
177
- if (doc.path) {
178
- filePathsToLoad.add(doc.path);
179
- }
180
- if (doc.references) {
181
- for (const ref of doc.references) {
182
- filePathsToLoad.add(ref);
183
- }
184
- }
185
- if (doc.content) {
186
- const pathMatches = doc.content.match(/`([^`]+\.[a-z]{1,6})`/g);
187
- if (pathMatches) {
188
- for (const m of pathMatches) {
189
- const p = m.slice(1, -1);
190
- if (p.includes('/') || p.includes('.')) {
191
- filePathsToLoad.add(p);
192
- }
193
- }
194
- }
195
- }
196
- }
197
- const loadPromises = Array.from(filePathsToLoad).map(async (filePath) => {
198
- const resolved = isAbsolute(filePath) ? filePath : resolve(this.workspaceRoot, filePath);
199
- try {
200
- const content = await readFile(resolved, 'utf8');
201
- return { path: filePath, content };
202
- }
203
- catch {
204
- return { path: filePath, content: `[unable to load: ${filePath}]` };
205
- }
206
- });
207
- const loadedFiles = await Promise.all(loadPromises);
208
- for (const { path, content } of loadedFiles) {
209
- files[path] = content;
210
- }
211
- return {
212
- harness,
213
- docs,
214
- files,
215
- task: plan.task ?? plan.response?.task ?? '',
216
- loadedAt: new Date().toISOString(),
217
- };
332
+ return this.buildHarnessInjection(harness, plan, { includeAegisLearnings: true });
218
333
  }
219
334
  async getHarnessPacket(bodyOverride) {
220
335
  return this.fetchHarnessPacket(bodyOverride);
221
336
  }
337
+ async getBoundHarnessAndPrompt(context, plan) {
338
+ const bindingBody = this.buildHarnessBindingBody(context);
339
+ const harness = await this.fetchHarnessPacket(bindingBody);
340
+ const injection = await this.buildHarnessInjection(harness, {
341
+ response: plan?.response ?? null,
342
+ docs: plan?.docs ?? [],
343
+ files: plan?.files ?? [],
344
+ task: plan?.task ?? context.intendedAction,
345
+ }, { includeAegisLearnings: context.includeAegisLearnings !== false });
346
+ return {
347
+ prompt: this.buildSystemPrompt(injection),
348
+ packet: harness,
349
+ harnessText: this.extractHarnessText(harness.packet),
350
+ bindingBody,
351
+ };
352
+ }
353
+ async getAegisLearnings(limit) {
354
+ try {
355
+ const params = new URLSearchParams();
356
+ if (limit)
357
+ params.set('limit', String(Math.min(limit, 50)));
358
+ const url = `${this.baseUrl}/api/harness/aegis-learnings${params.toString() ? '?' + params.toString() : ''}`;
359
+ const res = await this.fetchWithRetry(url, {
360
+ method: 'GET',
361
+ headers: {
362
+ Authorization: `Bearer ${this.apiKey}`,
363
+ 'Content-Type': 'application/json',
364
+ },
365
+ });
366
+ if (!res.ok)
367
+ return null;
368
+ const data = await res.json();
369
+ return {
370
+ avoidPatterns: data.avoidPatterns || [],
371
+ successPatterns: data.successPatterns || [],
372
+ recentPatterns: (data.recentPatterns || []).map((p) => ({
373
+ name: p.name || '',
374
+ category: p.category || '',
375
+ outcome: p.outcome || '',
376
+ lesson: p.lesson || '',
377
+ })),
378
+ decisionCount: data.decisionCount || 0,
379
+ reflectionCount: data.reflectionCount || 0,
380
+ };
381
+ }
382
+ catch {
383
+ return null;
384
+ }
385
+ }
222
386
  // ── System prompt builder ───────────────────────────────────────────────
223
387
  buildSystemPrompt(injection) {
224
388
  const parts = [];
225
389
  // 8-lens cognition FIRST — controls all thinking
226
390
  parts.push(this.get8LensBlock());
227
391
  parts.push('');
392
+ // Aegis cognitive guardrails — quality substrate from backfill learnings
393
+ if (injection.aegisLearnings) {
394
+ const al = injection.aegisLearnings;
395
+ parts.push('## Aegis Cognitive Guardrails');
396
+ parts.push(`Backed by ${al.decisionCount.toLocaleString()} decisions, ${al.reflectionCount.toLocaleString()} reflections.`);
397
+ parts.push('');
398
+ if (al.avoidPatterns.length > 0) {
399
+ parts.push('### Patterns to Avoid (learned from past failures)');
400
+ parts.push('These are anti-patterns that led to hallucinations, errors, or rejected outputs. Reason about them through the 8 lenses and avoid producing output that matches them.');
401
+ for (const p of al.avoidPatterns.slice(0, 15)) {
402
+ parts.push(`- ${p}`);
403
+ }
404
+ parts.push('');
405
+ }
406
+ if (al.successPatterns.length > 0) {
407
+ parts.push('### Patterns to Repeat (learned from successful outputs)');
408
+ parts.push('These patterns consistently produced high-quality output. When appropriate, structure your response using these approaches.');
409
+ for (const p of al.successPatterns.slice(0, 10)) {
410
+ parts.push(`- ${p}`);
411
+ }
412
+ parts.push('');
413
+ }
414
+ if (al.recentPatterns.length > 0) {
415
+ parts.push('### Recent Learnings');
416
+ for (const p of al.recentPatterns.slice(0, 8)) {
417
+ parts.push(`- [${p.outcome}] ${p.name}: ${p.lesson.slice(0, 200)}`);
418
+ }
419
+ parts.push('');
420
+ }
421
+ parts.push('Apply these guardrails through 8-lens cognition — do not list them, internalize them.');
422
+ parts.push('');
423
+ }
228
424
  parts.push('---');
229
425
  parts.push('name: aria-harness-injection');
230
426
  parts.push('description: Combined harness state + plan docs + loaded files');
@@ -233,11 +429,20 @@ export class HTTPHarnessClient {
233
429
  parts.push('');
234
430
  const p = injection.harness.packet;
235
431
  if (p && typeof p === 'object') {
236
- parts.push('## Aria Live State');
237
- parts.push('```json');
238
- parts.push(JSON.stringify(p, null, 2));
239
- parts.push('```');
240
- parts.push('');
432
+ const rawHarnessText = this.extractHarnessText(p);
433
+ if (rawHarnessText) {
434
+ parts.push('## Aria Harness Substrate');
435
+ parts.push(rawHarnessText);
436
+ parts.push('');
437
+ }
438
+ const metadata = Object.fromEntries(Object.entries(p).filter(([key]) => key !== 'harness' && key !== 'chunks'));
439
+ if (!rawHarnessText || Object.keys(metadata).length > 0) {
440
+ parts.push('## Aria Live State');
441
+ parts.push('```json');
442
+ parts.push(JSON.stringify(rawHarnessText ? metadata : p, null, 2));
443
+ parts.push('```');
444
+ parts.push('');
445
+ }
241
446
  }
242
447
  if (injection.task) {
243
448
  parts.push('## Task');
@@ -523,6 +728,59 @@ export class HTTPHarnessClient {
523
728
  }
524
729
  }
525
730
  catch { /* non-fatal — ownerTier defaults above apply */ }
731
+ // ── Layer 3: fetch harness packet for sub-agent cognition bootstrap ──────
732
+ // Fetch the harness packet now (before writing the handoff) so the path
733
+ // can be embedded in the handoff JSON. Sub-agents read ARIA_HARNESS_PACKET_PATH
734
+ // from the environment (set by the caller after spawnSubAgent returns) OR
735
+ // from the handoff JSON field. Either path eliminates the extra HTTP call
736
+ // inside the sub-agent.
737
+ //
738
+ // Packet paths (tier-aware):
739
+ // Owner: ~/.claude/aria-agent-harness-packet.json
740
+ // Client: /var/lib/aria-licensee/{jti}/aria-agent-harness-packet.json
741
+ //
742
+ // Fail-soft: packet fetch failure logs a warning but does NOT block spawn.
743
+ const packetPath = isClientTier
744
+ ? `/var/lib/aria-licensee/${args.jti}/aria-agent-harness-packet.json`
745
+ : `${HOME}/.claude/aria-agent-harness-packet.json`;
746
+ let resolvedPacketPath;
747
+ try {
748
+ const packetResp = await fetch(this.harnessPacketUrl, {
749
+ method: 'POST',
750
+ headers: {
751
+ 'Authorization': `Bearer ${this.apiKey}`,
752
+ 'Content-Type': 'application/json',
753
+ },
754
+ body: JSON.stringify({
755
+ stage: 'agent-spawn',
756
+ actor: 'harness-http-client',
757
+ system: 'claude-coding-agent',
758
+ surface: 'platform:harness-http-client',
759
+ isHamza: ownerTier.hamza,
760
+ sessionId: args.sessionId,
761
+ mode: 'subagent',
762
+ }),
763
+ });
764
+ if (packetResp.ok) {
765
+ const packetData = await packetResp.json();
766
+ fsMkdir(fsDirname(packetPath), { recursive: true });
767
+ fsWrite(packetPath, JSON.stringify(packetData, null, 2));
768
+ resolvedPacketPath = packetPath;
769
+ // Also pre-load into this SDK instance's cache so subsequent
770
+ // getHarnessPacket() calls are instant within this process.
771
+ const packetPayload = (packetData.packet ?? packetData);
772
+ this.cachedPacket = {
773
+ packet: packetPayload,
774
+ timestamp: new Date().toISOString(),
775
+ version: '1.0.0',
776
+ };
777
+ this.packetLastFetched = Date.now();
778
+ }
779
+ }
780
+ catch (_packetErr) {
781
+ // Fail-soft: warn, proceed identity-only (handoff still valid without packet).
782
+ // Network blips should never block sub-agent spawns.
783
+ }
526
784
  const handoff = {
527
785
  writtenAt: new Date().toISOString(),
528
786
  parentSessionId: args.sessionId,
@@ -533,6 +791,9 @@ export class HTTPHarnessClient {
533
791
  ownerTier,
534
792
  parentLedgerPath: ledgerPath,
535
793
  ttlMs: HANDOFF_TTL_MS,
794
+ // harnessPacketPath is set only if packet fetch succeeded. Sub-agents
795
+ // should also check ARIA_HARNESS_PACKET_PATH env (set by caller post-spawn).
796
+ ...(resolvedPacketPath ? { harnessPacketPath: resolvedPacketPath } : {}),
536
797
  };
537
798
  fsMkdir(fsDirname(handoffPath), { recursive: true });
538
799
  fsWrite(handoffPath, JSON.stringify(handoff, null, 2));
@@ -544,12 +805,36 @@ export class HTTPHarnessClient {
544
805
  allowedActions: args.allowedActions ?? [],
545
806
  defectContext: args.defectContext ?? null,
546
807
  handoffPath,
808
+ harnessPacketPath: resolvedPacketPath ?? null,
547
809
  });
548
810
  fsMkdir(fsDirname(ledgerPath), { recursive: true });
549
811
  const { appendFileSync: fsAppend } = await import('node:fs');
550
812
  fsAppend(ledgerPath, spawnEntry + '\n');
551
813
  return handoff;
552
814
  }
815
+ /**
816
+ * Returns the environment variables to set for a spawned sub-agent process
817
+ * so it inherits the parent's harness packet without an extra HTTP call.
818
+ * Call after spawnSubAgent() — uses the returned handoff.harnessPacketPath.
819
+ *
820
+ * Usage:
821
+ * const handoff = await sdk.spawnSubAgent(args);
822
+ * const env = sdk.subAgentEnv(handoff);
823
+ * // pass env to your process spawn / Agent tool env block
824
+ */
825
+ subAgentEnv(handoff) {
826
+ const env = {};
827
+ if (handoff.harnessPacketPath) {
828
+ env['ARIA_HARNESS_PACKET_PATH'] = handoff.harnessPacketPath;
829
+ }
830
+ if (handoff.harnessToken) {
831
+ env['ARIA_HARNESS_TOKEN'] = handoff.harnessToken;
832
+ }
833
+ if (handoff.harnessUrl) {
834
+ env['ARIA_HARNESS_URL'] = handoff.harnessUrl;
835
+ }
836
+ return env;
837
+ }
553
838
  /** Pulls sub-agent ledger entries newer than the handoff timestamp and appends
554
839
  * them to the parent ledger, deduplicating by `text` prefix (first 100 chars). */
555
840
  async mergeSubAgentLedger(parentSessionId, _subAgentSessionId, jti) {
@@ -655,6 +940,78 @@ export class HTTPHarnessClient {
655
940
  }
656
941
  return { merged, skipped };
657
942
  }
943
+ // ── recordDiscovery ──────────────────────────────────────────────────────
944
+ // Writes a finding row to the sub-agent's session ledger on disk so
945
+ // aria-agent-ledger-merge.mjs picks it up after the Agent tool completes.
946
+ //
947
+ // Tier routing (mirrors aria-discovery-record.mjs):
948
+ // Owner tier (no jti) → ~/.claude/aria-discoveries-{session}.jsonl
949
+ // Client tier (jti set) → /var/lib/aria-licensee/{jti}/aria-discoveries-{session}.jsonl
950
+ //
951
+ // Session ID must be the sub-agent's OWN session (not the parent's) so the
952
+ // ledger-merge loop finds a file that is NOT the parentLedgerPath. If no
953
+ // sessionId is supplied we derive one from the handoff parentSessionId + "-sub".
954
+ //
955
+ // Fail-soft: returns {ok:false, error} on write failures — never throws.
956
+ async recordDiscovery(args) {
957
+ if (!args.text || args.text.length < 4) {
958
+ return { ok: false, ledger: '', at: '', error: 'text too short (min 4 chars)' };
959
+ }
960
+ const { existsSync: fsExists, readFileSync: fsRead, appendFileSync: fsAppend, mkdirSync: fsMkdir } = await import('node:fs');
961
+ const { homedir } = await import('node:os');
962
+ const { dirname: fsDirname } = await import('node:path');
963
+ const HOME = homedir();
964
+ const LICENSE_PATH = `${HOME}/.aria/license.json`;
965
+ const HANDOFF_PATH = `${HOME}/.claude/aria-agent-harness-handoff.json`;
966
+ // Tier detection: caller-supplied jti wins; fall back to license file
967
+ let jti = args.jti ?? null;
968
+ let isClientTier = Boolean(jti);
969
+ if (!jti) {
970
+ try {
971
+ if (fsExists(LICENSE_PATH)) {
972
+ const lic = JSON.parse(fsRead(LICENSE_PATH, 'utf8'));
973
+ jti = lic.jti ?? null;
974
+ isClientTier = Boolean(jti);
975
+ }
976
+ }
977
+ catch { /* non-fatal — owner tier */ }
978
+ }
979
+ // Session ID: caller-supplied wins; then handoff-derived (with "-sub" suffix)
980
+ let sessionId = args.sessionId ?? null;
981
+ if (!sessionId) {
982
+ try {
983
+ if (fsExists(HANDOFF_PATH)) {
984
+ const handoff = JSON.parse(fsRead(HANDOFF_PATH, 'utf8'));
985
+ sessionId = handoff.parentSessionId ? `${handoff.parentSessionId}-sub` : 'sub-unknown';
986
+ }
987
+ }
988
+ catch { /* non-fatal */ }
989
+ }
990
+ sessionId = sessionId || 'sub-unknown';
991
+ const safeSession = String(sessionId).replace(/[^a-zA-Z0-9_-]/g, '_');
992
+ const ledgerPath = isClientTier
993
+ ? `/var/lib/aria-licensee/${jti}/aria-discoveries-${safeSession}.jsonl`
994
+ : `${HOME}/.claude/aria-discoveries-${safeSession}.jsonl`;
995
+ const at = new Date().toISOString();
996
+ const row = {
997
+ at,
998
+ session: sessionId,
999
+ kind: args.kind || 'observation',
1000
+ text: args.text,
1001
+ refs: args.refs || [],
1002
+ evidence: args.evidence ?? null,
1003
+ source: args.source || 'sub-agent-sdk',
1004
+ resolution_status: args.resolution_status || 'open',
1005
+ };
1006
+ try {
1007
+ fsMkdir(fsDirname(ledgerPath), { recursive: true });
1008
+ fsAppend(ledgerPath, JSON.stringify(row) + '\n');
1009
+ return { ok: true, ledger: ledgerPath, at };
1010
+ }
1011
+ catch (err) {
1012
+ return { ok: false, ledger: ledgerPath, at, error: err.message };
1013
+ }
1014
+ }
658
1015
  // ── verifyClaim ──────────────────────────────────────────────────────────
659
1016
  // POST /api/harness/verify-claim — persists a claim/evidence pair to
660
1017
  // aria_cognition_corpus and returns a verdict. Fail-soft: if the server-side
@@ -694,6 +1051,376 @@ export class HTTPHarnessClient {
694
1051
  };
695
1052
  }
696
1053
  }
1054
+ // ── postJson — generic SDK-routed JSON POST ────────────────────────────
1055
+ // Hamza 2026-04-27 directive: "use the SDK - use it, enforce it all". The
1056
+ // typed primitives above (consult, validateOutput, gardenTurn) cover Aria's
1057
+ // own control-plane endpoints. This method is the canonical primitive for
1058
+ // bridge POSTs (e.g., Telegram → /chat) so internal bridges inherit the
1059
+ // SDK's retry-with-backoff (250/500/1000ms) and audit-log discipline
1060
+ // instead of using raw fetch().
1061
+ //
1062
+ // Returns:
1063
+ // - { ok: true, status, data } on 2xx
1064
+ // - { ok: false, status, errorBody } on non-2xx (does NOT throw — caller
1065
+ // decides whether to fall back, retry semantically, or surface to user)
1066
+ // - throws on network errors after retries exhausted (real fetch failures)
1067
+ //
1068
+ // Headers are merged with Authorization (if apiKey is set) and
1069
+ // Content-Type: application/json. Caller can override either.
1070
+ async postJson(url, body, extraHeaders = {}) {
1071
+ const headers = {
1072
+ 'Content-Type': 'application/json',
1073
+ ...(this.apiKey ? { Authorization: `Bearer ${this.apiKey}` } : {}),
1074
+ ...extraHeaders,
1075
+ };
1076
+ const res = await this.fetchWithRetry(url, {
1077
+ method: 'POST',
1078
+ headers,
1079
+ body: JSON.stringify(body),
1080
+ });
1081
+ if (!res.ok) {
1082
+ const errorBody = await res.text().catch(() => '');
1083
+ return { ok: false, status: res.status, errorBody };
1084
+ }
1085
+ const data = (await res.json());
1086
+ return { ok: true, status: res.status, data };
1087
+ }
1088
+ // ══════════════════════════════════════════════════════════════════════════
1089
+ // ARISTOTLE 3-PHASE COGNITIVE PRIMITIVES
1090
+ // Dispatches to POST /api/harness/aristotle-noor with a `phase` field.
1091
+ // The harness route delegates to runAristotleNoorPipeline in aria-soul.
1092
+ //
1093
+ // Transport contract (per doctrine):
1094
+ // - NO deadline-based timeouts — fetchWithRetry uses 3 error-count retries
1095
+ // with 250ms / 500ms / 1000ms backoff. Real network faults (ECONNREFUSED,
1096
+ // EHOSTUNREACH) reject immediately; slow-but-alive endpoints get a chance.
1097
+ // - LOUD failure — non-2xx responses throw, never fail-open silently.
1098
+ // Callers decide whether to surface or recover (per feedback_non_blocking_errors_unacceptable.md).
1099
+ // - Tier-gating — owner-tier callers set `aristotleEnabled: true` by default;
1100
+ // client-tier callers receive it as opt-in via the `enabled` config field.
1101
+ // ══════════════════════════════════════════════════════════════════════════
1102
+ /**
1103
+ * ARISTOTLE PRE-PHASE
1104
+ * Fires before an LLM call or tool action. Runs: Fitrah hard-gate,
1105
+ * wisdom pull (principles + decisions), research pull, DeepSoulBridge,
1106
+ * and CleanCognition substrate check.
1107
+ *
1108
+ * Throws on transport-level failures per feedback_non_blocking_errors_unacceptable.md.
1109
+ * If Fitrah vetoes, result.fitrahVetoed === true — caller MUST emit a refusal,
1110
+ * not silently continue.
1111
+ *
1112
+ * @param message The user message or intent string being processed.
1113
+ * @param sessionId Session ID for telemetry + hive routing.
1114
+ * @param context Optional context overrides (userId, isHamza, tier, etc.).
1115
+ */
1116
+ async aristotlePre(message, sessionId, context) {
1117
+ const t0 = Date.now();
1118
+ const url = `${this.baseUrl}/api/harness/aristotle-noor`;
1119
+ const res = await this.fetchWithRetry(url, {
1120
+ method: 'POST',
1121
+ headers: {
1122
+ Authorization: `Bearer ${this.apiKey}`,
1123
+ 'Content-Type': 'application/json',
1124
+ },
1125
+ body: JSON.stringify({
1126
+ phase: 'pre',
1127
+ message,
1128
+ sessionId,
1129
+ userId: context?.userId,
1130
+ isHamza: context?.isHamza,
1131
+ isGroupChat: context?.isGroupChat,
1132
+ tier: context?.tier,
1133
+ }),
1134
+ });
1135
+ if (!res.ok) {
1136
+ const body = await res.text().catch(() => '');
1137
+ throw new Error(`[aristotlePre] harness route responded ${res.status} ${res.statusText}: ${body}`);
1138
+ }
1139
+ const data = (await res.json());
1140
+ return {
1141
+ fired: data.fired ?? [],
1142
+ fitrahVetoed: data.fitrahVetoed ?? false,
1143
+ fitrahAlignment: data.fitrahAlignment ?? 1.0,
1144
+ qualityScore: data.qualityScore ?? 100,
1145
+ reAuthorSignal: data.reAuthorSignal ?? false,
1146
+ notes: data.notes ?? [],
1147
+ soulCharge: data.soulCharge ?? null,
1148
+ soulManifoldStatus: data.soulManifoldStatus ?? null,
1149
+ ghazaliVerdict: data.ghazaliVerdict ?? null,
1150
+ latencyMs: data.latencyMs ?? Date.now() - t0,
1151
+ dispatched: true,
1152
+ };
1153
+ }
1154
+ /**
1155
+ * ARISTOTLE MID-PHASE
1156
+ * Fires after context is built but before the LLM call. Runs: MetaCognitive
1157
+ * confidence snapshot and ethical keyword check on the planned approach.
1158
+ *
1159
+ * @param message Original user message.
1160
+ * @param sessionId Session ID.
1161
+ * @param plannedApproach The drafted approach / plan string for mid-flight check.
1162
+ * @param context Optional context overrides.
1163
+ */
1164
+ async aristotleMid(message, sessionId, plannedApproach, context) {
1165
+ const t0 = Date.now();
1166
+ const url = `${this.baseUrl}/api/harness/aristotle-noor`;
1167
+ const res = await this.fetchWithRetry(url, {
1168
+ method: 'POST',
1169
+ headers: {
1170
+ Authorization: `Bearer ${this.apiKey}`,
1171
+ 'Content-Type': 'application/json',
1172
+ },
1173
+ body: JSON.stringify({
1174
+ phase: 'mid',
1175
+ message,
1176
+ sessionId,
1177
+ plannedApproach,
1178
+ userId: context?.userId,
1179
+ tier: context?.tier,
1180
+ }),
1181
+ });
1182
+ if (!res.ok) {
1183
+ const body = await res.text().catch(() => '');
1184
+ throw new Error(`[aristotleMid] harness route responded ${res.status} ${res.statusText}: ${body}`);
1185
+ }
1186
+ const data = (await res.json());
1187
+ return {
1188
+ fired: data.fired ?? [],
1189
+ fitrahVetoed: data.fitrahVetoed ?? false,
1190
+ fitrahAlignment: data.fitrahAlignment ?? 1.0,
1191
+ qualityScore: data.qualityScore ?? 100,
1192
+ reAuthorSignal: data.reAuthorSignal ?? false,
1193
+ notes: data.notes ?? [],
1194
+ soulCharge: data.soulCharge ?? null,
1195
+ soulManifoldStatus: data.soulManifoldStatus ?? null,
1196
+ ghazaliVerdict: data.ghazaliVerdict ?? null,
1197
+ latencyMs: data.latencyMs ?? Date.now() - t0,
1198
+ dispatched: true,
1199
+ };
1200
+ }
1201
+ /**
1202
+ * ARISTOTLE POST-PHASE
1203
+ * Fires after the LLM response, before emission. Runs: 8-Lens Detector,
1204
+ * Predictor, SelfReflection (on high-stakes turns), and quality scoring.
1205
+ *
1206
+ * If result.reAuthorSignal === true, the response has 8-lens violations —
1207
+ * per doctrine the caller MUST surface the signal, never silently strip.
1208
+ *
1209
+ * @param message Original user message.
1210
+ * @param response The LLM-generated response to gate.
1211
+ * @param sessionId Session ID.
1212
+ * @param context Optional context overrides (tier, isFirstOfSession).
1213
+ */
1214
+ async aristotlePost(message, response, sessionId, context) {
1215
+ const t0 = Date.now();
1216
+ const url = `${this.baseUrl}/api/harness/aristotle-noor`;
1217
+ const res = await this.fetchWithRetry(url, {
1218
+ method: 'POST',
1219
+ headers: {
1220
+ Authorization: `Bearer ${this.apiKey}`,
1221
+ 'Content-Type': 'application/json',
1222
+ },
1223
+ body: JSON.stringify({
1224
+ phase: 'post',
1225
+ message,
1226
+ draft: response,
1227
+ sessionId,
1228
+ userId: context?.userId,
1229
+ tier: context?.tier,
1230
+ isFirstOfSession: context?.isFirstOfSession,
1231
+ }),
1232
+ });
1233
+ if (!res.ok) {
1234
+ const body = await res.text().catch(() => '');
1235
+ throw new Error(`[aristotlePost] harness route responded ${res.status} ${res.statusText}: ${body}`);
1236
+ }
1237
+ const data = (await res.json());
1238
+ return {
1239
+ fired: data.fired ?? [],
1240
+ fitrahVetoed: data.fitrahVetoed ?? false,
1241
+ fitrahAlignment: data.fitrahAlignment ?? 1.0,
1242
+ qualityScore: data.qualityScore ?? 100,
1243
+ reAuthorSignal: data.reAuthorSignal ?? false,
1244
+ notes: data.notes ?? [],
1245
+ soulCharge: data.soulCharge ?? null,
1246
+ soulManifoldStatus: data.soulManifoldStatus ?? null,
1247
+ ghazaliVerdict: data.ghazaliVerdict ?? null,
1248
+ latencyMs: data.latencyMs ?? Date.now() - t0,
1249
+ dispatched: true,
1250
+ };
1251
+ }
1252
+ // ══════════════════════════════════════════════════════════════════════════
1253
+ // NOOR COGNITIVE PRIMITIVES
1254
+ // Dispatches to POST /api/harness/noor with an `operation` discriminator.
1255
+ // noor-core.ts is NOT modified — these are HTTP wrappers that call its
1256
+ // exposed surface via the harness route.
1257
+ //
1258
+ // noor-core.ts banner: "ONLY ARIA CAN CODE ARIA. No councils. No subagents.
1259
+ // No other LLMs." — complied. We call, never modify.
1260
+ // ══════════════════════════════════════════════════════════════════════════
1261
+ /**
1262
+ * NOOR RECOGNIZE (Kashf — eigenspace recognition, not generation)
1263
+ * Projects the query string (via harness-side embedding) onto Aria's
1264
+ * eigenspace manifold and returns the nearest recognized response + soul
1265
+ * distance + ethical membrane status + Ghazali 8-lens verdict.
1266
+ *
1267
+ * The harness routes the query through the manifold service (gRPC) if
1268
+ * NOOR_EIGENSPACE_SOURCE=manifold, otherwise uses local eigenspace.
1269
+ *
1270
+ * @param query The input string to recognize on the manifold.
1271
+ * @param context Optional context (sessionId, userId) for telemetry.
1272
+ */
1273
+ async noorRecognize(query, context) {
1274
+ const t0 = Date.now();
1275
+ const url = `${this.baseUrl}/api/harness/noor`;
1276
+ const res = await this.fetchWithRetry(url, {
1277
+ method: 'POST',
1278
+ headers: {
1279
+ Authorization: `Bearer ${this.apiKey}`,
1280
+ 'Content-Type': 'application/json',
1281
+ },
1282
+ body: JSON.stringify({
1283
+ operation: 'recognize',
1284
+ query,
1285
+ sessionId: context?.sessionId,
1286
+ userId: context?.userId,
1287
+ }),
1288
+ });
1289
+ if (!res.ok) {
1290
+ const body = await res.text().catch(() => '');
1291
+ throw new Error(`[noorRecognize] harness route responded ${res.status} ${res.statusText}: ${body}`);
1292
+ }
1293
+ const data = (await res.json());
1294
+ return {
1295
+ nearestText: data.nearestText ?? '',
1296
+ soulDistance: data.soulDistance ?? 0,
1297
+ confidence: data.confidence ?? 0,
1298
+ withinMembrane: data.withinMembrane ?? true,
1299
+ soulCharge: data.soulCharge ?? 0,
1300
+ projectionComponents: data.projectionComponents ?? [],
1301
+ ghazaliVerdict: data.ghazaliVerdict ?? null,
1302
+ manifoldHealth: data.manifoldHealth ?? null,
1303
+ dispatched: true,
1304
+ latencyMs: data.latencyMs ?? Date.now() - t0,
1305
+ };
1306
+ }
1307
+ /**
1308
+ * NOOR FORGE — self-generated tool invocation via 5 primitives
1309
+ * Composes a named primitive chain (http, sql, file_read, file_write, pub_sub)
1310
+ * and executes it through the harness-side NoorForge engine. The harness
1311
+ * applies the ethical membrane check (checkEthicalAlignment) before any
1312
+ * primitive fires — rejected intents return success=false, ethicallyApproved=false.
1313
+ *
1314
+ * @param intent Description of what the forged tool should accomplish.
1315
+ * @param primitives Ordered list of primitive descriptors to execute in sequence.
1316
+ * @param context Optional context for telemetry.
1317
+ */
1318
+ async noorForge(intent, primitives, context) {
1319
+ const t0 = Date.now();
1320
+ const url = `${this.baseUrl}/api/harness/noor`;
1321
+ const res = await this.fetchWithRetry(url, {
1322
+ method: 'POST',
1323
+ headers: {
1324
+ Authorization: `Bearer ${this.apiKey}`,
1325
+ 'Content-Type': 'application/json',
1326
+ },
1327
+ body: JSON.stringify({
1328
+ operation: 'forge',
1329
+ intent,
1330
+ primitives,
1331
+ sessionId: context?.sessionId,
1332
+ userId: context?.userId,
1333
+ }),
1334
+ });
1335
+ if (!res.ok) {
1336
+ const body = await res.text().catch(() => '');
1337
+ throw new Error(`[noorForge] harness route responded ${res.status} ${res.statusText}: ${body}`);
1338
+ }
1339
+ const data = (await res.json());
1340
+ return {
1341
+ success: data.success ?? false,
1342
+ toolName: data.toolName ?? `forged_${Date.now()}`,
1343
+ ethicallyApproved: data.ethicallyApproved ?? false,
1344
+ totalDurationMs: data.totalDurationMs ?? Date.now() - t0,
1345
+ results: data.results ?? [],
1346
+ dispatched: true,
1347
+ };
1348
+ }
1349
+ // ══════════════════════════════════════════════════════════════════════════
1350
+ // HARNESS CONSULT WITH ARISTOTLE — opt-in 3-phase wrapping
1351
+ //
1352
+ // Wraps getBoundHarnessAndPrompt() with pre + post Aristotle phases.
1353
+ // Per the harness packet cognition_runtime_rule: "Aristotle/Taddabur are
1354
+ // primary cognition. Do not replace contemplation with a dashboard state."
1355
+ // Aristotle fires EVERY consult for owner-tier; client-tier requires
1356
+ // explicit `aristotleEnabled: true` in options (default: false).
1357
+ //
1358
+ // Mid-phase is optional — pass `plannedApproach` to activate it between
1359
+ // packet fetch and the LLM call. Most callers omit mid-phase.
1360
+ // ══════════════════════════════════════════════════════════════════════════
1361
+ /**
1362
+ * Harness consult with Aristotle 3-phase wrapping.
1363
+ *
1364
+ * Flow:
1365
+ * 1. aristotlePre() — Fitrah gate, wisdom, research, soul state
1366
+ * 2. getBoundHarnessAndPrompt() — packet + system prompt
1367
+ * 3. aristotleMid() — MetaCognitive + ethical check (if plannedApproach provided)
1368
+ * 4. [caller calls LLM with the returned prompt]
1369
+ * 5. Call aristotlePost() from the caller with the LLM response
1370
+ * (post-phase is caller-side because the SDK doesn't own the LLM call)
1371
+ *
1372
+ * Returns the BoundHarnessPromptResult augmented with the pre-phase result.
1373
+ * If Fitrah vetoes, throws — the caller must handle the veto (emit refusal).
1374
+ *
1375
+ * @param context HarnessBindingContext (same shape as getBoundHarnessAndPrompt).
1376
+ * @param plan Optional plan docs / files (same shape as getBoundHarnessAndPrompt).
1377
+ * @param options Aristotle control options.
1378
+ * @param options.aristotleEnabled Whether Aristotle fires. Default true for this method.
1379
+ * @param options.plannedApproach Optional planned approach string — activates mid-phase.
1380
+ * @param options.tier Tier hint for Aristotle ('owner'|'client'|etc.).
1381
+ */
1382
+ async getBoundHarnessAndPromptWithAristotle(context, plan, options) {
1383
+ const enabled = options?.aristotleEnabled !== false; // default ON
1384
+ let preResult = null;
1385
+ let midResult = null;
1386
+ const message = context.message ?? context.intendedAction ?? 'harness consult';
1387
+ // ── Phase 1: PRE ────────────────────────────────────────────────────────
1388
+ if (enabled) {
1389
+ try {
1390
+ preResult = await this.aristotlePre(message, context.sessionId, {
1391
+ userId: context.userId,
1392
+ tier: options?.tier,
1393
+ });
1394
+ if (preResult.fitrahVetoed) {
1395
+ throw new Error(`[aristotlePre] Fitrah veto — alignment=${preResult.fitrahAlignment.toFixed(2)}. Caller must emit honest refusal.`);
1396
+ }
1397
+ }
1398
+ catch (err) {
1399
+ // Re-throw Fitrah veto (named Error pattern). Log transport faults.
1400
+ if (err.message.includes('Fitrah veto'))
1401
+ throw err;
1402
+ console.error('[getBoundHarnessAndPromptWithAristotle] aristotlePre transport fault:', err.message);
1403
+ // Transport faults are LOUD but non-blocking for the consult itself.
1404
+ // The pre-phase result stays null so callers know Aristotle didn't fire.
1405
+ }
1406
+ }
1407
+ // ── Phase 2: Harness packet + system prompt ─────────────────────────────
1408
+ const bound = await this.getBoundHarnessAndPrompt(context, plan);
1409
+ // ── Phase 3: MID (optional — only when plannedApproach is provided) ─────
1410
+ if (enabled && options?.plannedApproach) {
1411
+ try {
1412
+ midResult = await this.aristotleMid(message, context.sessionId, options.plannedApproach, {
1413
+ userId: context.userId,
1414
+ tier: options?.tier,
1415
+ });
1416
+ }
1417
+ catch (err) {
1418
+ console.error('[getBoundHarnessAndPromptWithAristotle] aristotleMid transport fault:', err.message);
1419
+ // Non-blocking — mid-phase unavailability doesn't stop the LLM call.
1420
+ }
1421
+ }
1422
+ return { ...bound, aristotlePre: preResult, aristotleMid: midResult };
1423
+ }
697
1424
  // ── Retry + exponential backoff helper ──────────────────────────────────
698
1425
  // Hamza 2026-04-27: "YES ADD RETRY AND BACKOFF BUT FUCK UR CIRCUIT BREAKER
699
1426
  // THAT NUST LEAVES HER BROKEN WE NEED SELF HEAL!!!" — every fetch retries
@@ -724,4 +1451,19 @@ export const harness = {
724
1451
  getInstance: HTTPHarnessClient.getInstance.bind(HTTPHarnessClient),
725
1452
  resetInstance: HTTPHarnessClient.resetInstance.bind(HTTPHarnessClient),
726
1453
  };
1454
+ export function bindingContext(role, sessionId, stage, intendedAction, rationale) {
1455
+ return {
1456
+ role,
1457
+ sessionId,
1458
+ stage,
1459
+ intendedAction,
1460
+ rationale,
1461
+ correlationId: randomUUID(),
1462
+ };
1463
+ }
1464
+ export async function getBoundHarnessAndPrompt(context, plan, client) {
1465
+ const sdk = client ?? HTTPHarnessClient.getInstance();
1466
+ return sdk.getBoundHarnessAndPrompt(context, plan);
1467
+ }
1468
+ export * from './runWithCognition.js';
727
1469
  //# sourceMappingURL=index.js.map