aura-code 0.3.0

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 (288) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +137 -0
  3. package/dist/agent/context.d.ts +13 -0
  4. package/dist/agent/context.js +183 -0
  5. package/dist/agent/context.js.map +1 -0
  6. package/dist/agent/loop.d.ts +60 -0
  7. package/dist/agent/loop.js +228 -0
  8. package/dist/agent/loop.js.map +1 -0
  9. package/dist/agent/session-store.d.ts +30 -0
  10. package/dist/agent/session-store.js +178 -0
  11. package/dist/agent/session-store.js.map +1 -0
  12. package/dist/agent/spawner.d.ts +57 -0
  13. package/dist/agent/spawner.js +112 -0
  14. package/dist/agent/spawner.js.map +1 -0
  15. package/dist/agent/system-prompt.d.ts +3 -0
  16. package/dist/agent/system-prompt.js +100 -0
  17. package/dist/agent/system-prompt.js.map +1 -0
  18. package/dist/architect/engine.d.ts +47 -0
  19. package/dist/architect/engine.js +217 -0
  20. package/dist/architect/engine.js.map +1 -0
  21. package/dist/architect/types.d.ts +61 -0
  22. package/dist/architect/types.js +6 -0
  23. package/dist/architect/types.js.map +1 -0
  24. package/dist/cli/diamond.d.ts +1 -0
  25. package/dist/cli/diamond.js +23 -0
  26. package/dist/cli/diamond.js.map +1 -0
  27. package/dist/cli/display.d.ts +44 -0
  28. package/dist/cli/display.js +234 -0
  29. package/dist/cli/display.js.map +1 -0
  30. package/dist/cli/index.d.ts +2 -0
  31. package/dist/cli/index.js +1734 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/config/defaults.d.ts +46 -0
  34. package/dist/config/defaults.js +106 -0
  35. package/dist/config/defaults.js.map +1 -0
  36. package/dist/config/project-config.d.ts +90 -0
  37. package/dist/config/project-config.js +141 -0
  38. package/dist/config/project-config.js.map +1 -0
  39. package/dist/harness/proposer.d.ts +26 -0
  40. package/dist/harness/proposer.js +204 -0
  41. package/dist/harness/proposer.js.map +1 -0
  42. package/dist/harness/weakness-miner.d.ts +28 -0
  43. package/dist/harness/weakness-miner.js +359 -0
  44. package/dist/harness/weakness-miner.js.map +1 -0
  45. package/dist/integrations/harnesses.d.ts +23 -0
  46. package/dist/integrations/harnesses.js +161 -0
  47. package/dist/integrations/harnesses.js.map +1 -0
  48. package/dist/orchestration/competence.d.ts +53 -0
  49. package/dist/orchestration/competence.js +181 -0
  50. package/dist/orchestration/competence.js.map +1 -0
  51. package/dist/orchestration/executor.d.ts +42 -0
  52. package/dist/orchestration/executor.js +206 -0
  53. package/dist/orchestration/executor.js.map +1 -0
  54. package/dist/orchestration/index.d.ts +20 -0
  55. package/dist/orchestration/index.js +39 -0
  56. package/dist/orchestration/index.js.map +1 -0
  57. package/dist/orchestration/orchestrator-prompts.d.ts +11 -0
  58. package/dist/orchestration/orchestrator-prompts.js +153 -0
  59. package/dist/orchestration/orchestrator-prompts.js.map +1 -0
  60. package/dist/orchestration/orchestrator.d.ts +27 -0
  61. package/dist/orchestration/orchestrator.js +169 -0
  62. package/dist/orchestration/orchestrator.js.map +1 -0
  63. package/dist/orchestration/plan-store.d.ts +53 -0
  64. package/dist/orchestration/plan-store.js +176 -0
  65. package/dist/orchestration/plan-store.js.map +1 -0
  66. package/dist/orchestration/router-prompts.d.ts +6 -0
  67. package/dist/orchestration/router-prompts.js +55 -0
  68. package/dist/orchestration/router-prompts.js.map +1 -0
  69. package/dist/orchestration/router.d.ts +23 -0
  70. package/dist/orchestration/router.js +70 -0
  71. package/dist/orchestration/router.js.map +1 -0
  72. package/dist/orchestration/ruby-detect.d.ts +6 -0
  73. package/dist/orchestration/ruby-detect.js +130 -0
  74. package/dist/orchestration/ruby-detect.js.map +1 -0
  75. package/dist/orchestration/ruby-types.d.ts +44 -0
  76. package/dist/orchestration/ruby-types.js +6 -0
  77. package/dist/orchestration/ruby-types.js.map +1 -0
  78. package/dist/orchestration/specialist-prompts.d.ts +19 -0
  79. package/dist/orchestration/specialist-prompts.js +143 -0
  80. package/dist/orchestration/specialist-prompts.js.map +1 -0
  81. package/dist/orchestration/specialists.d.ts +66 -0
  82. package/dist/orchestration/specialists.js +175 -0
  83. package/dist/orchestration/specialists.js.map +1 -0
  84. package/dist/orchestration/types.d.ts +82 -0
  85. package/dist/orchestration/types.js +6 -0
  86. package/dist/orchestration/types.js.map +1 -0
  87. package/dist/perception/extractor.d.ts +7 -0
  88. package/dist/perception/extractor.js +521 -0
  89. package/dist/perception/extractor.js.map +1 -0
  90. package/dist/perception/graph-store.d.ts +25 -0
  91. package/dist/perception/graph-store.js +117 -0
  92. package/dist/perception/graph-store.js.map +1 -0
  93. package/dist/perception/index.d.ts +4 -0
  94. package/dist/perception/index.js +18 -0
  95. package/dist/perception/index.js.map +1 -0
  96. package/dist/perception/queries.d.ts +29 -0
  97. package/dist/perception/queries.js +354 -0
  98. package/dist/perception/queries.js.map +1 -0
  99. package/dist/perception/types.d.ts +103 -0
  100. package/dist/perception/types.js +6 -0
  101. package/dist/perception/types.js.map +1 -0
  102. package/dist/providers/anthropic.d.ts +11 -0
  103. package/dist/providers/anthropic.js +172 -0
  104. package/dist/providers/anthropic.js.map +1 -0
  105. package/dist/providers/factory.d.ts +58 -0
  106. package/dist/providers/factory.js +354 -0
  107. package/dist/providers/factory.js.map +1 -0
  108. package/dist/providers/fallback.d.ts +24 -0
  109. package/dist/providers/fallback.js +72 -0
  110. package/dist/providers/fallback.js.map +1 -0
  111. package/dist/providers/google.d.ts +11 -0
  112. package/dist/providers/google.js +129 -0
  113. package/dist/providers/google.js.map +1 -0
  114. package/dist/providers/openai-compatible.d.ts +24 -0
  115. package/dist/providers/openai-compatible.js +216 -0
  116. package/dist/providers/openai-compatible.js.map +1 -0
  117. package/dist/providers/resilient-factory.d.ts +24 -0
  118. package/dist/providers/resilient-factory.js +57 -0
  119. package/dist/providers/resilient-factory.js.map +1 -0
  120. package/dist/providers/resilient.d.ts +63 -0
  121. package/dist/providers/resilient.js +145 -0
  122. package/dist/providers/resilient.js.map +1 -0
  123. package/dist/providers/types.d.ts +84 -0
  124. package/dist/providers/types.js +6 -0
  125. package/dist/providers/types.js.map +1 -0
  126. package/dist/ruby/alternator.d.ts +40 -0
  127. package/dist/ruby/alternator.js +233 -0
  128. package/dist/ruby/alternator.js.map +1 -0
  129. package/dist/ruby/competence.d.ts +34 -0
  130. package/dist/ruby/competence.js +264 -0
  131. package/dist/ruby/competence.js.map +1 -0
  132. package/dist/ruby/episode-capture.d.ts +56 -0
  133. package/dist/ruby/episode-capture.js +193 -0
  134. package/dist/ruby/episode-capture.js.map +1 -0
  135. package/dist/ruby/fine-tune.d.ts +16 -0
  136. package/dist/ruby/fine-tune.js +253 -0
  137. package/dist/ruby/fine-tune.js.map +1 -0
  138. package/dist/ruby/index.d.ts +10 -0
  139. package/dist/ruby/index.js +26 -0
  140. package/dist/ruby/index.js.map +1 -0
  141. package/dist/ruby/ruby-model.d.ts +36 -0
  142. package/dist/ruby/ruby-model.js +81 -0
  143. package/dist/ruby/ruby-model.js.map +1 -0
  144. package/dist/ruby/training-data.d.ts +11 -0
  145. package/dist/ruby/training-data.js +107 -0
  146. package/dist/ruby/training-data.js.map +1 -0
  147. package/dist/ruby/types.d.ts +136 -0
  148. package/dist/ruby/types.js +20 -0
  149. package/dist/ruby/types.js.map +1 -0
  150. package/dist/safety/permissions.d.ts +32 -0
  151. package/dist/safety/permissions.js +304 -0
  152. package/dist/safety/permissions.js.map +1 -0
  153. package/dist/server/index.d.ts +9 -0
  154. package/dist/server/index.js +342 -0
  155. package/dist/server/index.js.map +1 -0
  156. package/dist/server/session.d.ts +22 -0
  157. package/dist/server/session.js +16 -0
  158. package/dist/server/session.js.map +1 -0
  159. package/dist/setup/first-run.d.ts +41 -0
  160. package/dist/setup/first-run.js +386 -0
  161. package/dist/setup/first-run.js.map +1 -0
  162. package/dist/setup/global-config.d.ts +19 -0
  163. package/dist/setup/global-config.js +97 -0
  164. package/dist/setup/global-config.js.map +1 -0
  165. package/dist/tools/audio-transcribe.d.ts +8 -0
  166. package/dist/tools/audio-transcribe.js +156 -0
  167. package/dist/tools/audio-transcribe.js.map +1 -0
  168. package/dist/tools/browser.d.ts +15 -0
  169. package/dist/tools/browser.js +241 -0
  170. package/dist/tools/browser.js.map +1 -0
  171. package/dist/tools/calendar.d.ts +12 -0
  172. package/dist/tools/calendar.js +196 -0
  173. package/dist/tools/calendar.js.map +1 -0
  174. package/dist/tools/clipboard.d.ts +7 -0
  175. package/dist/tools/clipboard.js +90 -0
  176. package/dist/tools/clipboard.js.map +1 -0
  177. package/dist/tools/cron.d.ts +10 -0
  178. package/dist/tools/cron.js +167 -0
  179. package/dist/tools/cron.js.map +1 -0
  180. package/dist/tools/edit-file.d.ts +6 -0
  181. package/dist/tools/edit-file.js +98 -0
  182. package/dist/tools/edit-file.js.map +1 -0
  183. package/dist/tools/email.d.ts +11 -0
  184. package/dist/tools/email.js +153 -0
  185. package/dist/tools/email.js.map +1 -0
  186. package/dist/tools/git.d.ts +1 -0
  187. package/dist/tools/git.js +7 -0
  188. package/dist/tools/git.js.map +1 -0
  189. package/dist/tools/http-request.d.ts +12 -0
  190. package/dist/tools/http-request.js +91 -0
  191. package/dist/tools/http-request.js.map +1 -0
  192. package/dist/tools/image-read.d.ts +7 -0
  193. package/dist/tools/image-read.js +125 -0
  194. package/dist/tools/image-read.js.map +1 -0
  195. package/dist/tools/index.d.ts +3 -0
  196. package/dist/tools/index.js +201 -0
  197. package/dist/tools/index.js.map +1 -0
  198. package/dist/tools/list-dir.d.ts +1 -0
  199. package/dist/tools/list-dir.js +6 -0
  200. package/dist/tools/list-dir.js.map +1 -0
  201. package/dist/tools/mcp.d.ts +11 -0
  202. package/dist/tools/mcp.js +356 -0
  203. package/dist/tools/mcp.js.map +1 -0
  204. package/dist/tools/memory.d.ts +9 -0
  205. package/dist/tools/memory.js +124 -0
  206. package/dist/tools/memory.js.map +1 -0
  207. package/dist/tools/notify.d.ts +9 -0
  208. package/dist/tools/notify.js +40 -0
  209. package/dist/tools/notify.js.map +1 -0
  210. package/dist/tools/read-file.d.ts +6 -0
  211. package/dist/tools/read-file.js +76 -0
  212. package/dist/tools/read-file.js.map +1 -0
  213. package/dist/tools/run-shell.d.ts +1 -0
  214. package/dist/tools/run-shell.js +6 -0
  215. package/dist/tools/run-shell.js.map +1 -0
  216. package/dist/tools/run-tests.d.ts +1 -0
  217. package/dist/tools/run-tests.js +6 -0
  218. package/dist/tools/run-tests.js.map +1 -0
  219. package/dist/tools/search-code.d.ts +1 -0
  220. package/dist/tools/search-code.js +6 -0
  221. package/dist/tools/search-code.js.map +1 -0
  222. package/dist/tools/telegram-bot.d.ts +2 -0
  223. package/dist/tools/telegram-bot.js +693 -0
  224. package/dist/tools/telegram-bot.js.map +1 -0
  225. package/dist/tools/telegram-safety.d.ts +52 -0
  226. package/dist/tools/telegram-safety.js +189 -0
  227. package/dist/tools/telegram-safety.js.map +1 -0
  228. package/dist/tools/telegram.d.ts +16 -0
  229. package/dist/tools/telegram.js +227 -0
  230. package/dist/tools/telegram.js.map +1 -0
  231. package/dist/tools/tools.d.ts +48 -0
  232. package/dist/tools/tools.js +328 -0
  233. package/dist/tools/tools.js.map +1 -0
  234. package/dist/tools/web-fetch.d.ts +11 -0
  235. package/dist/tools/web-fetch.js +119 -0
  236. package/dist/tools/web-fetch.js.map +1 -0
  237. package/dist/tools/web-search.d.ts +8 -0
  238. package/dist/tools/web-search.js +81 -0
  239. package/dist/tools/web-search.js.map +1 -0
  240. package/dist/tools/whatsapp.d.ts +10 -0
  241. package/dist/tools/whatsapp.js +222 -0
  242. package/dist/tools/whatsapp.js.map +1 -0
  243. package/dist/tools/write-file.d.ts +1 -0
  244. package/dist/tools/write-file.js +6 -0
  245. package/dist/tools/write-file.js.map +1 -0
  246. package/dist/tools/youtube-transcript.d.ts +9 -0
  247. package/dist/tools/youtube-transcript.js +200 -0
  248. package/dist/tools/youtube-transcript.js.map +1 -0
  249. package/dist/util/circuit-breaker.d.ts +44 -0
  250. package/dist/util/circuit-breaker.js +78 -0
  251. package/dist/util/circuit-breaker.js.map +1 -0
  252. package/dist/util/env.d.ts +18 -0
  253. package/dist/util/env.js +39 -0
  254. package/dist/util/env.js.map +1 -0
  255. package/dist/util/errors.d.ts +42 -0
  256. package/dist/util/errors.js +121 -0
  257. package/dist/util/errors.js.map +1 -0
  258. package/dist/util/rate-limiter.d.ts +71 -0
  259. package/dist/util/rate-limiter.js +141 -0
  260. package/dist/util/rate-limiter.js.map +1 -0
  261. package/dist/util/retry.d.ts +30 -0
  262. package/dist/util/retry.js +57 -0
  263. package/dist/util/retry.js.map +1 -0
  264. package/dist/util/sanitize.d.ts +16 -0
  265. package/dist/util/sanitize.js +255 -0
  266. package/dist/util/sanitize.js.map +1 -0
  267. package/dist/verify/checks.d.ts +21 -0
  268. package/dist/verify/checks.js +311 -0
  269. package/dist/verify/checks.js.map +1 -0
  270. package/dist/verify/index.d.ts +33 -0
  271. package/dist/verify/index.js +127 -0
  272. package/dist/verify/index.js.map +1 -0
  273. package/dist/verify/types.d.ts +20 -0
  274. package/dist/verify/types.js +3 -0
  275. package/dist/verify/types.js.map +1 -0
  276. package/dist/versioncheck.d.ts +22 -0
  277. package/dist/versioncheck.js +203 -0
  278. package/dist/versioncheck.js.map +1 -0
  279. package/dist/viz/index.d.ts +2 -0
  280. package/dist/viz/index.js +733 -0
  281. package/dist/viz/index.js.map +1 -0
  282. package/dist/workflows/engine.d.ts +46 -0
  283. package/dist/workflows/engine.js +320 -0
  284. package/dist/workflows/engine.js.map +1 -0
  285. package/dist/workflows/types.d.ts +92 -0
  286. package/dist/workflows/types.js +6 -0
  287. package/dist/workflows/types.js.map +1 -0
  288. package/package.json +86 -0
