@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,92 @@
1
+ // Destructuring Integration Tests
2
+ // Tests destructuring declarations with const and let using Google Gemini 3
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
+ const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
10
+
11
+ const modelConfig = `
12
+ model testModel = {
13
+ name: "gemini-3-flash-preview",
14
+ apiKey: "${GOOGLE_API_KEY}",
15
+ provider: "google"
16
+ }
17
+ `;
18
+
19
+ async function runVibe(vibeCode: string, logAi = true): Promise<Runtime> {
20
+ const program = parse(modelConfig + vibeCode);
21
+ const runtime = new Runtime(
22
+ program,
23
+ createRealAIProvider(() => runtime.getState()),
24
+ { logAiInteractions: logAi }
25
+ );
26
+ await runtime.run();
27
+
28
+ if (logAi) {
29
+ const interactions = runtime.getAIInteractions();
30
+ console.log('\n' + formatAIInteractions(interactions));
31
+ }
32
+
33
+ return runtime;
34
+ }
35
+
36
+ describe.skipIf(!GOOGLE_API_KEY)('Google Gemini 3 - Destructuring Declarations', () => {
37
+ test('const destructuring with multiple fields', async () => {
38
+ const runtime = await runVibe(`
39
+ const {name: text, age: number} = do "Return a person named Alice who is 25 years old" testModel default
40
+ `);
41
+ expect(runtime.getValue('name')).toBe('Alice');
42
+ expect(runtime.getValue('age')).toBe(25);
43
+ }, 30000);
44
+
45
+ test('let destructuring with multiple fields', async () => {
46
+ const runtime = await runVibe(`
47
+ let {city: text, population: number} = do "Return a city named Tokyo with population 14000000" testModel default
48
+ `);
49
+ expect(runtime.getValue('city')).toBe('Tokyo');
50
+ expect(runtime.getValue('population')).toBe(14000000);
51
+ }, 30000);
52
+
53
+ test('const destructuring with boolean field', async () => {
54
+ const runtime = await runVibe(`
55
+ const {valid: boolean, message: text} = do "Return valid as true and message as OK" testModel default
56
+ `);
57
+ expect(runtime.getValue('valid')).toBe(true);
58
+ expect(runtime.getValue('message')).toBe('OK');
59
+ }, 30000);
60
+
61
+ test('let destructuring with array fields', async () => {
62
+ const runtime = await runVibe(`
63
+ let {colors: text[], counts: number[]} = do "Return colors as [red, green, blue] and counts as [1, 2, 3]" testModel default
64
+ `);
65
+ const colors = runtime.getValue('colors') as string[];
66
+ const counts = runtime.getValue('counts') as number[];
67
+ expect(Array.isArray(colors)).toBe(true);
68
+ expect(colors).toEqual(['red', 'green', 'blue']);
69
+ expect(Array.isArray(counts)).toBe(true);
70
+ expect(counts).toEqual([1, 2, 3]);
71
+ }, 30000);
72
+
73
+ test('const destructuring with json field', async () => {
74
+ const runtime = await runVibe(`
75
+ const {user: json, active: boolean} = do "Return user as an object with name Alice and role admin, and active as true" testModel default
76
+ `);
77
+ const user = runtime.getValue('user') as Record<string, unknown>;
78
+ expect(typeof user).toBe('object');
79
+ expect(user.name).toBe('Alice');
80
+ expect(user.role).toBe('admin');
81
+ expect(runtime.getValue('active')).toBe(true);
82
+ }, 30000);
83
+
84
+ test('destructuring with three fields', async () => {
85
+ const runtime = await runVibe(`
86
+ const {x: number, y: number, z: number} = do "Return x as 10, y as 20, z as 30" testModel default
87
+ `);
88
+ expect(runtime.getValue('x')).toBe(10);
89
+ expect(runtime.getValue('y')).toBe(20);
90
+ expect(runtime.getValue('z')).toBe(30);
91
+ }, 30000);
92
+ });
@@ -0,0 +1,61 @@
1
+ // Multi-language Hello World Translator Integration Test
2
+ // Tests optional model/context syntax and lastUsedModel feature
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
+ const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
10
+
11
+ async function runVibe(code: string, logAi = true): Promise<Runtime> {
12
+ const program = parse(code);
13
+ const runtime = new Runtime(
14
+ program,
15
+ createRealAIProvider(() => runtime.getState()),
16
+ { logAiInteractions: logAi }
17
+ );
18
+ await runtime.run();
19
+
20
+ if (logAi) {
21
+ const interactions = runtime.getAIInteractions();
22
+ console.log('\n' + formatAIInteractions(interactions));
23
+ }
24
+
25
+ return runtime;
26
+ }
27
+
28
+ describe.skipIf(!GOOGLE_API_KEY)('Hello World Translator', () => {
29
+
30
+ test('translate Hello World to multiple languages', async () => {
31
+ // print() and env() are auto-imported core functions - no import needed
32
+ const code = `
33
+ model translator = {
34
+ name: "gemini-3-flash-preview",
35
+ provider: "google",
36
+ apiKey: env("GOOGLE_API_KEY")
37
+ }
38
+
39
+ // First call establishes lastUsedModel
40
+ let languages: text[] = do "List the major human languages(max 10) as a JSON array of strings."
41
+
42
+ // Loop uses lastUsedModel (translator) since no model specified
43
+ for language in languages {
44
+ let translated = do "Translate 'Hello World' into {language}. Return only the translation, nothing else."
45
+ print(translated)
46
+ }
47
+ `;
48
+
49
+ const runtime = await runVibe(code);
50
+
51
+ // Check that languages array was populated
52
+ const languages = runtime.getValue('languages') as string[];
53
+ expect(Array.isArray(languages)).toBe(true);
54
+ expect(languages.length).toBeGreaterThanOrEqual(1);
55
+
56
+ // The loop should have run and printed translations
57
+ expect(runtime.getState().status).toBe('completed');
58
+ }, 60000);
59
+
60
+
61
+ });
@@ -0,0 +1,261 @@
1
+ // Context Modes Integration Test
2
+ // Tests that forget mode properly cleans up inner loop context
3
+
4
+ import { describe, test, expect, beforeAll, afterAll } 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 { buildLocalContext, formatContextForAI } from '../../../src/runtime/context';
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+
12
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
13
+ const shouldRun = !!ANTHROPIC_API_KEY;
14
+
15
+ // Test directory setup
16
+ const TEST_WORKSPACE = path.join(__dirname, '.test-workspace-context');
17
+
18
+ // Test file contents
19
+ const SAMPLE_FILES = {
20
+ 'file1.txt': `Line one
21
+ Line two
22
+ Line three`,
23
+
24
+ 'file2.txt': `Alpha
25
+ Beta
26
+ Gamma`,
27
+ };
28
+
29
+ // Expected line lengths for verification
30
+ const EXPECTED_LENGTHS: Record<string, number[]> = {
31
+ 'file1.txt': [8, 8, 10],
32
+ 'file2.txt': [5, 4, 5],
33
+ };
34
+
35
+ // Function to generate Vibe program with configurable context mode
36
+ function createVibeProgram(innerLoopMode: 'forget' | 'verbose' | ''): string {
37
+ const modeKeyword = innerLoopMode ? ` ${innerLoopMode}` : '';
38
+
39
+ return `
40
+ import { glob, readFile, writeFile } from "system/tools"
41
+
42
+ model analyzer = {
43
+ name: "claude-haiku-4-5",
44
+ apiKey: "${ANTHROPIC_API_KEY}",
45
+ url: "https://api.anthropic.com",
46
+ provider: "anthropic",
47
+ tools: [glob, readFile, writeFile]
48
+ }
49
+
50
+ // Find all .txt files
51
+ let files: text[] = vibe "Find all .txt files in the current directory. Return just the filenames as an array." analyzer default
52
+
53
+ // Process each file
54
+ for file in files {
55
+ // Read the file
56
+ let content: text = vibe "Use the readFile tool to read '{file}'. Return the exact file contents with no formatting, no markdown, no code blocks - just the raw text." analyzer default
57
+
58
+ // Split content into lines using inline TS
59
+ let lines: text[] = ts(content) { return content.split('\\n'); }
60
+
61
+ // Process each line - TS calculates length
62
+ let annotatedLines: number[] = []
63
+ for line in lines {
64
+ let annotated: number = ts(line) { return (line ?? '').length }
65
+ annotatedLines.push(annotated)
66
+ }${modeKeyword}
67
+
68
+ // Write annotated file
69
+ vibe "update the file '{file}' with the annotated lines, so each line ends with its length in brackets like [5]" analyzer default
70
+ }
71
+
72
+ "done"
73
+ `;
74
+ }
75
+
76
+ describe.skipIf(!shouldRun)('Context Modes Integration', () => {
77
+ beforeAll(() => {
78
+ // Create test workspace and sample files
79
+ fs.mkdirSync(TEST_WORKSPACE, { recursive: true });
80
+ for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
81
+ fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
82
+ }
83
+ });
84
+
85
+ afterAll(() => {
86
+ // Clean up test workspace
87
+ if (fs.existsSync(TEST_WORKSPACE)) {
88
+ fs.rmSync(TEST_WORKSPACE, { recursive: true });
89
+ }
90
+ });
91
+
92
+ test(
93
+ 'forget mode cleans up inner loop iterations from context',
94
+ async () => {
95
+ // Run with forget mode on inner loop
96
+ const program = parse(createVibeProgram('forget'));
97
+ const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
98
+ logAiInteractions: true,
99
+ rootDir: TEST_WORKSPACE,
100
+ });
101
+
102
+ await runtime.run();
103
+
104
+ const state = runtime.getState();
105
+ console.log('\n=== FORGET MODE ===');
106
+ console.log(formatAIInteractions(state.aiInteractions));
107
+
108
+ // Get the final context
109
+ const context = buildLocalContext(state);
110
+ const formatted = formatContextForAI(context);
111
+ console.log('\n=== FINAL CONTEXT ===');
112
+ console.log(formatted.text);
113
+
114
+ // Verify files were processed correctly
115
+ for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
116
+ const filePath = path.join(TEST_WORKSPACE, filename);
117
+ const content = fs.readFileSync(filePath, 'utf-8').trim();
118
+ const lines = content.split('\n');
119
+
120
+ console.log(`\n--- ${filename} ---`);
121
+ console.log(content);
122
+
123
+ expect(lines.length).toBe(expectedLengths.length);
124
+
125
+ for (let i = 0; i < lines.length; i++) {
126
+ const match = lines[i].match(/\[(\d+)\]$/);
127
+ expect(match).not.toBeNull();
128
+ if (match) {
129
+ expect(parseInt(match[1], 10)).toBe(expectedLengths[i]);
130
+ }
131
+ }
132
+ }
133
+
134
+ // KEY ASSERTION: With forget mode, the inner loop iterations should NOT be in context
135
+ // The context should NOT contain individual "line" variable assignments from the inner loop
136
+ const contextText = formatted.text;
137
+
138
+ // Count how many times "- line (text):" appears - should be minimal with forget
139
+ const lineVarMatches = contextText.match(/- line \(text\):/g) || [];
140
+ console.log(`\nLine variable occurrences in context: ${lineVarMatches.length}`);
141
+
142
+ // With forget, we should NOT see accumulated line iterations
143
+ // Each file has 3 lines, 2 files = 6 iterations total
144
+ // With forget, none of these should persist in final context
145
+ expect(lineVarMatches.length).toBe(0);
146
+
147
+ // Also verify the annotatedLines array IS present (the final result)
148
+ expect(contextText).toContain('annotatedLines');
149
+ },
150
+ 300000
151
+ );
152
+
153
+ test(
154
+ 'verbose mode (default) keeps all inner loop iterations in context',
155
+ async () => {
156
+ // Reset test files
157
+ for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
158
+ fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
159
+ }
160
+
161
+ // Run with verbose mode (explicit) on inner loop
162
+ const program = parse(createVibeProgram('verbose'));
163
+ const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
164
+ logAiInteractions: true,
165
+ rootDir: TEST_WORKSPACE,
166
+ });
167
+
168
+ await runtime.run();
169
+
170
+ const state = runtime.getState();
171
+ console.log('\n=== VERBOSE MODE ===');
172
+ console.log(formatAIInteractions(state.aiInteractions));
173
+
174
+ // Get the final context
175
+ const context = buildLocalContext(state);
176
+ const formatted = formatContextForAI(context);
177
+ console.log('\n=== FINAL CONTEXT ===');
178
+ console.log(formatted.text);
179
+
180
+ // Verify files were processed correctly
181
+ for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
182
+ const filePath = path.join(TEST_WORKSPACE, filename);
183
+ const content = fs.readFileSync(filePath, 'utf-8').trim();
184
+ const lines = content.split('\n');
185
+
186
+ expect(lines.length).toBe(expectedLengths.length);
187
+
188
+ for (let i = 0; i < lines.length; i++) {
189
+ const match = lines[i].match(/\[(\d+)\]$/);
190
+ expect(match).not.toBeNull();
191
+ if (match) {
192
+ expect(parseInt(match[1], 10)).toBe(expectedLengths[i]);
193
+ }
194
+ }
195
+ }
196
+
197
+ // KEY ASSERTION: With verbose mode, the inner loop iterations SHOULD be in context
198
+ const contextText = formatted.text;
199
+
200
+ // Count how many times "- line (text):" appears
201
+ const lineVarMatches = contextText.match(/- line \(text\):/g) || [];
202
+ console.log(`\nLine variable occurrences in context: ${lineVarMatches.length}`);
203
+
204
+ // With verbose, we SHOULD see all line iterations
205
+ // Each file has 3 lines, 2 files = 6 iterations total
206
+ // But due to outer loop verbose behavior, we see all from both files
207
+ expect(lineVarMatches.length).toBeGreaterThan(0);
208
+ },
209
+ 300000
210
+ );
211
+
212
+ test(
213
+ 'compare token usage between forget and verbose modes',
214
+ async () => {
215
+ // Reset test files
216
+ for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
217
+ fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
218
+ }
219
+
220
+ // Run forget mode
221
+ const forgetProgram = parse(createVibeProgram('forget'));
222
+ const forgetRuntime = new Runtime(forgetProgram, createRealAIProvider(() => forgetRuntime.getState()), {
223
+ logAiInteractions: true,
224
+ rootDir: TEST_WORKSPACE,
225
+ });
226
+ await forgetRuntime.run();
227
+ const forgetState = forgetRuntime.getState();
228
+
229
+ // Calculate total tokens for forget mode
230
+ const forgetTokens = forgetState.aiInteractions?.reduce((sum, i) => sum + (i.usage?.inputTokens ?? 0), 0) ?? 0;
231
+
232
+ // Reset test files
233
+ for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
234
+ fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
235
+ }
236
+
237
+ // Run verbose mode
238
+ const verboseProgram = parse(createVibeProgram('verbose'));
239
+ const verboseRuntime = new Runtime(verboseProgram, createRealAIProvider(() => verboseRuntime.getState()), {
240
+ logAiInteractions: true,
241
+ rootDir: TEST_WORKSPACE,
242
+ });
243
+ await verboseRuntime.run();
244
+ const verboseState = verboseRuntime.getState();
245
+
246
+ // Calculate total tokens for verbose mode
247
+ const verboseTokens = verboseState.aiInteractions?.reduce((sum, i) => sum + (i.usage?.inputTokens ?? 0), 0) ?? 0;
248
+
249
+ console.log('\n=== TOKEN COMPARISON ===');
250
+ console.log(`Forget mode total input tokens: ${forgetTokens}`);
251
+ console.log(`Verbose mode total input tokens: ${verboseTokens}`);
252
+ console.log(`Difference: ${verboseTokens - forgetTokens} tokens`);
253
+ console.log(`Forget mode saves: ${((1 - forgetTokens / verboseTokens) * 100).toFixed(1)}%`);
254
+
255
+ // Forget mode should use fewer tokens (less context accumulation)
256
+ // Note: This might not always be true for small examples, but for larger ones it should be
257
+ console.log('\nBoth modes completed successfully!');
258
+ },
259
+ 600000 // 10 minute timeout for both runs
260
+ );
261
+ });
@@ -0,0 +1,148 @@
1
+ // Line Annotator Integration Test
2
+ // Tests tool calling, Vibe loops, and TS functions working together
3
+
4
+ import { describe, test, expect, beforeAll, afterAll } 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
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
12
+ const shouldRun = !!ANTHROPIC_API_KEY;
13
+
14
+ // Test directory setup
15
+ const TEST_WORKSPACE = path.join(__dirname, '.test-workspace');
16
+
17
+ // Test file contents (simplified for initial testing)
18
+ const SAMPLE_FILES = {
19
+ 'sample1.txt': `Hello world
20
+ Short
21
+ End`,
22
+
23
+ 'sample2.txt': `Apple
24
+ Banana
25
+ Cherry`,
26
+ };
27
+
28
+ // Expected line lengths for verification
29
+ const EXPECTED_LENGTHS: Record<string, number[]> = {
30
+ 'sample1.txt': [11, 5, 3],
31
+ 'sample2.txt': [5, 6, 6],
32
+ };
33
+
34
+ // Vibe program that processes files
35
+ const VIBE_PROGRAM = `
36
+ import { glob, readFile, writeFile } from "system/tools"
37
+
38
+ model analyzer = {
39
+ name: "claude-haiku-4-5",
40
+ apiKey: "${ANTHROPIC_API_KEY}",
41
+ url: "https://api.anthropic.com",
42
+ provider: "anthropic",
43
+ tools: [glob, readFile, writeFile]
44
+ }
45
+
46
+ // Find all .txt files
47
+ let files: text[] = vibe "Find all .txt files in the current directory. Return just the filenames as an array." analyzer default
48
+
49
+ // Process each file
50
+ for file in files {
51
+ // Read the file
52
+ let content: text = vibe "Use the readFile tool to read '{file}'. Return the exact file contents with no formatting, no markdown, no code blocks - just the raw text." analyzer default
53
+
54
+ // Split content into lines using inline TS
55
+ let lines: text[] = ts(content) { return content.split('\\n'); }
56
+
57
+ // Process each line - TS calculates length and annotates
58
+ let annotatedLines: number[] = []
59
+ for line in lines {
60
+ let annotated: number = ts(line) {return (line ?? '').length }
61
+ annotatedLines.push(annotated)
62
+ }
63
+ vibe "update the file '{file}' with the annotated lines, so each line ends with its length in brackets like [5]" analyzer default
64
+ }
65
+
66
+ "done"
67
+ `;
68
+
69
+ describe.skipIf(!shouldRun)('Line Annotator Integration', () => {
70
+ beforeAll(() => {
71
+ // Create test workspace and sample files
72
+ fs.mkdirSync(TEST_WORKSPACE, { recursive: true });
73
+ for (const [filename, content] of Object.entries(SAMPLE_FILES)) {
74
+ fs.writeFileSync(path.join(TEST_WORKSPACE, filename), content);
75
+ }
76
+ });
77
+
78
+ afterAll(() => {
79
+ // Clean up test workspace
80
+ if (fs.existsSync(TEST_WORKSPACE)) {
81
+ fs.rmSync(TEST_WORKSPACE, { recursive: true });
82
+ }
83
+ });
84
+
85
+ test(
86
+ 'AI annotates each line with its length using nested loops',
87
+ async () => {
88
+ // Verify initial files exist
89
+ for (const filename of Object.keys(SAMPLE_FILES)) {
90
+ expect(fs.existsSync(path.join(TEST_WORKSPACE, filename))).toBe(true);
91
+ }
92
+
93
+ // Run the Vibe program
94
+ const program = parse(VIBE_PROGRAM);
95
+ const runtime = new Runtime(program, createRealAIProvider(() => runtime.getState()), {
96
+ logAiInteractions: true,
97
+ rootDir: TEST_WORKSPACE,
98
+ });
99
+
100
+ await runtime.run();
101
+
102
+ // Log AI interactions
103
+ const state = runtime.getState();
104
+ console.log('\n' + formatAIInteractions(state.aiInteractions));
105
+
106
+ // Verify tool calls were made
107
+ const hasToolCalls = state.callStack.some((frame) =>
108
+ frame.orderedEntries.some(
109
+ (entry) => entry.kind === 'prompt' && entry.toolCalls && entry.toolCalls.length > 0
110
+ )
111
+ );
112
+ console.log(`\nTool calls made: ${hasToolCalls}`);
113
+ expect(hasToolCalls).toBe(true);
114
+
115
+ // Verify each file was annotated correctly
116
+ for (const [filename, expectedLengths] of Object.entries(EXPECTED_LENGTHS)) {
117
+ const filePath = path.join(TEST_WORKSPACE, filename);
118
+ const content = fs.readFileSync(filePath, 'utf-8').trim();
119
+ const lines = content.split('\n');
120
+
121
+ console.log(`\n--- ${filename} ---`);
122
+ console.log(content);
123
+
124
+ // Check line count matches
125
+ expect(lines.length).toBe(expectedLengths.length);
126
+
127
+ // Check each line ends with correct length annotation
128
+ for (let i = 0; i < lines.length; i++) {
129
+ const line = lines[i];
130
+ const expectedLength = expectedLengths[i];
131
+
132
+ // Line should end with [N] where N is the expected length
133
+ const match = line.match(/\[(\d+)\]$/);
134
+ expect(match).not.toBeNull();
135
+
136
+ if (match) {
137
+ const actualLength = parseInt(match[1], 10);
138
+ expect(actualLength).toBe(expectedLength);
139
+ }
140
+ }
141
+ }
142
+
143
+ // Program completed successfully
144
+ console.log('\nAll files annotated successfully!');
145
+ },
146
+ 300000
147
+ ); // 5 minute timeout for many AI calls
148
+ });
@@ -0,0 +1,75 @@
1
+ // Cumulative Sum Integration Test
2
+ // Simple test: AI adds numbers one at a time in a loop
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
+ const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
10
+ const shouldRun = !!ANTHROPIC_API_KEY;
11
+
12
+ // Generate random integers for the test
13
+ const numbers = Array.from({ length: 5 }, () => Math.floor(Math.random() * 20) - 5);
14
+ const bonusNumber = Math.floor(Math.random() * 10) + 1;
15
+ const expectedSum = numbers.reduce((a, b) => a + b, 0) + bonusNumber;
16
+
17
+ const VIBE_PROGRAM = `
18
+ model calc = {
19
+ name: "claude-haiku-4-5",
20
+ apiKey: "${ANTHROPIC_API_KEY}",
21
+ url: "https://api.anthropic.com",
22
+ provider: "anthropic"
23
+ }
24
+
25
+ let numbers: number[] = [${numbers.join(', ')}]
26
+ let sum: number = 0
27
+
28
+ for n in numbers {
29
+ let result: number = vibe "Add n to sum. Return only the number." calc default
30
+ sum = result
31
+ }
32
+
33
+ let bonus: number = ${bonusNumber}
34
+ let final: number = vibe "Add bonus to sum. Return only the number." calc default
35
+ `;
36
+
37
+ async function runTest(logAi = true): Promise<Runtime> {
38
+ const program = parse(VIBE_PROGRAM);
39
+ const runtime = new Runtime(
40
+ program,
41
+ createRealAIProvider(() => runtime.getState()),
42
+ { logAiInteractions: logAi }
43
+ );
44
+ await runtime.run();
45
+
46
+ if (logAi) {
47
+ console.log('\n' + formatAIInteractions(runtime.getAIInteractions()));
48
+ }
49
+
50
+ return runtime;
51
+ }
52
+
53
+ describe.skipIf(!shouldRun)('Cumulative Sum Integration', () => {
54
+ test('AI adds numbers correctly in a loop', async () => {
55
+ console.log(`\nTest numbers: [${numbers.join(', ')}] + bonus ${bonusNumber}`);
56
+ console.log(`Expected sum: ${expectedSum}`);
57
+
58
+ const runtime = await runTest();
59
+
60
+ const finalSum = runtime.getValue('final') as number;
61
+ console.log(`AI computed sum: ${finalSum}`);
62
+
63
+ // AI should compute the correct sum
64
+ expect(finalSum).toBe(expectedSum);
65
+
66
+ // Verify we made the right number of AI calls (loop + bonus)
67
+ const interactions = runtime.getAIInteractions();
68
+ expect(interactions.length).toBe(numbers.length + 1);
69
+
70
+ // Each interaction should be a number response
71
+ for (const interaction of interactions) {
72
+ expect(interaction.targetType).toBe('number');
73
+ }
74
+ }, 120000);
75
+ });