@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,594 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync, readFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { join } from 'node:path';
6
+
7
+ const CONFIG_PATH = join(homedir(), '.aria', 'config.json');
8
+ const DEFAULT_OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://127.0.0.1:11434';
9
+ const DEFAULT_DEEPSEEK_BASE_URL = process.env.DEEPSEEK_API_BASE || 'https://api.deepseek.com/v1/chat/completions';
10
+ const DEFAULT_OLLAMA_MODEL =
11
+ process.env.ARIA_RUNTIME_MODEL ||
12
+ process.env.ARIA_LOCAL_MODEL ||
13
+ process.env.OLLAMA_MODEL ||
14
+ 'llama3';
15
+ const DEFAULT_CHAT_MODEL =
16
+ process.env.ARIA_CHAT_MODEL ||
17
+ process.env.ARIA_CASUAL_MODEL ||
18
+ 'deepseek-v4-flash';
19
+ const DEFAULT_DEEP_MODEL =
20
+ process.env.ARIA_DEEP_MODEL ||
21
+ 'deepseek-v4-pro';
22
+ const DEFAULT_XAI_BASE_URL = process.env.XAI_API_BASE || 'https://api.x.ai/v1/chat/completions';
23
+ const DEFAULT_XAI_FALLBACK_MODEL =
24
+ process.env.ARIA_XAI_FALLBACK_MODEL ||
25
+ 'grok-4-2-reasoning';
26
+ const DEFAULT_NIM_BASE_URL = process.env.NIM_BASE_URL || 'https://integrate.api.nvidia.com/v1/chat/completions';
27
+ const DEFAULT_NIM_FALLBACK_MODEL =
28
+ process.env.ARIA_NIM_FALLBACK_MODEL ||
29
+ 'qwen/qwen3.5-122b-a10b';
30
+
31
+ function safeJsonParse(raw, fallback = null) {
32
+ try {
33
+ return JSON.parse(raw);
34
+ } catch {
35
+ return fallback;
36
+ }
37
+ }
38
+
39
+ function loadLocalConfig() {
40
+ if (!existsSync(CONFIG_PATH)) return null;
41
+ return safeJsonParse(readFileSync(CONFIG_PATH, 'utf8'), null);
42
+ }
43
+
44
+ function coerceString(value) {
45
+ return typeof value === 'string' && value.trim() ? value.trim() : '';
46
+ }
47
+
48
+ function loadRuntimeProfiles(local) {
49
+ const profiles = local?.runtimeProfiles && typeof local.runtimeProfiles === 'object' ? local.runtimeProfiles : {};
50
+ return {
51
+ chatModel: coerceString(profiles.chatModel || local?.chatModel || local?.model?.chatModel) || DEFAULT_CHAT_MODEL,
52
+ deepModel: coerceString(profiles.deepModel || local?.deepModel || local?.model?.deepModel) || DEFAULT_DEEP_MODEL,
53
+ xaiFallbackModel: coerceString(profiles.xaiFallbackModel || local?.xaiFallbackModel || local?.model?.xaiFallbackModel) || DEFAULT_XAI_FALLBACK_MODEL,
54
+ nimFallbackModel: coerceString(profiles.nimFallbackModel || local?.nimFallbackModel || local?.model?.nimFallbackModel) || DEFAULT_NIM_FALLBACK_MODEL,
55
+ xaiApiKey: coerceString(profiles.xaiApiKey || local?.xaiApiKey),
56
+ nimApiKey: coerceString(profiles.nimApiKey || local?.nimApiKey),
57
+ xaiBaseUrl: coerceString(profiles.xaiBaseUrl || local?.xaiBaseUrl) || DEFAULT_XAI_BASE_URL,
58
+ nimBaseUrl: coerceString(profiles.nimBaseUrl || local?.nimBaseUrl) || DEFAULT_NIM_BASE_URL,
59
+ };
60
+ }
61
+
62
+ function extractIntentText(body = {}) {
63
+ const pieces = [];
64
+ if (typeof body.ariaPlannedApproach === 'string') pieces.push(body.ariaPlannedApproach);
65
+ if (typeof body.ariaRationale === 'string') pieces.push(body.ariaRationale);
66
+ if (typeof body.prompt === 'string') pieces.push(body.prompt);
67
+ if (typeof body.input === 'string') pieces.push(body.input);
68
+ if (typeof body.modelIntent === 'string') pieces.push(body.modelIntent);
69
+ if (typeof body?.metadata?.roleProfile === 'string') pieces.push(body.metadata.roleProfile);
70
+ if (typeof body?.metadata?.intent === 'string') pieces.push(body.metadata.intent);
71
+ if (typeof body?.metadata?.mode === 'string') pieces.push(body.metadata.mode);
72
+ if (Array.isArray(body.messages)) {
73
+ for (const message of body.messages) {
74
+ if (typeof message?.content === 'string') pieces.push(message.content);
75
+ if (Array.isArray(message?.content)) {
76
+ for (const part of message.content) {
77
+ if (typeof part === 'string') pieces.push(part);
78
+ if (typeof part?.text === 'string') pieces.push(part.text);
79
+ }
80
+ }
81
+ }
82
+ }
83
+ return pieces.filter(Boolean).join('\n');
84
+ }
85
+
86
+ function isDeepArchitectureIntent(body = {}) {
87
+ const intent = extractIntentText(body).toLowerCase();
88
+ if (!intent) return false;
89
+ return /\b(architect|architecture|system design|design doc|deepwork|deep work|reasoning|first principles|migration plan|refactor plan|root cause|tradeoff|trade-off|analy[sz]e deeply|complex code|hardener|planner)\b/i.test(intent);
90
+ }
91
+
92
+ function resolveProviderApiKey(provider, overrideApiKey = '', localApiKey = '') {
93
+ if (overrideApiKey) return overrideApiKey;
94
+ if (localApiKey) return localApiKey;
95
+ switch (provider) {
96
+ case 'deepseek':
97
+ return process.env.DEEPSEEK_API_KEY || '';
98
+ case 'openai':
99
+ return process.env.OPENAI_API_KEY || '';
100
+ case 'anthropic':
101
+ return process.env.ANTHROPIC_API_KEY || '';
102
+ case 'google':
103
+ return process.env.GOOGLE_API_KEY || process.env.GEMINI_API_KEY || '';
104
+ case 'openrouter':
105
+ return process.env.OPENROUTER_API_KEY || '';
106
+ case 'xai':
107
+ return process.env.XAI_API_KEY || '';
108
+ case 'nim':
109
+ case 'nvidia-nim':
110
+ return process.env.NVIDIA_NIM_API_KEY || process.env.NIM_API_KEY || '';
111
+ default:
112
+ return '';
113
+ }
114
+ }
115
+
116
+ function resolveDefaultModel(provider, body, localModel, runtimeProfiles) {
117
+ const explicitModel = coerceString(body?.llm?.model || body?.providerConfig?.model || body?.model);
118
+ if (explicitModel) return explicitModel;
119
+ if (provider === 'deepseek') {
120
+ return isDeepArchitectureIntent(body) ? runtimeProfiles.deepModel : runtimeProfiles.chatModel;
121
+ }
122
+ if (provider === 'xai') return runtimeProfiles.xaiFallbackModel;
123
+ if (provider === 'nim' || provider === 'nvidia-nim') return runtimeProfiles.nimFallbackModel;
124
+ if (coerceString(localModel?.model)) return coerceString(localModel.model);
125
+ if (provider === 'ollama') return DEFAULT_OLLAMA_MODEL;
126
+ return '';
127
+ }
128
+
129
+ function buildFallbackChain(body, runtimeProfiles) {
130
+ if (body.allowProviderFallback === false) return [];
131
+ const explicitFallbacks = Array.isArray(body?.llm?.fallbacks)
132
+ ? body.llm.fallbacks
133
+ : Array.isArray(body?.providerConfig?.fallbacks)
134
+ ? body.providerConfig.fallbacks
135
+ : [];
136
+ if (explicitFallbacks.length > 0) return explicitFallbacks;
137
+ return [
138
+ {
139
+ provider: 'xai',
140
+ model: runtimeProfiles.xaiFallbackModel,
141
+ baseUrl: runtimeProfiles.xaiBaseUrl,
142
+ apiKey: runtimeProfiles.xaiApiKey || process.env.XAI_API_KEY || '',
143
+ reason: 'xai reasoning fallback',
144
+ },
145
+ {
146
+ provider: 'nim',
147
+ model: runtimeProfiles.nimFallbackModel,
148
+ baseUrl: runtimeProfiles.nimBaseUrl,
149
+ apiKey: runtimeProfiles.nimApiKey || process.env.NVIDIA_NIM_API_KEY || process.env.NIM_API_KEY || '',
150
+ reason: 'nim qwen fallback',
151
+ },
152
+ ].filter((entry) => entry.apiKey && entry.model);
153
+ }
154
+
155
+ async function callResolvedProvider(config, body, ariaSystemPrompt) {
156
+ const model = body.model || config.model || 'gpt-4o';
157
+ const messages = composeOpenAIMessages(Array.isArray(body.messages) ? body.messages : [], ariaSystemPrompt);
158
+ const maxTokens = Number(body.max_tokens || body.max_completion_tokens || 4096);
159
+
160
+ if (config.provider === 'anthropic') {
161
+ const anthropicPayload = {
162
+ model,
163
+ max_tokens: maxTokens,
164
+ messages: messages.filter((message) => message.role !== 'system').map((message) => ({
165
+ role: message.role,
166
+ content: [{ type: 'text', text: message.content }],
167
+ })),
168
+ system: messages.filter((message) => message.role === 'system').map((message) => ({
169
+ type: 'text',
170
+ text: message.content,
171
+ })),
172
+ };
173
+ const data = await callAnthropic(config.baseUrl || 'https://api.anthropic.com/v1/messages', config.apiKey, anthropicPayload);
174
+ const text = Array.isArray(data.content)
175
+ ? data.content.filter((part) => part?.type === 'text').map((part) => part.text).join('')
176
+ : '';
177
+ return {
178
+ provider: config.provider,
179
+ model,
180
+ text,
181
+ usage: asUsage(data?.usage?.input_tokens, data?.usage?.output_tokens),
182
+ finishReason: data?.stop_reason || 'stop',
183
+ raw: data,
184
+ };
185
+ }
186
+
187
+ if (config.provider === 'google') {
188
+ const googlePayload = {
189
+ contents: messages
190
+ .filter((message) => message.role !== 'system')
191
+ .map((message) => ({
192
+ role: message.role === 'assistant' ? 'model' : 'user',
193
+ parts: [{ text: message.content }],
194
+ })),
195
+ system_instruction: {
196
+ parts: [{ text: messages.filter((message) => message.role === 'system').map((message) => message.content).join('\n\n') }],
197
+ },
198
+ generationConfig: {
199
+ maxOutputTokens: maxTokens,
200
+ },
201
+ };
202
+ const data = await callGoogle(model || 'gemini-2.5-pro', config.apiKey, googlePayload);
203
+ const text = data?.candidates?.[0]?.content?.parts?.map((part) => part.text).join('') || '';
204
+ return {
205
+ provider: config.provider,
206
+ model,
207
+ text,
208
+ usage: asUsage(data?.usageMetadata?.promptTokenCount, data?.usageMetadata?.candidatesTokenCount),
209
+ finishReason: data?.candidates?.[0]?.finishReason || 'stop',
210
+ raw: data,
211
+ };
212
+ }
213
+
214
+ if (config.provider === 'ollama') {
215
+ const data = await callOllama(config.baseUrl || process.env.OLLAMA_HOST || 'http://localhost:11434', {
216
+ model,
217
+ messages,
218
+ stream: false,
219
+ options: { num_predict: maxTokens },
220
+ });
221
+ return {
222
+ provider: config.provider,
223
+ model,
224
+ text: data?.message?.content || '',
225
+ usage: asUsage(data?.prompt_eval_count, data?.eval_count),
226
+ finishReason: data?.done ? 'stop' : 'unknown',
227
+ raw: data,
228
+ };
229
+ }
230
+
231
+ const urlByProvider = {
232
+ openai: config.baseUrl || 'https://api.openai.com/v1/chat/completions',
233
+ deepseek: config.baseUrl || 'https://api.deepseek.com/v1/chat/completions',
234
+ openrouter: config.baseUrl || 'https://openrouter.ai/api/v1/chat/completions',
235
+ xai: config.baseUrl || DEFAULT_XAI_BASE_URL,
236
+ nim: config.baseUrl || DEFAULT_NIM_BASE_URL,
237
+ 'nvidia-nim': config.baseUrl || DEFAULT_NIM_BASE_URL,
238
+ };
239
+ const providerUrl = urlByProvider[config.provider] || urlByProvider.openai;
240
+ const extraHeaders = config.provider === 'openrouter'
241
+ ? {
242
+ 'HTTP-Referer': 'http://127.0.0.1',
243
+ 'X-Title': 'Aria Mounted Runtime',
244
+ }
245
+ : {};
246
+ const data = await callOpenAICompatible(providerUrl, config.apiKey, {
247
+ model,
248
+ messages,
249
+ max_tokens: maxTokens,
250
+ stream: false,
251
+ temperature: body.temperature,
252
+ top_p: body.top_p,
253
+ tools: Array.isArray(body.tools) ? body.tools : undefined,
254
+ tool_choice: body.tool_choice,
255
+ }, extraHeaders);
256
+
257
+ return {
258
+ provider: config.provider,
259
+ model,
260
+ text: data?.choices?.[0]?.message?.content || '',
261
+ usage: asUsage(data?.usage?.prompt_tokens, data?.usage?.completion_tokens),
262
+ finishReason: data?.choices?.[0]?.finish_reason || 'stop',
263
+ raw: data,
264
+ };
265
+ }
266
+
267
+ export function resolveProviderConfig(body = {}) {
268
+ const local = loadLocalConfig();
269
+ const localModel = local?.model && typeof local.model === 'object' ? local.model : null;
270
+ const override = body.llm && typeof body.llm === 'object' ? body.llm : body.providerConfig && typeof body.providerConfig === 'object' ? body.providerConfig : null;
271
+ const runtimeProfiles = loadRuntimeProfiles(local);
272
+ const provider =
273
+ override?.provider ||
274
+ body.provider ||
275
+ localModel?.provider ||
276
+ coerceString(local?.defaultProvider) ||
277
+ (process.env.DEEPSEEK_API_KEY ? 'deepseek' : 'ollama');
278
+ const merged = {
279
+ provider,
280
+ model: resolveDefaultModel(provider, body, localModel, runtimeProfiles),
281
+ apiKey: resolveProviderApiKey(
282
+ provider,
283
+ coerceString(override?.apiKey || body.providerApiKey),
284
+ coerceString(localModel?.apiKey),
285
+ ),
286
+ baseUrl:
287
+ override?.baseUrl ||
288
+ body.providerBaseUrl ||
289
+ localModel?.baseUrl ||
290
+ (provider === 'ollama'
291
+ ? DEFAULT_OLLAMA_BASE_URL
292
+ : provider === 'deepseek'
293
+ ? DEFAULT_DEEPSEEK_BASE_URL
294
+ : provider === 'xai'
295
+ ? DEFAULT_XAI_BASE_URL
296
+ : provider === 'nim' || provider === 'nvidia-nim'
297
+ ? DEFAULT_NIM_BASE_URL
298
+ : ''),
299
+ };
300
+
301
+ if (!merged.provider) {
302
+ throw new Error('No provider configured. Save one in ~/.aria/config.json or pass llm/providerConfig in the request body.');
303
+ }
304
+
305
+ if (merged.provider !== 'ollama' && merged.provider !== 'google' && !merged.apiKey) {
306
+ throw new Error(`No API key configured for provider ${merged.provider}. Save one in ~/.aria/config.json or pass llm.apiKey.`);
307
+ }
308
+
309
+ return merged;
310
+ }
311
+
312
+ export function extractOpenAIUserMessage(messages = []) {
313
+ const reversed = [...messages].reverse();
314
+ for (const message of reversed) {
315
+ if (message?.role === 'user') return flattenOpenAIContent(message.content);
316
+ }
317
+ return '';
318
+ }
319
+
320
+ export function extractAnthropicUserMessage(messages = []) {
321
+ const reversed = [...messages].reverse();
322
+ for (const message of reversed) {
323
+ if (message?.role === 'user') return flattenAnthropicContent(message.content);
324
+ }
325
+ return '';
326
+ }
327
+
328
+ function flattenOpenAIContent(content) {
329
+ if (typeof content === 'string') return content;
330
+ if (!Array.isArray(content)) return '';
331
+ return content
332
+ .map((part) => {
333
+ if (typeof part === 'string') return part;
334
+ if (part?.type === 'text' && typeof part.text === 'string') return part.text;
335
+ if (typeof part?.content === 'string') return part.content;
336
+ return '';
337
+ })
338
+ .filter(Boolean)
339
+ .join('\n');
340
+ }
341
+
342
+ function flattenAnthropicContent(content) {
343
+ if (typeof content === 'string') return content;
344
+ if (!Array.isArray(content)) return '';
345
+ return content
346
+ .map((part) => {
347
+ if (typeof part === 'string') return part;
348
+ if (part?.type === 'text' && typeof part.text === 'string') return part.text;
349
+ return '';
350
+ })
351
+ .filter(Boolean)
352
+ .join('\n');
353
+ }
354
+
355
+ function extractOpenAISystem(messages = []) {
356
+ return messages
357
+ .filter((message) => message?.role === 'system')
358
+ .map((message) => flattenOpenAIContent(message.content))
359
+ .filter(Boolean)
360
+ .join('\n\n');
361
+ }
362
+
363
+ function extractAnthropicSystem(systemField) {
364
+ if (typeof systemField === 'string') return systemField;
365
+ if (!Array.isArray(systemField)) return '';
366
+ return systemField
367
+ .map((part) => {
368
+ if (typeof part === 'string') return part;
369
+ if (part?.type === 'text' && typeof part.text === 'string') return part.text;
370
+ return '';
371
+ })
372
+ .filter(Boolean)
373
+ .join('\n\n');
374
+ }
375
+
376
+ export function composeOpenAIMessages(messages = [], ariaSystemPrompt) {
377
+ const incomingSystem = extractOpenAISystem(messages);
378
+ const mergedSystem = [ariaSystemPrompt, incomingSystem].filter(Boolean).join('\n\n');
379
+ const nonSystem = messages
380
+ .filter((message) => message?.role !== 'system')
381
+ .map((message) => ({
382
+ role: message.role,
383
+ content: flattenOpenAIContent(message.content),
384
+ }));
385
+ return [{ role: 'system', content: mergedSystem }, ...nonSystem];
386
+ }
387
+
388
+ export function composeAnthropicRequest(body = {}, ariaSystemPrompt) {
389
+ const incomingSystem = extractAnthropicSystem(body.system);
390
+ const mergedSystem = [ariaSystemPrompt, incomingSystem].filter(Boolean).join('\n\n');
391
+ const messages = Array.isArray(body.messages) ? body.messages.map((message) => ({
392
+ role: message.role,
393
+ content: Array.isArray(message.content)
394
+ ? message.content
395
+ : [{ type: 'text', text: flattenAnthropicContent(message.content) }],
396
+ })) : [];
397
+ return {
398
+ system: mergedSystem,
399
+ messages,
400
+ };
401
+ }
402
+
403
+ function asUsage(promptTokens, completionTokens) {
404
+ if (promptTokens == null && completionTokens == null) return null;
405
+ return {
406
+ promptTokens: Number(promptTokens || 0),
407
+ completionTokens: Number(completionTokens || 0),
408
+ };
409
+ }
410
+
411
+ async function callOpenAICompatible(url, apiKey, payload, extraHeaders = {}) {
412
+ let response;
413
+ try {
414
+ response = await fetch(url, {
415
+ method: 'POST',
416
+ headers: {
417
+ 'Content-Type': 'application/json',
418
+ Authorization: `Bearer ${apiKey}`,
419
+ ...extraHeaders,
420
+ },
421
+ body: JSON.stringify(payload),
422
+ });
423
+ } catch (error) {
424
+ throw new Error(`Provider fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`);
425
+ }
426
+
427
+ if (!response.ok) {
428
+ const body = await response.text().catch(() => response.statusText);
429
+ throw new Error(`Provider error ${response.status}: ${body}`);
430
+ }
431
+
432
+ return response.json();
433
+ }
434
+
435
+ async function callAnthropic(url, apiKey, payload) {
436
+ let response;
437
+ try {
438
+ response = await fetch(url, {
439
+ method: 'POST',
440
+ headers: {
441
+ 'Content-Type': 'application/json',
442
+ 'x-api-key': apiKey,
443
+ 'anthropic-version': '2023-06-01',
444
+ },
445
+ body: JSON.stringify(payload),
446
+ });
447
+ } catch (error) {
448
+ throw new Error(`Anthropic fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`);
449
+ }
450
+
451
+ if (!response.ok) {
452
+ const body = await response.text().catch(() => response.statusText);
453
+ throw new Error(`Anthropic error ${response.status}: ${body}`);
454
+ }
455
+
456
+ return response.json();
457
+ }
458
+
459
+ async function callGoogle(model, apiKey, payload) {
460
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
461
+ let response;
462
+ try {
463
+ response = await fetch(url, {
464
+ method: 'POST',
465
+ headers: { 'Content-Type': 'application/json' },
466
+ body: JSON.stringify(payload),
467
+ });
468
+ } catch (error) {
469
+ throw new Error(`Google fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`);
470
+ }
471
+
472
+ if (!response.ok) {
473
+ const body = await response.text().catch(() => response.statusText);
474
+ throw new Error(`Google error ${response.status}: ${body}`);
475
+ }
476
+
477
+ return response.json();
478
+ }
479
+
480
+ async function callOllama(baseUrl, payload) {
481
+ const url = `${baseUrl.replace(/\/$/, '')}/api/chat`;
482
+ let response;
483
+ try {
484
+ response = await fetch(url, {
485
+ method: 'POST',
486
+ headers: { 'Content-Type': 'application/json' },
487
+ body: JSON.stringify(payload),
488
+ });
489
+ } catch (error) {
490
+ throw new Error(`Ollama fetch failed for ${url}: ${error instanceof Error ? error.message : String(error)}`);
491
+ }
492
+
493
+ if (!response.ok) {
494
+ const body = await response.text().catch(() => response.statusText);
495
+ throw new Error(`Ollama error ${response.status}: ${body}`);
496
+ }
497
+
498
+ return response.json();
499
+ }
500
+
501
+ export async function callProviderForOpenAI(body, ariaSystemPrompt) {
502
+ const config = resolveProviderConfig(body);
503
+ const runtimeProfiles = loadRuntimeProfiles(loadLocalConfig());
504
+ try {
505
+ return await callResolvedProvider(config, body, ariaSystemPrompt);
506
+ } catch (primaryError) {
507
+ const fallbacks = buildFallbackChain(body, runtimeProfiles)
508
+ .filter((fallback) => fallback.provider !== config.provider || fallback.model !== config.model);
509
+ for (const fallback of fallbacks) {
510
+ try {
511
+ return await callResolvedProvider({
512
+ provider: fallback.provider,
513
+ model: fallback.model,
514
+ apiKey: fallback.apiKey,
515
+ baseUrl: fallback.baseUrl,
516
+ }, {
517
+ ...body,
518
+ allowProviderFallback: false,
519
+ llm: {
520
+ ...(body.llm && typeof body.llm === 'object' ? body.llm : {}),
521
+ provider: fallback.provider,
522
+ model: fallback.model,
523
+ apiKey: fallback.apiKey,
524
+ baseUrl: fallback.baseUrl,
525
+ },
526
+ metadata: {
527
+ ...(body.metadata && typeof body.metadata === 'object' ? body.metadata : {}),
528
+ provider_fallback_from: `${config.provider}:${config.model}`,
529
+ provider_fallback_to: `${fallback.provider}:${fallback.model}`,
530
+ provider_fallback_reason: fallback.reason || 'fallback',
531
+ },
532
+ }, ariaSystemPrompt);
533
+ } catch {}
534
+ }
535
+ throw primaryError;
536
+ }
537
+ }
538
+
539
+ export async function callProviderForAnthropic(body, ariaSystemPrompt) {
540
+ const config = resolveProviderConfig(body);
541
+ const model = body.model || config.model || 'claude-sonnet-4-20250514';
542
+ const req = composeAnthropicRequest(body, ariaSystemPrompt);
543
+ const maxTokens = Number(body.max_tokens || 4096);
544
+
545
+ if (config.provider === 'anthropic') {
546
+ const data = await callAnthropic(config.baseUrl || 'https://api.anthropic.com/v1/messages', config.apiKey, {
547
+ model,
548
+ max_tokens: maxTokens,
549
+ messages: req.messages,
550
+ system: [{ type: 'text', text: req.system }],
551
+ });
552
+ const text = Array.isArray(data.content)
553
+ ? data.content.filter((part) => part?.type === 'text').map((part) => part.text).join('')
554
+ : '';
555
+ return {
556
+ provider: config.provider,
557
+ model,
558
+ text,
559
+ usage: asUsage(data?.usage?.input_tokens, data?.usage?.output_tokens),
560
+ finishReason: data?.stop_reason || 'stop',
561
+ raw: data,
562
+ };
563
+ }
564
+
565
+ const openAiMessages = [
566
+ { role: 'system', content: req.system },
567
+ ...req.messages.map((message) => ({
568
+ role: message.role,
569
+ content: flattenAnthropicContent(message.content),
570
+ })),
571
+ ];
572
+ return callProviderForOpenAI({
573
+ ...body,
574
+ provider: config.provider,
575
+ model,
576
+ messages: openAiMessages,
577
+ llm: config,
578
+ }, req.system);
579
+ }
580
+
581
+ export function chunkText(text, maxChunkChars = 120) {
582
+ const content = String(text || '');
583
+ if (!content) return [''];
584
+ const chunks = [];
585
+ let remaining = content;
586
+ while (remaining.length > maxChunkChars) {
587
+ let cut = remaining.lastIndexOf(' ', maxChunkChars);
588
+ if (cut < Math.floor(maxChunkChars / 2)) cut = maxChunkChars;
589
+ chunks.push(remaining.slice(0, cut));
590
+ remaining = remaining.slice(cut).trimStart();
591
+ }
592
+ if (remaining) chunks.push(remaining);
593
+ return chunks;
594
+ }