@@ -0,0 +1,40 @@
1
+ import type { LLMProvider } from '../providers/types.js';
2
+ import type { ProjectContext } from '../agent/context.js';
3
+ import type { Display } from '../cli/display.js';
4
+ import type { AlternationDecision, Episode, RubyConfig } from './types.js';
5
+ import type { EpisodeStats } from './episode-capture.js';
6
+ /** Configuration for a {@link RubyAlternator} instance. */
7
+ export interface AlternatorOptions {
8
+ rubyConfig: RubyConfig;
9
+ largeModelProvider: LLMProvider;
10
+ projectRoot: string;
11
+ context: ProjectContext;
12
+ /** When set, routing and loop events are surfaced to the user. */
13
+ display?: Display;
14
+ }
15
+ export interface AlternatorRunResult {
16
+ result: string;
17
+ episode: Episode;
18
+ usedRuby: boolean;
19
+ decision: AlternationDecision;
20
+ }
21
+ /**
22
+ * Routes tasks between the small Ruby model (Ollama) and a large model based on
23
+ * learned competence, capturing every alternation as an {@link Episode}.
24
+ */
25
+ export declare class RubyAlternator {
26
+ private readonly opts;
27
+ private readonly display;
28
+ private readonly permissions;
29
+ constructor(opts: AlternatorOptions);
30
+ /**
31
+ * Runs a task through Ruby and/or the large model, persists an episode, and
32
+ * returns the final output. Never throws — failures escalate to the large model.
33
+ */
34
+ run(task: string): Promise<AlternatorRunResult>;
35
+ /**
36
+ * Returns aggregate episode statistics for this alternator's project.
37
+ * Never throws.
38
+ */
39
+ getStats(): Promise<EpisodeStats>;
40
+ }
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RubyAlternator = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const openai_compatible_js_1 = require("../providers/openai-compatible.js");
6
+ const loop_js_1 = require("../agent/loop.js");
7
+ const permissions_js_1 = require("../safety/permissions.js");
8
+ const competence_js_1 = require("./competence.js");
9
+ const episode_capture_js_1 = require("./episode-capture.js");
10
+ const RECENT_EPISODE_LIMIT = 50;
11
+ const OLLAMA_PING_MS = 3_000;
12
+ // ─────────────────────────────────────────────────────────────────────────────
13
+ // Display noop
14
+ // ─────────────────────────────────────────────────────────────────────────────
15
+ function createNoopDisplay() {
16
+ return {
17
+ agentThinking: () => { },
18
+ streamText: () => { },
19
+ streamEnd: () => { },
20
+ toolStart: () => { },
21
+ toolCall: () => { },
22
+ toolResult: () => { },
23
+ toolBlocked: () => { },
24
+ warning: () => { },
25
+ success: () => { },
26
+ error: () => { },
27
+ header: () => { },
28
+ summary: () => { },
29
+ showPlan: () => { },
30
+ stepStarted: () => { },
31
+ stepCompleted: () => { },
32
+ };
33
+ }
34
+ // ─────────────────────────────────────────────────────────────────────────────
35
+ // Helpers
36
+ // ─────────────────────────────────────────────────────────────────────────────
37
+ function inferTaskCategory(task) {
38
+ const t = task.toLowerCase();
39
+ if (/\b(review|audit|lint|check)\b/.test(t))
40
+ return 'review';
41
+ if (/\b(research|explore|find|investigate|understand)\b/.test(t))
42
+ return 'research';
43
+ if (/\b(refactor|restructure|rename|migrate)\b/.test(t))
44
+ return 'refactor';
45
+ if (/\b(implement|fix|add|write|create|build|update)\b/.test(t))
46
+ return 'implementation';
47
+ return 'other';
48
+ }
49
+ function isNonEmptyResult(text) {
50
+ return typeof text === 'string' && text.trim().length > 0;
51
+ }
52
+ /**
53
+ * Checks whether the Ollama OpenAI-compatible endpoint responds.
54
+ * Never throws.
55
+ */
56
+ async function isOllamaAvailable(baseUrl) {
57
+ try {
58
+ const root = baseUrl.replace(/\/v1\/?$/, '').replace(/\/$/, '');
59
+ const url = `${root}/v1/models`;
60
+ const controller = new AbortController();
61
+ const timer = setTimeout(() => controller.abort(), OLLAMA_PING_MS);
62
+ const res = await fetch(url, {
63
+ method: 'GET',
64
+ headers: { Authorization: 'Bearer ollama' },
65
+ signal: controller.signal,
66
+ });
67
+ clearTimeout(timer);
68
+ return res.ok;
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ function buildRubyProvider(config) {
75
+ return new openai_compatible_js_1.OpenAICompatibleProvider({
76
+ model: config.modelName,
77
+ baseUrl: config.ollamaBaseUrl,
78
+ apiKey: 'ollama',
79
+ }, 'Ruby (Ollama)');
80
+ }
81
+ // ─────────────────────────────────────────────────────────────────────────────
82
+ // RubyAlternator
83
+ // ─────────────────────────────────────────────────────────────────────────────
84
+ /**
85
+ * Routes tasks between the small Ruby model (Ollama) and a large model based on
86
+ * learned competence, capturing every alternation as an {@link Episode}.
87
+ */
88
+ class RubyAlternator {
89
+ opts;
90
+ display;
91
+ permissions = new permissions_js_1.PermissionSystem('auto');
92
+ constructor(opts) {
93
+ this.opts = opts;
94
+ this.display = opts.display ?? createNoopDisplay();
95
+ }
96
+ /**
97
+ * Runs a task through Ruby and/or the large model, persists an episode, and
98
+ * returns the final output. Never throws — failures escalate to the large model.
99
+ */
100
+ async run(task) {
101
+ const startMs = Date.now();
102
+ const { rubyConfig, largeModelProvider, projectRoot, context } = this.opts;
103
+ let decision = {
104
+ useRuby: false,
105
+ reason: 'Initializing alternation.',
106
+ confidence: 0,
107
+ fallbackModel: largeModelProvider.model,
108
+ };
109
+ let rubyAttempted = false;
110
+ let rubySucceeded = false;
111
+ let rubyOutput;
112
+ let rubyTokens = 0;
113
+ let largeModelOutput;
114
+ let largeModelTokens = 0;
115
+ let usedRuby = false;
116
+ let result = '';
117
+ try {
118
+ const recent = await episode_capture_js_1.episodeStore.loadEpisodes(projectRoot, RECENT_EPISODE_LIMIT);
119
+ decision = (0, competence_js_1.assessCompetence)(recent, task, rubyConfig);
120
+ decision.fallbackModel = largeModelProvider.model;
121
+ this.display.header('Ruby Principle', decision.reason);
122
+ if (decision.useRuby && rubyConfig.enabled) {
123
+ const available = await isOllamaAvailable(rubyConfig.ollamaBaseUrl);
124
+ if (!available) {
125
+ this.display.warning('Ruby (Ollama) is not reachable — escalating to large model.');
126
+ }
127
+ else {
128
+ rubyAttempted = true;
129
+ this.display.success(`Trying Ruby (${rubyConfig.modelName})…`);
130
+ try {
131
+ const rubyProvider = buildRubyProvider(rubyConfig);
132
+ const loopResult = await (0, loop_js_1.runAgentLoop)({
133
+ provider: rubyProvider,
134
+ task,
135
+ context,
136
+ permissions: this.permissions,
137
+ display: this.display,
138
+ disableSpawn: true,
139
+ maxTurns: 15,
140
+ });
141
+ rubyTokens = loopResult.usage.totalTokens;
142
+ rubyOutput = loopResult.summary;
143
+ if (isNonEmptyResult(rubyOutput) && loopResult.success) {
144
+ rubySucceeded = true;
145
+ usedRuby = true;
146
+ result = rubyOutput;
147
+ this.display.success('Ruby handled the task without escalation.');
148
+ }
149
+ else {
150
+ this.display.warning('Ruby did not produce a usable result — escalating.');
151
+ }
152
+ }
153
+ catch (e) {
154
+ this.display.warning(`Ruby error: ${String(e)} — escalating.`);
155
+ rubyOutput = rubyOutput ?? `Error: ${String(e)}`;
156
+ }
157
+ }
158
+ }
159
+ if (!usedRuby) {
160
+ this.display.header('Large model', largeModelProvider.name);
161
+ try {
162
+ const loopResult = await (0, loop_js_1.runAgentLoop)({
163
+ provider: largeModelProvider,
164
+ task,
165
+ context,
166
+ permissions: this.permissions,
167
+ display: this.display,
168
+ disableSpawn: true,
169
+ });
170
+ largeModelTokens = loopResult.usage.totalTokens;
171
+ largeModelOutput = loopResult.summary;
172
+ result = isNonEmptyResult(largeModelOutput)
173
+ ? largeModelOutput
174
+ : loopResult.success
175
+ ? '(Task completed with no output)'
176
+ : `Large model did not complete: ${loopResult.summary}`;
177
+ }
178
+ catch (e) {
179
+ result = `Large model error: ${String(e)}`;
180
+ largeModelOutput = result;
181
+ this.display.error(result);
182
+ }
183
+ }
184
+ }
185
+ catch (e) {
186
+ result = `Alternation error: ${String(e)}`;
187
+ this.display.error(result);
188
+ }
189
+ const episode = {
190
+ id: (0, crypto_1.randomUUID)(),
191
+ timestamp: Date.now(),
192
+ task,
193
+ projectRoot,
194
+ rubyAttempted,
195
+ rubySucceeded,
196
+ rubyOutput,
197
+ largeModelUsed: usedRuby ? undefined : largeModelProvider.model,
198
+ largeModelOutput: usedRuby ? undefined : largeModelOutput,
199
+ reviewerApproved: isNonEmptyResult(result),
200
+ tokensUsed: {
201
+ ruby: rubyAttempted ? rubyTokens : undefined,
202
+ largeModel: usedRuby ? undefined : largeModelTokens,
203
+ },
204
+ durationMs: Date.now() - startMs,
205
+ taskCategory: inferTaskCategory(task),
206
+ };
207
+ try {
208
+ await episode_capture_js_1.episodeStore.saveEpisode(projectRoot, episode);
209
+ }
210
+ catch (e) {
211
+ this.display.warning(`Failed to save episode: ${String(e)}`);
212
+ }
213
+ try {
214
+ const all = await episode_capture_js_1.episodeStore.loadEpisodes(projectRoot);
215
+ if ((0, competence_js_1.shouldFineTune)(all)) {
216
+ this.display.warning('Ruby Principle: enough failures accumulated — project is ready for fine-tuning.');
217
+ }
218
+ }
219
+ catch {
220
+ /* best-effort */
221
+ }
222
+ return { result, episode, usedRuby, decision };
223
+ }
224
+ /**
225
+ * Returns aggregate episode statistics for this alternator's project.
226
+ * Never throws.
227
+ */
228
+ async getStats() {
229
+ return episode_capture_js_1.episodeStore.getEpisodeStats(this.opts.projectRoot);
230
+ }
231
+ }
232
+ exports.RubyAlternator = RubyAlternator;
233
+ //# sourceMappingURL=alternator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alternator.js","sourceRoot":"","sources":["../../src/ruby/alternator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { AlternationDecision, CompetenceLevel, Episode, RubyConfig } from './types.js';
2
+ /** Large model used when Ruby is not trusted or is disabled. */
3
+ export declare const DEFAULT_FALLBACK_MODEL = "claude-sonnet-4-5-20251001";
4
+ /** Default failure count before `shouldFineTune` returns true. */
5
+ export declare const DEFAULT_MIN_FAILURES = 20;
6
+ /**
7
+ * Decides whether the current task should be routed to Ruby or escalated to
8
+ * the large model, based on historical episodes and configuration thresholds.
9
+ *
10
+ * Never throws — returns a conservative `AlternationDecision` on bad input.
11
+ */
12
+ export declare function assessCompetence(episodes: Episode[], task: string, config: RubyConfig): AlternationDecision;
13
+ /**
14
+ * Merges one completed episode into the competence map.
15
+ * Never throws — returns the previous list unchanged on invalid input.
16
+ */
17
+ export declare function updateCompetence(existing: CompetenceLevel[], episode: Episode): CompetenceLevel[];
18
+ /**
19
+ * Summarises Ruby performance grouped by `taskCategory`.
20
+ * Never throws — returns an empty array on invalid input.
21
+ */
22
+ export declare function getCompetenceReport(episodes: Episode[]): {
23
+ category: string;
24
+ successRate: number;
25
+ count: number;
26
+ }[];
27
+ /**
28
+ * Returns true when enough Ruby failures have accumulated to justify a
29
+ * fine-tune pass. A failure is an episode where Ruby was attempted but did
30
+ * not succeed (large model intervened or reviewer rejected).
31
+ *
32
+ * Never throws — returns false on invalid input.
33
+ */
34
+ export declare function shouldFineTune(episodes: Episode[], minFailures?: number): boolean;
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MIN_FAILURES = exports.DEFAULT_FALLBACK_MODEL = void 0;
4
+ exports.assessCompetence = assessCompetence;
5
+ exports.updateCompetence = updateCompetence;
6
+ exports.getCompetenceReport = getCompetenceReport;
7
+ exports.shouldFineTune = shouldFineTune;
8
+ const types_js_1 = require("./types.js");
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ // Constants
11
+ // ─────────────────────────────────────────────────────────────────────────────
12
+ /** Large model used when Ruby is not trusted or is disabled. */
13
+ exports.DEFAULT_FALLBACK_MODEL = 'claude-sonnet-4-5-20251001';
14
+ /** Default failure count before `shouldFineTune` returns true. */
15
+ exports.DEFAULT_MIN_FAILURES = 20;
16
+ /** Maximum exemplars kept per competence pattern. */
17
+ const MAX_EXAMPLES = 10;
18
+ /** Minimum token-overlap ratio to treat two tasks as similar. */
19
+ const SIMILARITY_THRESHOLD = 0.35;
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+ // Internal helpers (pure, no I/O)
22
+ // ─────────────────────────────────────────────────────────────────────────────
23
+ function clamp01(n) {
24
+ if (!Number.isFinite(n))
25
+ return 0;
26
+ return Math.max(0, Math.min(1, n));
27
+ }
28
+ function safeConfig(config) {
29
+ return {
30
+ modelName: config?.modelName ?? types_js_1.DEFAULT_RUBY_CONFIG.modelName,
31
+ ollamaBaseUrl: config?.ollamaBaseUrl ?? types_js_1.DEFAULT_RUBY_CONFIG.ollamaBaseUrl,
32
+ competenceThreshold: clamp01(config?.competenceThreshold ?? types_js_1.DEFAULT_RUBY_CONFIG.competenceThreshold),
33
+ minAttempts: Math.max(0, config?.minAttempts ?? types_js_1.DEFAULT_RUBY_CONFIG.minAttempts),
34
+ enabled: config?.enabled ?? types_js_1.DEFAULT_RUBY_CONFIG.enabled,
35
+ };
36
+ }
37
+ function tokenize(text) {
38
+ if (typeof text !== 'string')
39
+ return [];
40
+ return text
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9\s]/g, ' ')
43
+ .split(/\s+/)
44
+ .filter(t => t.length > 2);
45
+ }
46
+ function similarity(a, b) {
47
+ const ta = new Set(tokenize(a));
48
+ const tb = new Set(tokenize(b));
49
+ if (ta.size === 0 || tb.size === 0)
50
+ return 0;
51
+ let intersection = 0;
52
+ for (const t of ta) {
53
+ if (tb.has(t))
54
+ intersection++;
55
+ }
56
+ const union = ta.size + tb.size - intersection;
57
+ return union === 0 ? 0 : intersection / union;
58
+ }
59
+ function tasksSimilar(task, other) {
60
+ const na = task.trim().toLowerCase();
61
+ const nb = other.trim().toLowerCase();
62
+ if (!na || !nb)
63
+ return false;
64
+ if (na === nb)
65
+ return true;
66
+ if (na.includes(nb) || nb.includes(na))
67
+ return true;
68
+ return similarity(na, nb) >= SIMILARITY_THRESHOLD;
69
+ }
70
+ /**
71
+ * Derives a stable pattern key from a task string and optional category.
72
+ */
73
+ function taskPatternFrom(task, category) {
74
+ const tokens = tokenize(task).slice(0, 8);
75
+ const slug = tokens.join('_') || 'generic';
76
+ const cat = category ?? 'other';
77
+ return `${cat}:${slug}`;
78
+ }
79
+ function safeEpisodes(episodes) {
80
+ if (!Array.isArray(episodes))
81
+ return [];
82
+ return episodes.filter(e => e && typeof e.task === 'string' && typeof e.rubyAttempted === 'boolean');
83
+ }
84
+ function buildLevelFromEpisodes(pattern, matched) {
85
+ const attempted = matched.filter(e => e.rubyAttempted);
86
+ const successes = attempted.filter(e => e.rubySucceeded);
87
+ const attemptCount = attempted.length;
88
+ const successRate = attemptCount === 0 ? 0 : successes.length / attemptCount;
89
+ const examples = attempted
90
+ .slice(-MAX_EXAMPLES)
91
+ .map(e => ({ task: e.task, succeeded: e.rubySucceeded }));
92
+ const lastUpdated = attempted.reduce((max, e) => Math.max(max, e.timestamp ?? 0), 0);
93
+ return {
94
+ taskPattern: pattern,
95
+ successRate: clamp01(successRate),
96
+ attemptCount,
97
+ lastUpdated: lastUpdated || Date.now(),
98
+ examples,
99
+ };
100
+ }
101
+ function safeDecision(partial) {
102
+ return {
103
+ useRuby: partial.useRuby ?? true,
104
+ reason: partial.reason ?? 'Defaulting to Ruby (safe fallback).',
105
+ confidence: clamp01(partial.confidence ?? 0),
106
+ competenceLevel: partial.competenceLevel,
107
+ fallbackModel: partial.fallbackModel ?? exports.DEFAULT_FALLBACK_MODEL,
108
+ };
109
+ }
110
+ // ─────────────────────────────────────────────────────────────────────────────
111
+ // assessCompetence
112
+ // ─────────────────────────────────────────────────────────────────────────────
113
+ /**
114
+ * Decides whether the current task should be routed to Ruby or escalated to
115
+ * the large model, based on historical episodes and configuration thresholds.
116
+ *
117
+ * Never throws — returns a conservative `AlternationDecision` on bad input.
118
+ */
119
+ function assessCompetence(episodes, task, config) {
120
+ try {
121
+ const cfg = safeConfig(config);
122
+ const fallbackModel = exports.DEFAULT_FALLBACK_MODEL;
123
+ if (!cfg.enabled) {
124
+ return safeDecision({
125
+ useRuby: false,
126
+ reason: 'Ruby alternation is disabled in configuration.',
127
+ confidence: 1,
128
+ fallbackModel,
129
+ });
130
+ }
131
+ const safeTask = typeof task === 'string' ? task : '';
132
+ const list = safeEpisodes(episodes);
133
+ const matched = list.filter(e => tasksSimilar(safeTask, e.task));
134
+ const pattern = taskPatternFrom(safeTask, matched[0]?.taskCategory ?? 'other');
135
+ const level = buildLevelFromEpisodes(pattern, matched);
136
+ if (level.attemptCount < cfg.minAttempts) {
137
+ return safeDecision({
138
+ useRuby: true,
139
+ reason: `Only ${level.attemptCount} prior attempt(s) for this pattern (minimum ${cfg.minAttempts}) — giving Ruby a chance to learn.`,
140
+ confidence: clamp01(0.3 + level.attemptCount / Math.max(cfg.minAttempts, 1) * 0.3),
141
+ competenceLevel: level,
142
+ fallbackModel,
143
+ });
144
+ }
145
+ if (level.successRate >= cfg.competenceThreshold) {
146
+ return safeDecision({
147
+ useRuby: true,
148
+ reason: `Ruby success rate ${(level.successRate * 100).toFixed(0)}% meets threshold ${(cfg.competenceThreshold * 100).toFixed(0)}% for pattern "${pattern}".`,
149
+ confidence: clamp01(level.successRate * Math.min(1, level.attemptCount / 10)),
150
+ competenceLevel: level,
151
+ fallbackModel,
152
+ });
153
+ }
154
+ return safeDecision({
155
+ useRuby: false,
156
+ reason: `Ruby success rate ${(level.successRate * 100).toFixed(0)}% below threshold ${(cfg.competenceThreshold * 100).toFixed(0)}% after ${level.attemptCount} attempt(s) — escalating to large model.`,
157
+ confidence: clamp01((1 - level.successRate) * Math.min(1, level.attemptCount / 10)),
158
+ competenceLevel: level,
159
+ fallbackModel,
160
+ });
161
+ }
162
+ catch {
163
+ return safeDecision({
164
+ useRuby: true,
165
+ reason: 'Assessment error — defaulting to Ruby attempt.',
166
+ confidence: 0,
167
+ });
168
+ }
169
+ }
170
+ // ─────────────────────────────────────────────────────────────────────────────
171
+ // updateCompetence
172
+ // ─────────────────────────────────────────────────────────────────────────────
173
+ /**
174
+ * Merges one completed episode into the competence map.
175
+ * Never throws — returns the previous list unchanged on invalid input.
176
+ */
177
+ function updateCompetence(existing, episode) {
178
+ try {
179
+ if (!episode?.rubyAttempted)
180
+ return Array.isArray(existing) ? [...existing] : [];
181
+ const list = Array.isArray(existing) ? [...existing] : [];
182
+ const pattern = taskPatternFrom(episode.task ?? '', episode.taskCategory);
183
+ const idx = list.findIndex(l => l.taskPattern === pattern);
184
+ const prev = idx >= 0 ? list[idx] : undefined;
185
+ const examples = [
186
+ ...(prev?.examples ?? []),
187
+ { task: episode.task, succeeded: episode.rubySucceeded },
188
+ ].slice(-MAX_EXAMPLES);
189
+ const attemptCount = (prev?.attemptCount ?? 0) + 1;
190
+ const prevSuccesses = Math.round((prev?.successRate ?? 0) * (prev?.attemptCount ?? 0));
191
+ const newSuccesses = prevSuccesses + (episode.rubySucceeded ? 1 : 0);
192
+ const successRate = attemptCount === 0 ? 0 : newSuccesses / attemptCount;
193
+ const updated = {
194
+ taskPattern: pattern,
195
+ successRate: clamp01(successRate),
196
+ attemptCount,
197
+ lastUpdated: episode.timestamp ?? Date.now(),
198
+ examples,
199
+ };
200
+ if (idx >= 0) {
201
+ list[idx] = updated;
202
+ return list;
203
+ }
204
+ return [...list, updated];
205
+ }
206
+ catch {
207
+ return Array.isArray(existing) ? [...existing] : [];
208
+ }
209
+ }
210
+ // ─────────────────────────────────────────────────────────────────────────────
211
+ // getCompetenceReport
212
+ // ─────────────────────────────────────────────────────────────────────────────
213
+ /**
214
+ * Summarises Ruby performance grouped by `taskCategory`.
215
+ * Never throws — returns an empty array on invalid input.
216
+ */
217
+ function getCompetenceReport(episodes) {
218
+ try {
219
+ const list = safeEpisodes(episodes);
220
+ const buckets = new Map();
221
+ for (const ep of list) {
222
+ if (!ep.rubyAttempted)
223
+ continue;
224
+ const cat = ep.taskCategory ?? 'other';
225
+ const b = buckets.get(cat) ?? { successes: 0, count: 0 };
226
+ b.count += 1;
227
+ if (ep.rubySucceeded)
228
+ b.successes += 1;
229
+ buckets.set(cat, b);
230
+ }
231
+ return Array.from(buckets.entries())
232
+ .map(([category, { successes, count }]) => ({
233
+ category,
234
+ successRate: clamp01(count === 0 ? 0 : successes / count),
235
+ count,
236
+ }))
237
+ .sort((a, b) => b.count - a.count);
238
+ }
239
+ catch {
240
+ return [];
241
+ }
242
+ }
243
+ // ─────────────────────────────────────────────────────────────────────────────
244
+ // shouldFineTune
245
+ // ─────────────────────────────────────────────────────────────────────────────
246
+ /**
247
+ * Returns true when enough Ruby failures have accumulated to justify a
248
+ * fine-tune pass. A failure is an episode where Ruby was attempted but did
249
+ * not succeed (large model intervened or reviewer rejected).
250
+ *
251
+ * Never throws — returns false on invalid input.
252
+ */
253
+ function shouldFineTune(episodes, minFailures = exports.DEFAULT_MIN_FAILURES) {
254
+ try {
255
+ const threshold = Math.max(1, minFailures ?? exports.DEFAULT_MIN_FAILURES);
256
+ const list = safeEpisodes(episodes);
257
+ const failures = list.filter(e => e.rubyAttempted && !e.rubySucceeded).length;
258
+ return failures >= threshold;
259
+ }
260
+ catch {
261
+ return false;
262
+ }
263
+ }
264
+ //# sourceMappingURL=competence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"competence.js","sourceRoot":"","sources":["../../src/ruby/competence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ import type { Episode } from './types.js';
2
+ /**
3
+ * Rough character-based token estimation (≈1 token per 4 characters).
4
+ * Used only for episode recording when the provider does not report usage
5
+ * (e.g. MiMo streaming, Google Gemini streaming).
6
+ */
7
+ export declare function estimateTokens(text: string | undefined): number;
8
+ /** Stats returned by {@link getEpisodeStats}. */
9
+ export interface EpisodeStats {
10
+ total: number;
11
+ rubySuccesses: number;
12
+ rubyFailures: number;
13
+ largeModelInterventions: number;
14
+ readyForFineTune: boolean;
15
+ }
16
+ /**
17
+ * On-disk episode persistence for the Ruby Principle.
18
+ * Mirrors {@link sessionStore} — atomic `.tmp` writes, namespaced by project hash.
19
+ */
20
+ export declare const episodeStore: {
21
+ defaultDir(): string;
22
+ /**
23
+ * First 8 characters of the base64 encoding of `projectRoot`.
24
+ */
25
+ projectHash(projectRoot: string): string;
26
+ /** Directory containing all episodes for a project. */
27
+ projectDir(projectRoot: string): string;
28
+ /** Full path to one episode file. */
29
+ episodePath(projectRoot: string, id: string): string;
30
+ /**
31
+ * Persists one episode to `~/.aura/episodes/{projectHash}/{id}.json`.
32
+ *
33
+ * When the provider did not report token usage (totalTokens === 0), fills in
34
+ * a character-based estimate from the episode's output text so that fine-tune
35
+ * readiness and competence tracking still have meaningful token counts.
36
+ */
37
+ saveEpisode(projectRoot: string, episode: Episode): Promise<void>;
38
+ /**
39
+ * Loads episodes for a project, newest first.
40
+ * Never throws — returns `[]` on missing dir or parse errors.
41
+ */
42
+ loadEpisodes(projectRoot: string, limit?: number): Promise<Episode[]>;
43
+ /**
44
+ * Removes one episode file. Safe when the file does not exist.
45
+ */
46
+ deleteEpisode(projectRoot: string, id: string): Promise<void>;
47
+ /**
48
+ * Aggregates episode counters and fine-tune readiness for a project.
49
+ * Never throws — returns zeroed stats on error.
50
+ */
51
+ getEpisodeStats(projectRoot: string): Promise<EpisodeStats>;
52
+ };
53
+ export declare const saveEpisode: (projectRoot: string, episode: Episode) => Promise<void>;
54
+ export declare const loadEpisodes: (projectRoot: string, limit?: number) => Promise<Episode[]>;
55
+ export declare const deleteEpisode: (projectRoot: string, id: string) => Promise<void>;
56
+ export declare const getEpisodeStats: (projectRoot: string) => Promise<EpisodeStats>;