@vibe-lang/runtime 0.2.5

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 (250) hide show
  1. package/package.json +46 -0
  2. package/src/ast/index.ts +375 -0
  3. package/src/ast.ts +2 -0
  4. package/src/debug/advanced-features.ts +482 -0
  5. package/src/debug/bun-inspector.ts +424 -0
  6. package/src/debug/handoff-manager.ts +283 -0
  7. package/src/debug/index.ts +150 -0
  8. package/src/debug/runner.ts +365 -0
  9. package/src/debug/server.ts +565 -0
  10. package/src/debug/stack-merger.ts +267 -0
  11. package/src/debug/state.ts +581 -0
  12. package/src/debug/test/advanced-features.test.ts +300 -0
  13. package/src/debug/test/e2e.test.ts +218 -0
  14. package/src/debug/test/handoff-manager.test.ts +256 -0
  15. package/src/debug/test/runner.test.ts +256 -0
  16. package/src/debug/test/stack-merger.test.ts +163 -0
  17. package/src/debug/test/state.test.ts +400 -0
  18. package/src/debug/test/ts-debug-integration.test.ts +374 -0
  19. package/src/debug/test/ts-import-tracker.test.ts +125 -0
  20. package/src/debug/test/ts-source-map.test.ts +169 -0
  21. package/src/debug/ts-import-tracker.ts +151 -0
  22. package/src/debug/ts-source-map.ts +171 -0
  23. package/src/errors/index.ts +124 -0
  24. package/src/index.ts +358 -0
  25. package/src/lexer/index.ts +348 -0
  26. package/src/lexer.ts +2 -0
  27. package/src/parser/index.ts +792 -0
  28. package/src/parser/parse.ts +45 -0
  29. package/src/parser/test/async.test.ts +248 -0
  30. package/src/parser/test/destructuring.test.ts +167 -0
  31. package/src/parser/test/do-expression.test.ts +486 -0
  32. package/src/parser/test/errors/do-expression.test.ts +95 -0
  33. package/src/parser/test/errors/error-locations.test.ts +230 -0
  34. package/src/parser/test/errors/invalid-expressions.test.ts +144 -0
  35. package/src/parser/test/errors/missing-tokens.test.ts +126 -0
  36. package/src/parser/test/errors/model-declaration.test.ts +185 -0
  37. package/src/parser/test/errors/nested-blocks.test.ts +226 -0
  38. package/src/parser/test/errors/unclosed-delimiters.test.ts +122 -0
  39. package/src/parser/test/errors/unexpected-tokens.test.ts +120 -0
  40. package/src/parser/test/import-export.test.ts +143 -0
  41. package/src/parser/test/literals.test.ts +404 -0
  42. package/src/parser/test/model-declaration.test.ts +161 -0
  43. package/src/parser/test/nested-blocks.test.ts +402 -0
  44. package/src/parser/test/parser.test.ts +743 -0
  45. package/src/parser/test/private.test.ts +136 -0
  46. package/src/parser/test/template-literal.test.ts +127 -0
  47. package/src/parser/test/tool-declaration.test.ts +302 -0
  48. package/src/parser/test/ts-block.test.ts +252 -0
  49. package/src/parser/test/type-annotations.test.ts +254 -0
  50. package/src/parser/visitor/helpers.ts +330 -0
  51. package/src/parser/visitor.ts +794 -0
  52. package/src/parser.ts +2 -0
  53. package/src/runtime/ai/cache-chunking.test.ts +69 -0
  54. package/src/runtime/ai/cache-chunking.ts +73 -0
  55. package/src/runtime/ai/client.ts +109 -0
  56. package/src/runtime/ai/context.ts +168 -0
  57. package/src/runtime/ai/formatters.ts +316 -0
  58. package/src/runtime/ai/index.ts +38 -0
  59. package/src/runtime/ai/language-ref.ts +38 -0
  60. package/src/runtime/ai/providers/anthropic.ts +253 -0
  61. package/src/runtime/ai/providers/google.ts +201 -0
  62. package/src/runtime/ai/providers/openai.ts +156 -0
  63. package/src/runtime/ai/retry.ts +100 -0
  64. package/src/runtime/ai/return-tools.ts +301 -0
  65. package/src/runtime/ai/test/client.test.ts +83 -0
  66. package/src/runtime/ai/test/formatters.test.ts +485 -0
  67. package/src/runtime/ai/test/retry.test.ts +137 -0
  68. package/src/runtime/ai/test/return-tools.test.ts +450 -0
  69. package/src/runtime/ai/test/tool-loop.test.ts +319 -0
  70. package/src/runtime/ai/test/tool-schema.test.ts +241 -0
  71. package/src/runtime/ai/tool-loop.ts +203 -0
  72. package/src/runtime/ai/tool-schema.ts +151 -0
  73. package/src/runtime/ai/types.ts +113 -0
  74. package/src/runtime/ai-logger.ts +255 -0
  75. package/src/runtime/ai-provider.ts +347 -0
  76. package/src/runtime/async/dependencies.ts +276 -0
  77. package/src/runtime/async/executor.ts +293 -0
  78. package/src/runtime/async/index.ts +43 -0
  79. package/src/runtime/async/scheduling.ts +163 -0
  80. package/src/runtime/async/test/dependencies.test.ts +284 -0
  81. package/src/runtime/async/test/executor.test.ts +388 -0
  82. package/src/runtime/context.ts +357 -0
  83. package/src/runtime/exec/ai.ts +139 -0
  84. package/src/runtime/exec/expressions.ts +475 -0
  85. package/src/runtime/exec/frames.ts +26 -0
  86. package/src/runtime/exec/functions.ts +305 -0
  87. package/src/runtime/exec/interpolation.ts +312 -0
  88. package/src/runtime/exec/statements.ts +604 -0
  89. package/src/runtime/exec/tools.ts +129 -0
  90. package/src/runtime/exec/typescript.ts +215 -0
  91. package/src/runtime/exec/variables.ts +279 -0
  92. package/src/runtime/index.ts +975 -0
  93. package/src/runtime/modules.ts +452 -0
  94. package/src/runtime/serialize.ts +103 -0
  95. package/src/runtime/state.ts +489 -0
  96. package/src/runtime/stdlib/core.ts +45 -0
  97. package/src/runtime/stdlib/directory.test.ts +156 -0
  98. package/src/runtime/stdlib/edit.test.ts +154 -0
  99. package/src/runtime/stdlib/fastEdit.test.ts +201 -0
  100. package/src/runtime/stdlib/glob.test.ts +106 -0
  101. package/src/runtime/stdlib/grep.test.ts +144 -0
  102. package/src/runtime/stdlib/index.ts +16 -0
  103. package/src/runtime/stdlib/readFile.test.ts +123 -0
  104. package/src/runtime/stdlib/tools/index.ts +707 -0
  105. package/src/runtime/stdlib/writeFile.test.ts +157 -0
  106. package/src/runtime/step.ts +969 -0
  107. package/src/runtime/test/ai-context.test.ts +1086 -0
  108. package/src/runtime/test/ai-result-object.test.ts +419 -0
  109. package/src/runtime/test/ai-tool-flow.test.ts +859 -0
  110. package/src/runtime/test/async-execution-order.test.ts +618 -0
  111. package/src/runtime/test/async-execution.test.ts +344 -0
  112. package/src/runtime/test/async-nested.test.ts +660 -0
  113. package/src/runtime/test/async-parallel-timing.test.ts +546 -0
  114. package/src/runtime/test/basic1.test.ts +154 -0
  115. package/src/runtime/test/binary-operators.test.ts +431 -0
  116. package/src/runtime/test/break-statement.test.ts +257 -0
  117. package/src/runtime/test/context-modes.test.ts +650 -0
  118. package/src/runtime/test/context.test.ts +466 -0
  119. package/src/runtime/test/core-functions.test.ts +228 -0
  120. package/src/runtime/test/e2e.test.ts +88 -0
  121. package/src/runtime/test/error-locations/error-locations.test.ts +80 -0
  122. package/src/runtime/test/error-locations/main-error.vibe +4 -0
  123. package/src/runtime/test/error-locations/main-import-error.vibe +3 -0
  124. package/src/runtime/test/error-locations/utils/helper.vibe +5 -0
  125. package/src/runtime/test/for-in.test.ts +312 -0
  126. package/src/runtime/test/helpers.ts +69 -0
  127. package/src/runtime/test/imports.test.ts +334 -0
  128. package/src/runtime/test/json-expressions.test.ts +232 -0
  129. package/src/runtime/test/literals.test.ts +372 -0
  130. package/src/runtime/test/logical-indexing.test.ts +478 -0
  131. package/src/runtime/test/member-methods.test.ts +324 -0
  132. package/src/runtime/test/model-config.test.ts +338 -0
  133. package/src/runtime/test/null-handling.test.ts +342 -0
  134. package/src/runtime/test/private-visibility.test.ts +332 -0
  135. package/src/runtime/test/runtime-state.test.ts +514 -0
  136. package/src/runtime/test/scoping.test.ts +370 -0
  137. package/src/runtime/test/string-interpolation.test.ts +354 -0
  138. package/src/runtime/test/template-literal.test.ts +181 -0
  139. package/src/runtime/test/tool-execution.test.ts +467 -0
  140. package/src/runtime/test/tool-schema-generation.test.ts +477 -0
  141. package/src/runtime/test/tostring.test.ts +210 -0
  142. package/src/runtime/test/ts-block.test.ts +594 -0
  143. package/src/runtime/test/ts-error-location.test.ts +231 -0
  144. package/src/runtime/test/types.test.ts +732 -0
  145. package/src/runtime/test/verbose-logger.test.ts +710 -0
  146. package/src/runtime/test/vibe-expression.test.ts +54 -0
  147. package/src/runtime/test/vibe-value-errors.test.ts +541 -0
  148. package/src/runtime/test/while.test.ts +232 -0
  149. package/src/runtime/tools/builtin.ts +30 -0
  150. package/src/runtime/tools/directory-tools.ts +70 -0
  151. package/src/runtime/tools/file-tools.ts +228 -0
  152. package/src/runtime/tools/index.ts +5 -0
  153. package/src/runtime/tools/registry.ts +48 -0
  154. package/src/runtime/tools/search-tools.ts +134 -0
  155. package/src/runtime/tools/security.ts +36 -0
  156. package/src/runtime/tools/system-tools.ts +312 -0
  157. package/src/runtime/tools/test/fixtures/base-types.ts +40 -0
  158. package/src/runtime/tools/test/fixtures/test-types.ts +132 -0
  159. package/src/runtime/tools/test/registry.test.ts +713 -0
  160. package/src/runtime/tools/test/security.test.ts +86 -0
  161. package/src/runtime/tools/test/system-tools.test.ts +679 -0
  162. package/src/runtime/tools/test/ts-schema.test.ts +357 -0
  163. package/src/runtime/tools/ts-schema.ts +341 -0
  164. package/src/runtime/tools/types.ts +89 -0
  165. package/src/runtime/tools/utility-tools.ts +198 -0
  166. package/src/runtime/ts-eval.ts +126 -0
  167. package/src/runtime/types.ts +797 -0
  168. package/src/runtime/validation.ts +160 -0
  169. package/src/runtime/verbose-logger.ts +459 -0
  170. package/src/runtime.ts +2 -0
  171. package/src/semantic/analyzer-context.ts +62 -0
  172. package/src/semantic/analyzer-validators.ts +575 -0
  173. package/src/semantic/analyzer-visitors.ts +534 -0
  174. package/src/semantic/analyzer.ts +83 -0
  175. package/src/semantic/index.ts +11 -0
  176. package/src/semantic/symbol-table.ts +58 -0
  177. package/src/semantic/test/async-validation.test.ts +301 -0
  178. package/src/semantic/test/compress-validation.test.ts +179 -0
  179. package/src/semantic/test/const-reassignment.test.ts +111 -0
  180. package/src/semantic/test/control-flow.test.ts +346 -0
  181. package/src/semantic/test/destructuring.test.ts +185 -0
  182. package/src/semantic/test/duplicate-declarations.test.ts +168 -0
  183. package/src/semantic/test/export-validation.test.ts +111 -0
  184. package/src/semantic/test/fixtures/math.ts +31 -0
  185. package/src/semantic/test/imports.test.ts +148 -0
  186. package/src/semantic/test/json-type.test.ts +68 -0
  187. package/src/semantic/test/literals.test.ts +127 -0
  188. package/src/semantic/test/model-validation.test.ts +179 -0
  189. package/src/semantic/test/prompt-validation.test.ts +343 -0
  190. package/src/semantic/test/scoping.test.ts +312 -0
  191. package/src/semantic/test/tool-validation.test.ts +306 -0
  192. package/src/semantic/test/ts-type-checking.test.ts +563 -0
  193. package/src/semantic/test/type-constraints.test.ts +111 -0
  194. package/src/semantic/test/type-inference.test.ts +87 -0
  195. package/src/semantic/test/type-validation.test.ts +552 -0
  196. package/src/semantic/test/undefined-variables.test.ts +163 -0
  197. package/src/semantic/ts-block-checker.ts +204 -0
  198. package/src/semantic/ts-signatures.ts +194 -0
  199. package/src/semantic/ts-types.ts +170 -0
  200. package/src/semantic/types.ts +58 -0
  201. package/tests/fixtures/conditional-logic.vibe +14 -0
  202. package/tests/fixtures/function-call.vibe +16 -0
  203. package/tests/fixtures/imports/cycle-detection/a.vibe +6 -0
  204. package/tests/fixtures/imports/cycle-detection/b.vibe +5 -0
  205. package/tests/fixtures/imports/cycle-detection/main.vibe +3 -0
  206. package/tests/fixtures/imports/module-isolation/main-b.vibe +8 -0
  207. package/tests/fixtures/imports/module-isolation/main.vibe +9 -0
  208. package/tests/fixtures/imports/module-isolation/moduleA.vibe +6 -0
  209. package/tests/fixtures/imports/module-isolation/moduleB.vibe +6 -0
  210. package/tests/fixtures/imports/nested-import/helper.vibe +6 -0
  211. package/tests/fixtures/imports/nested-import/main.vibe +3 -0
  212. package/tests/fixtures/imports/nested-import/utils.ts +3 -0
  213. package/tests/fixtures/imports/nested-isolation/file2.vibe +15 -0
  214. package/tests/fixtures/imports/nested-isolation/file3.vibe +10 -0
  215. package/tests/fixtures/imports/nested-isolation/main.vibe +21 -0
  216. package/tests/fixtures/imports/pure-cycle/a.vibe +5 -0
  217. package/tests/fixtures/imports/pure-cycle/b.vibe +5 -0
  218. package/tests/fixtures/imports/pure-cycle/main.vibe +3 -0
  219. package/tests/fixtures/imports/ts-boolean/checks.ts +14 -0
  220. package/tests/fixtures/imports/ts-boolean/main.vibe +10 -0
  221. package/tests/fixtures/imports/ts-boolean/type-mismatch.vibe +5 -0
  222. package/tests/fixtures/imports/ts-boolean/use-constant.vibe +18 -0
  223. package/tests/fixtures/imports/ts-error-handling/helpers.ts +42 -0
  224. package/tests/fixtures/imports/ts-error-handling/main.vibe +5 -0
  225. package/tests/fixtures/imports/ts-import/main.vibe +4 -0
  226. package/tests/fixtures/imports/ts-import/math.ts +9 -0
  227. package/tests/fixtures/imports/ts-variables/call-non-function.vibe +5 -0
  228. package/tests/fixtures/imports/ts-variables/data.ts +10 -0
  229. package/tests/fixtures/imports/ts-variables/import-json.vibe +5 -0
  230. package/tests/fixtures/imports/ts-variables/import-type-mismatch.vibe +5 -0
  231. package/tests/fixtures/imports/ts-variables/import-variable.vibe +5 -0
  232. package/tests/fixtures/imports/vibe-import/greet.vibe +5 -0
  233. package/tests/fixtures/imports/vibe-import/main.vibe +3 -0
  234. package/tests/fixtures/multiple-ai-calls.vibe +10 -0
  235. package/tests/fixtures/simple-greeting.vibe +6 -0
  236. package/tests/fixtures/template-literals.vibe +11 -0
  237. package/tests/integration/basic-ai/basic-ai.integration.test.ts +166 -0
  238. package/tests/integration/basic-ai/basic-ai.vibe +12 -0
  239. package/tests/integration/bug-fix/bug-fix.integration.test.ts +201 -0
  240. package/tests/integration/bug-fix/buggy-code.ts +22 -0
  241. package/tests/integration/bug-fix/fix-bug.vibe +21 -0
  242. package/tests/integration/compress/compress.integration.test.ts +206 -0
  243. package/tests/integration/destructuring/destructuring.integration.test.ts +92 -0
  244. package/tests/integration/hello-world-translator/hello-world-translator.integration.test.ts +61 -0
  245. package/tests/integration/line-annotator/context-modes.integration.test.ts +261 -0
  246. package/tests/integration/line-annotator/line-annotator.integration.test.ts +148 -0
  247. package/tests/integration/multi-feature/cumulative-sum.integration.test.ts +75 -0
  248. package/tests/integration/multi-feature/number-analyzer.integration.test.ts +191 -0
  249. package/tests/integration/multi-feature/number-analyzer.vibe +59 -0
  250. package/tests/integration/tool-calls/tool-calls.integration.test.ts +93 -0
@@ -0,0 +1,166 @@
1
+ // Basic AI Integration Tests
2
+ // Tests real API calls with OpenAI, Anthropic, and Google for all return types
3
+
4
+ import { describe, test, expect } from 'bun:test';
5
+ import { Runtime, formatAIInteractions } from '../../../src/runtime';
6
+ import { createRealAIProvider } from '../../../src/runtime/ai-provider';
7
+ import { parse } from '../../../src/parser/parse';
8
+
9
+ // API Keys from environment
10
+ const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
11
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
12
+ const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
13
+
14
+ // Provider configurations
15
+ const providers = [
16
+ {
17
+ name: 'OpenAI',
18
+ hasKey: !!OPENAI_API_KEY,
19
+ modelConfig: `
20
+ model testModel = {
21
+ name: "gpt-5-mini",
22
+ apiKey: "${OPENAI_API_KEY}",
23
+ url: "https://api.openai.com/v1",
24
+ provider: "openai"
25
+ }
26
+ `,
27
+ },
28
+ {
29
+ name: 'Anthropic',
30
+ hasKey: !!ANTHROPIC_API_KEY,
31
+ modelConfig: `
32
+ model testModel = {
33
+ name: "claude-haiku-4-5",
34
+ apiKey: "${ANTHROPIC_API_KEY}",
35
+ url: "https://api.anthropic.com",
36
+ provider: "anthropic"
37
+ }
38
+ `,
39
+ },
40
+ {
41
+ name: 'Google',
42
+ hasKey: !!GOOGLE_API_KEY,
43
+ modelConfig: `
44
+ model testModel = {
45
+ name: "gemini-3-flash-preview",
46
+ apiKey: "${GOOGLE_API_KEY}",
47
+ provider: "google"
48
+ }
49
+ `,
50
+ },
51
+ ];
52
+
53
+ // Shared Vibe code for each test case
54
+ const testCases = [
55
+ {
56
+ name: 'returns text response',
57
+ vibeCode: `let result: text = vibe "Reply with exactly: PONG" testModel default`,
58
+ assert: (runtime: Runtime) => {
59
+ const result = runtime.getValue('result') as string;
60
+ expect(typeof result).toBe('string');
61
+ expect(result.toUpperCase()).toContain('PONG');
62
+ },
63
+ },
64
+ {
65
+ name: 'returns number response',
66
+ vibeCode: `let result: number = vibe "What is 2 + 2? Reply with just the number." testModel default`,
67
+ assert: (runtime: Runtime) => {
68
+ const result = runtime.getValue('result');
69
+ expect(typeof result).toBe('number');
70
+ expect(result).toBe(4);
71
+ },
72
+ },
73
+ {
74
+ name: 'returns boolean response',
75
+ vibeCode: `let result: boolean = vibe "Is 5 greater than 3? Reply with true or false only." testModel default`,
76
+ assert: (runtime: Runtime) => {
77
+ const result = runtime.getValue('result');
78
+ expect(typeof result).toBe('boolean');
79
+ expect(result).toBe(true);
80
+ },
81
+ },
82
+ {
83
+ name: 'returns json response',
84
+ vibeCode: `let result: json = vibe "Return a JSON object with name set to Alice and age set to 30" testModel default`,
85
+ assert: (runtime: Runtime) => {
86
+ const result = runtime.getValue('result') as Record<string, unknown>;
87
+ expect(typeof result).toBe('object');
88
+ expect(result).not.toBeNull();
89
+ expect(result.name).toBe('Alice');
90
+ expect(result.age).toBe(30);
91
+ },
92
+ },
93
+ {
94
+ name: 'returns text[] response',
95
+ vibeCode: `let result: text[] = vibe "Return a JSON array of 3 colors: red, green, blue" testModel default`,
96
+ assert: (runtime: Runtime) => {
97
+ const result = runtime.getValue('result') as string[];
98
+ expect(Array.isArray(result)).toBe(true);
99
+ expect(result).toHaveLength(3);
100
+ expect(result).toContain('red');
101
+ expect(result).toContain('green');
102
+ expect(result).toContain('blue');
103
+ },
104
+ },
105
+ {
106
+ name: 'returns number[] response',
107
+ vibeCode: `let result: number[] = vibe "Return a JSON array of the first 5 prime numbers" testModel default`,
108
+ assert: (runtime: Runtime) => {
109
+ const result = runtime.getValue('result') as number[];
110
+ expect(Array.isArray(result)).toBe(true);
111
+ expect(result).toHaveLength(5);
112
+ expect(result).toEqual([2, 3, 5, 7, 11]);
113
+ },
114
+ },
115
+ {
116
+ name: 'returns boolean[] response',
117
+ vibeCode: `let result: boolean[] = vibe "Return a JSON array: [true, false, true]" testModel default`,
118
+ assert: (runtime: Runtime) => {
119
+ const result = runtime.getValue('result') as boolean[];
120
+ expect(Array.isArray(result)).toBe(true);
121
+ expect(result).toEqual([true, false, true]);
122
+ },
123
+ },
124
+ {
125
+ name: 'returns json[] response',
126
+ vibeCode: `let result: json[] = do "Return a JSON array with 2 objects: first with name Alice and age 30, second with name Bob and age 25" testModel default`,
127
+ assert: (runtime: Runtime) => {
128
+ const result = runtime.getValue('result') as Array<Record<string, unknown>>;
129
+ expect(Array.isArray(result)).toBe(true);
130
+ expect(result).toHaveLength(2);
131
+ expect(result[0].name).toBe('Alice');
132
+ expect(result[0].age).toBe(30);
133
+ expect(result[1].name).toBe('Bob');
134
+ expect(result[1].age).toBe(25);
135
+ },
136
+ },
137
+ ];
138
+
139
+ async function runVibe(modelConfig: string, vibeCode: string, logAi = true): Promise<Runtime> {
140
+ const program = parse(modelConfig + vibeCode);
141
+ const runtime = new Runtime(
142
+ program,
143
+ createRealAIProvider(() => runtime.getState()),
144
+ { logAiInteractions: logAi }
145
+ );
146
+ await runtime.run();
147
+
148
+ if (logAi) {
149
+ const interactions = runtime.getAIInteractions();
150
+ console.log('\n' + formatAIInteractions(interactions));
151
+ }
152
+
153
+ return runtime;
154
+ }
155
+
156
+ // Generate tests for each provider
157
+ for (const provider of providers) {
158
+ describe.skipIf(!provider.hasKey)(`${provider.name} Integration - Return Types`, () => {
159
+ for (const testCase of testCases) {
160
+ test(testCase.name, async () => {
161
+ const runtime = await runVibe(provider.modelConfig, testCase.vibeCode);
162
+ testCase.assert(runtime);
163
+ }, 30000);
164
+ }
165
+ });
166
+ }
@@ -0,0 +1,12 @@
1
+ // Basic AI Integration Test
2
+ // Tests the most fundamental AI operation: a simple prompt
3
+
4
+ model testModel = {
5
+ name: "gpt-5-mini",
6
+ apiKey: OPENAI_API_KEY,
7
+ url: "https://api.openai.com/v1",
8
+ provider: "openai"
9
+ }
10
+
11
+ // Test: Basic text response - model should reply with exactly "PONG"
12
+ let greeting = vibe "Reply with exactly: PONG" testModel default
@@ -0,0 +1,201 @@
1
+ // Bug Fix Integration Test
2
+ // Tests that AI can use tools to find and fix bugs in real code
3
+
4
+ import { describe, test, expect, beforeAll, afterAll, beforeEach } from 'bun:test';
5
+ import { Runtime, formatAIInteractions } from '../../../src/runtime';
6
+ import { createRealAIProvider } from '../../../src/runtime/ai-provider';
7
+ import { parse } from '../../../src/parser/parse';
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ // API Keys from environment
12
+ const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
13
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
14
+ const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
15
+
16
+ // Test directory setup
17
+ const BASE_WORKSPACE = path.join(__dirname, '.test-workspace');
18
+
19
+ // The buggy code - sumArray has an off-by-one error (starts at index 1 instead of 0)
20
+ const BUGGY_CODE = `// Array utilities module
21
+
22
+ export function sumArray(numbers: number[]): number {
23
+ // Bug: loop starts at index 1, skipping the first element
24
+ let total = 0;
25
+ for (let i = 1; i < numbers.length; i++) {
26
+ total += numbers[i];
27
+ }
28
+ return total;
29
+ }
30
+
31
+ export function findMax(numbers: number[]): number {
32
+ if (numbers.length === 0) {
33
+ return 0;
34
+ }
35
+ // This function is correct - starts at 1 because max is initialized with numbers[0]
36
+ let max = numbers[0];
37
+ for (let j = 1; j < numbers.length; j++) {
38
+ if (numbers[j] > max) {
39
+ max = numbers[j];
40
+ }
41
+ }
42
+ return max;
43
+ }
44
+ `;
45
+
46
+ // Generate Vibe program with provider-specific model config
47
+ function createVibeProgram(modelConfig: string): string {
48
+ return `
49
+ import { readFile, edit } from "system/tools"
50
+
51
+ ${modelConfig}
52
+
53
+ let result: text = vibe "There's a file 'buggy-code.ts' with array utility functions.
54
+
55
+ A test is failing: sumArray([1, 2, 3, 4, 5]) returns 14 but should return 15.
56
+
57
+ Read the file, find the bug, and fix it. After fixing, respond with 'FIXED'." fixer default
58
+
59
+ result
60
+ `;
61
+ }
62
+
63
+ // Core test function that runs the bug fix test for a given provider
64
+ async function runBugFixTest(
65
+ providerName: string,
66
+ modelConfig: string,
67
+ workspace: string
68
+ ): Promise<void> {
69
+ const buggyFile = path.join(workspace, 'buggy-code.ts');
70
+
71
+ // Ensure workspace exists and reset the buggy file
72
+ fs.mkdirSync(workspace, { recursive: true });
73
+ fs.writeFileSync(buggyFile, BUGGY_CODE);
74
+
75
+ // Verify the code is buggy
76
+ const buggyModule = await import(`${buggyFile}?t=${Date.now()}`);
77
+ const buggyResult = buggyModule.sumArray([1, 2, 3, 4, 5]);
78
+ console.log(`\n[${providerName}] Buggy sumArray([1,2,3,4,5]) = ${buggyResult} (expected 15)`);
79
+ expect(buggyResult).toBe(14);
80
+
81
+ // Run the Vibe program
82
+ const program = parse(createVibeProgram(modelConfig));
83
+ const runtime = new Runtime(
84
+ program,
85
+ createRealAIProvider(() => runtime.getState()),
86
+ { logAiInteractions: true, rootDir: workspace }
87
+ );
88
+
89
+ await runtime.run();
90
+
91
+ // Log AI interactions
92
+ const state = runtime.getState();
93
+ console.log('\n' + formatAIInteractions(state.aiInteractions));
94
+
95
+ // Verify tool calls were made
96
+ const hasToolCalls = state.callStack.some(frame =>
97
+ frame.orderedEntries.some(entry =>
98
+ entry.kind === 'prompt' && entry.toolCalls && entry.toolCalls.length > 0
99
+ )
100
+ );
101
+ console.log(`\n[${providerName}] Tool calls made: ${hasToolCalls}`);
102
+ expect(hasToolCalls).toBe(true);
103
+
104
+ // Verify the fix
105
+ const fixedCode = fs.readFileSync(buggyFile, 'utf-8');
106
+ console.log(`\n[${providerName}] Fixed code:\n`, fixedCode);
107
+ expect(fixedCode).toContain('i = 0');
108
+
109
+ // Test the fixed code
110
+ const fixedModule = await import(`${buggyFile}?t=${Date.now()}`);
111
+ const fixedResult = fixedModule.sumArray([1, 2, 3, 4, 5]);
112
+ console.log(`[${providerName}] Fixed sumArray([1,2,3,4,5]) = ${fixedResult}`);
113
+ expect(fixedResult).toBe(15);
114
+ }
115
+
116
+ // Test findMax still works after fix
117
+ async function testFindMaxAfterFix(workspace: string): Promise<void> {
118
+ const buggyFile = path.join(workspace, 'buggy-code.ts');
119
+ const fixedModule = await import(`${buggyFile}?t=${Date.now()}`);
120
+ expect(fixedModule.findMax([3, 1, 4, 1, 5, 9, 2, 6])).toBe(9);
121
+ expect(fixedModule.findMax([1])).toBe(1);
122
+ expect(fixedModule.findMax([])).toBe(0);
123
+ }
124
+
125
+ // Cleanup workspace
126
+ function cleanupWorkspace(workspace: string): void {
127
+ if (fs.existsSync(workspace)) {
128
+ fs.rmSync(workspace, { recursive: true });
129
+ }
130
+ }
131
+
132
+ // OpenAI Tests
133
+ describe.skipIf(!OPENAI_API_KEY)('Bug Fix Integration - OpenAI', () => {
134
+ const workspace = path.join(BASE_WORKSPACE, 'openai');
135
+ const modelConfig = `
136
+ model fixer = {
137
+ name: "gpt-5-mini",
138
+ apiKey: "${OPENAI_API_KEY}",
139
+ url: "https://api.openai.com/v1",
140
+ provider: "openai",
141
+ tools: [readFile, edit]
142
+ }
143
+ `;
144
+
145
+ afterAll(() => cleanupWorkspace(workspace));
146
+
147
+ test('AI diagnoses and fixes bug using tools', async () => {
148
+ await runBugFixTest('OpenAI', modelConfig, workspace);
149
+ }, 120000);
150
+
151
+ test('findMax function still works after fix', async () => {
152
+ await testFindMaxAfterFix(workspace);
153
+ }, 10000);
154
+ });
155
+
156
+ // Anthropic Tests
157
+ describe.skipIf(!ANTHROPIC_API_KEY)('Bug Fix Integration - Anthropic', () => {
158
+ const workspace = path.join(BASE_WORKSPACE, 'anthropic');
159
+ const modelConfig = `
160
+ model fixer = {
161
+ name: "claude-haiku-4-5",
162
+ apiKey: "${ANTHROPIC_API_KEY}",
163
+ url: "https://api.anthropic.com",
164
+ provider: "anthropic",
165
+ tools: [readFile, edit]
166
+ }
167
+ `;
168
+
169
+ afterAll(() => cleanupWorkspace(workspace));
170
+
171
+ test('AI diagnoses and fixes bug using tools', async () => {
172
+ await runBugFixTest('Anthropic', modelConfig, workspace);
173
+ }, 120000);
174
+
175
+ test('findMax function still works after fix', async () => {
176
+ await testFindMaxAfterFix(workspace);
177
+ }, 10000);
178
+ });
179
+
180
+ // Google Tests
181
+ describe.skipIf(!GOOGLE_API_KEY)('Bug Fix Integration - Google', () => {
182
+ const workspace = path.join(BASE_WORKSPACE, 'google');
183
+ const modelConfig = `
184
+ model fixer = {
185
+ name: "gemini-3-flash-preview",
186
+ apiKey: "${GOOGLE_API_KEY}",
187
+ provider: "google",
188
+ tools: [readFile, edit]
189
+ }
190
+ `;
191
+
192
+ afterAll(() => cleanupWorkspace(workspace));
193
+
194
+ test('AI diagnoses and fixes bug using tools', async () => {
195
+ await runBugFixTest('Google', modelConfig, workspace);
196
+ }, 120000);
197
+
198
+ test('findMax function still works after fix', async () => {
199
+ await testFindMaxAfterFix(workspace);
200
+ }, 10000);
201
+ });
@@ -0,0 +1,22 @@
1
+ // Array utilities module
2
+
3
+ export function sumArray(numbers: number[]): number {
4
+ let total = 0;
5
+ for (let i = 1; i < numbers.length; i++) {
6
+ total += numbers[i];
7
+ }
8
+ return total;
9
+ }
10
+
11
+ export function findMax(numbers: number[]): number {
12
+ if (numbers.length === 0) {
13
+ return 0;
14
+ }
15
+ let max = numbers[0];
16
+ for (let i = 1; i < numbers.length; i++) {
17
+ if (numbers[i] > max) {
18
+ max = numbers[i];
19
+ }
20
+ }
21
+ return max;
22
+ }
@@ -0,0 +1,21 @@
1
+ // Bug Fix Script
2
+ // AI diagnoses and fixes bugs - figures out which tools to use on its own
3
+
4
+ model fixer = {
5
+ name: "claude-haiku-4-5",
6
+ apiKey: env("ANTHROPIC_API_KEY"),
7
+ url: "https://api.anthropic.com",
8
+ provider: "anthropic"
9
+ }
10
+
11
+ let code = readFile("buggy-code.ts")
12
+
13
+ let result: text = vibe "Here's a TypeScript file 'buggy-code.ts':
14
+
15
+ {code}
16
+
17
+ A test is failing: sumArray([1, 2, 3, 4, 5]) returns 14 but should return 15.
18
+
19
+ Fix the bug. After fixing, respond with 'FIXED'." fixer default
20
+
21
+ result
@@ -0,0 +1,206 @@
1
+ // Compress Context Mode Integration Test
2
+ // Tests that compress mode calls AI to summarize loop context
3
+
4
+ import { describe, test, expect } from 'bun:test';
5
+ import { Runtime } from '../../../src/runtime';
6
+ import { createRealAIProvider } from '../../../src/runtime/ai-provider';
7
+ import { parse } from '../../../src/parser/parse';
8
+ import { buildLocalContext, formatContextForAI } from '../../../src/runtime/context';
9
+
10
+ // Use Google's Gemini Flash for cheap/fast summarization
11
+ const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
12
+ const shouldRun = !!GOOGLE_API_KEY;
13
+
14
+ describe.skipIf(!shouldRun)('Compress Integration', () => {
15
+ test(
16
+ 'compress summarizes loop iterations',
17
+ async () => {
18
+ const program = parse(`
19
+ model m = {
20
+ name: "gemini-3-flash-preview",
21
+ apiKey: "${GOOGLE_API_KEY}",
22
+ provider: "google"
23
+ }
24
+
25
+ // Simple loop that compress will summarize
26
+ let results: number[] = []
27
+ for i in [1, 2, 3, 4, 5] {
28
+ let squared = ts(i) { return i * i }
29
+ results.push(squared)
30
+ } compress
31
+
32
+ results
33
+ `);
34
+
35
+ const runtime = new Runtime(
36
+ program,
37
+ createRealAIProvider(() => runtime.getState()),
38
+ { logAiInteractions: true }
39
+ );
40
+
41
+ const result = await runtime.run();
42
+
43
+ // Verify the loop executed correctly
44
+ expect(result).toEqual([1, 4, 9, 16, 25]);
45
+
46
+ // Get final context
47
+ const state = runtime.getState();
48
+ const context = buildLocalContext(state);
49
+ const formatted = formatContextForAI(context);
50
+
51
+ console.log('\n=== FINAL CONTEXT ===');
52
+ console.log(formatted.text);
53
+
54
+ // Verify there's a summary in the context
55
+ const summaryEntry = context.find(e => e.kind === 'summary');
56
+ expect(summaryEntry).toBeDefined();
57
+ console.log('\n=== SUMMARY ===');
58
+ console.log((summaryEntry as { text: string }).text);
59
+
60
+ // The summary should exist and be non-empty
61
+ expect((summaryEntry as { text: string }).text.length).toBeGreaterThan(0);
62
+ },
63
+ 60000
64
+ );
65
+
66
+ test(
67
+ 'compress with custom prompt',
68
+ async () => {
69
+ const program = parse(`
70
+ model m = {
71
+ name: "gemini-3-flash-preview",
72
+ apiKey: "${GOOGLE_API_KEY}",
73
+ provider: "google"
74
+ }
75
+
76
+ let items: text[] = []
77
+ for word in ["apple", "banana", "cherry"] {
78
+ let upper: text = ts(word) { return word.toUpperCase() }
79
+ items.push(upper)
80
+ } compress("List the fruits that were processed")
81
+
82
+ items
83
+ `);
84
+
85
+ const runtime = new Runtime(
86
+ program,
87
+ createRealAIProvider(() => runtime.getState()),
88
+ { logAiInteractions: true }
89
+ );
90
+
91
+ const result = await runtime.run();
92
+
93
+ // Verify the loop executed correctly
94
+ expect(result).toEqual(['APPLE', 'BANANA', 'CHERRY']);
95
+
96
+ // Get final context and verify summary
97
+ const state = runtime.getState();
98
+ const context = buildLocalContext(state);
99
+ const summaryEntry = context.find(e => e.kind === 'summary');
100
+
101
+ expect(summaryEntry).toBeDefined();
102
+ const summaryText = (summaryEntry as { text: string }).text.toLowerCase();
103
+ console.log('\n=== SUMMARY ===');
104
+ console.log((summaryEntry as { text: string }).text);
105
+
106
+ // Summary should mention the fruits
107
+ expect(
108
+ summaryText.includes('apple') ||
109
+ summaryText.includes('banana') ||
110
+ summaryText.includes('cherry') ||
111
+ summaryText.includes('fruit')
112
+ ).toBe(true);
113
+ },
114
+ 60000
115
+ );
116
+
117
+ test(
118
+ 'compress with explicit model',
119
+ async () => {
120
+ const program = parse(`
121
+ model main = {
122
+ name: "gemini-3-flash-preview",
123
+ apiKey: "${GOOGLE_API_KEY}",
124
+ provider: "google"
125
+ }
126
+
127
+ model summarizer = {
128
+ name: "gemini-3-flash-preview",
129
+ apiKey: "${GOOGLE_API_KEY}",
130
+ provider: "google"
131
+ }
132
+
133
+ let total = 0
134
+ for n in [10, 20, 30] {
135
+ total = total + n
136
+ } compress(summarizer)
137
+
138
+ total
139
+ `);
140
+
141
+ const runtime = new Runtime(
142
+ program,
143
+ createRealAIProvider(() => runtime.getState()),
144
+ { logAiInteractions: true }
145
+ );
146
+
147
+ const result = await runtime.run();
148
+
149
+ // Verify the loop executed correctly
150
+ expect(result).toBe(60);
151
+
152
+ // Verify summary exists
153
+ const state = runtime.getState();
154
+ const context = buildLocalContext(state);
155
+ const summaryEntry = context.find(e => e.kind === 'summary');
156
+
157
+ expect(summaryEntry).toBeDefined();
158
+ console.log('\n=== SUMMARY ===');
159
+ console.log((summaryEntry as { text: string }).text);
160
+ },
161
+ 60000
162
+ );
163
+
164
+ test(
165
+ 'while loop with compress',
166
+ async () => {
167
+ const program = parse(`
168
+ model m = {
169
+ name: "gemini-3-flash-preview",
170
+ apiKey: "${GOOGLE_API_KEY}",
171
+ provider: "google"
172
+ }
173
+
174
+ let count = 0
175
+ let sum = 0
176
+ while (count < 4) {
177
+ count = count + 1
178
+ sum = sum + count
179
+ } compress
180
+
181
+ sum
182
+ `);
183
+
184
+ const runtime = new Runtime(
185
+ program,
186
+ createRealAIProvider(() => runtime.getState()),
187
+ { logAiInteractions: true }
188
+ );
189
+
190
+ const result = await runtime.run();
191
+
192
+ // sum = 1 + 2 + 3 + 4 = 10
193
+ expect(result).toBe(10);
194
+
195
+ // Verify summary exists
196
+ const state = runtime.getState();
197
+ const context = buildLocalContext(state);
198
+ const summaryEntry = context.find(e => e.kind === 'summary');
199
+
200
+ expect(summaryEntry).toBeDefined();
201
+ console.log('\n=== SUMMARY ===');
202
+ console.log((summaryEntry as { text: string }).text);
203
+ },
204
+ 60000
205
+ );
206
+ });