@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,514 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import {
4
+ createInitialState,
5
+ step,
6
+ stepN,
7
+ runUntilPause,
8
+ resumeWithAIResponse,
9
+ resumeWithUserInput,
10
+ serializeState,
11
+ deserializeState,
12
+ getStateSummary,
13
+ cloneState,
14
+ getNextInstruction,
15
+ stepUntilCondition,
16
+ stepUntilStatement,
17
+ stepUntilOp,
18
+ } from '../index';
19
+
20
+ describe('Functional Runtime - Step Execution', () => {
21
+ test('createInitialState creates valid state', () => {
22
+ const ast = parse('let x = "hello"');
23
+ const state = createInitialState(ast);
24
+
25
+ expect(state.status).toBe('running');
26
+ expect(state.callStack).toHaveLength(1);
27
+ expect(state.callStack[0].name).toBe('<entry>');
28
+ expect(state.instructionStack.length).toBeGreaterThan(0);
29
+ expect(state.lastResult).toBeNull();
30
+ });
31
+
32
+ test('step executes one instruction at a time', () => {
33
+ const ast = parse('let x = "hello"');
34
+ let state = createInitialState(ast);
35
+
36
+ // Initial: 1 exec_statement instruction
37
+ expect(state.instructionStack).toHaveLength(1);
38
+ expect(state.instructionStack[0].op).toBe('exec_statement');
39
+
40
+ // Step 1: exec_statement expands to exec_expression + declare_var
41
+ state = step(state);
42
+ expect(state.instructionStack.length).toBeGreaterThanOrEqual(2);
43
+
44
+ // Continue stepping until complete
45
+ // Need to keep stepping until status changes (step marks complete when no instructions left)
46
+ while (state.status === 'running') {
47
+ state = step(state);
48
+ }
49
+
50
+ expect(state.status).toBe('completed');
51
+ expect(state.callStack[0].locals['x']).toBeDefined();
52
+ expect(state.callStack[0].locals['x'].value).toBe('hello');
53
+ });
54
+
55
+ test('runUntilPause runs to completion for simple program', () => {
56
+ const ast = parse(`
57
+ let x = "hello"
58
+ let y = "world"
59
+ `);
60
+ let state = createInitialState(ast);
61
+ state = runUntilPause(state);
62
+
63
+ expect(state.status).toBe('completed');
64
+ expect(state.callStack[0].locals['x'].value).toBe('hello');
65
+ expect(state.callStack[0].locals['y'].value).toBe('world');
66
+ });
67
+
68
+ test('runUntilPause pauses at AI call', () => {
69
+ const ast = parse(`
70
+ model m = { name: "test", apiKey: "key", url: "http://test" }
71
+ let response = vibe "prompt" m default
72
+ `);
73
+ let state = createInitialState(ast);
74
+ state = runUntilPause(state);
75
+
76
+ expect(state.status).toBe('awaiting_ai');
77
+ expect(state.pendingAI).not.toBeNull();
78
+ expect(state.pendingAI?.type).toBe('vibe');
79
+ expect(state.pendingAI?.prompt).toBe('prompt');
80
+ });
81
+
82
+ test('runUntilPause pauses at second vibe call', () => {
83
+ const ast = parse(`
84
+ model m = { name: "test", apiKey: "key", url: "http://test" }
85
+ let input = vibe "question?" m default
86
+ `);
87
+ let state = createInitialState(ast);
88
+ state = runUntilPause(state);
89
+
90
+ expect(state.status).toBe('awaiting_ai');
91
+ expect(state.pendingAI).not.toBeNull();
92
+ expect(state.pendingAI?.type).toBe('vibe');
93
+ expect(state.pendingAI?.prompt).toBe('question?');
94
+ });
95
+ });
96
+
97
+ describe('Functional Runtime - Resume Execution', () => {
98
+ test('resumeWithAIResponse continues after vibe expression', () => {
99
+ const ast = parse(`
100
+ model m = { name: "test", apiKey: "key", url: "http://test" }
101
+ let response = vibe "prompt" m default
102
+ `);
103
+ let state = createInitialState(ast);
104
+ state = runUntilPause(state);
105
+
106
+ expect(state.status).toBe('awaiting_ai');
107
+
108
+ state = resumeWithAIResponse(state, 'AI response');
109
+ state = runUntilPause(state);
110
+
111
+ expect(state.status).toBe('completed');
112
+ // VibeValue: value is directly in .value
113
+ expect(state.callStack[0].locals['response'].value).toBe('AI response');
114
+ });
115
+
116
+ test('resumeWithAIResponse continues after second vibe expression', () => {
117
+ const ast = parse(`
118
+ model m = { name: "test", apiKey: "key", url: "http://test" }
119
+ let input = vibe "What is your name?" m default
120
+ `);
121
+ let state = createInitialState(ast);
122
+ state = runUntilPause(state);
123
+
124
+ expect(state.status).toBe('awaiting_ai');
125
+
126
+ state = resumeWithAIResponse(state, 'Alice');
127
+ state = runUntilPause(state);
128
+
129
+ expect(state.status).toBe('completed');
130
+ // VibeValue: value is directly in .value
131
+ expect(state.callStack[0].locals['input'].value).toBe('Alice');
132
+ });
133
+
134
+ test('multiple AI calls can be resumed sequentially', () => {
135
+ const ast = parse(`
136
+ model m = { name: "test", apiKey: "key", url: "http://test" }
137
+ let a = vibe "first" m default
138
+ let b = vibe "second" m default
139
+ `);
140
+ let state = createInitialState(ast);
141
+
142
+ // First pause
143
+ state = runUntilPause(state);
144
+ expect(state.status).toBe('awaiting_ai');
145
+ expect(state.pendingAI?.prompt).toBe('first');
146
+
147
+ // Resume and hit second pause
148
+ state = resumeWithAIResponse(state, 'response1');
149
+ state = runUntilPause(state);
150
+ expect(state.status).toBe('awaiting_ai');
151
+ expect(state.pendingAI?.prompt).toBe('second');
152
+
153
+ // Resume and complete
154
+ state = resumeWithAIResponse(state, 'response2');
155
+ state = runUntilPause(state);
156
+ expect(state.status).toBe('completed');
157
+
158
+ // VibeValue: value is directly in .value
159
+ expect(state.callStack[0].locals['a'].value).toBe('response1');
160
+ expect(state.callStack[0].locals['b'].value).toBe('response2');
161
+ });
162
+ });
163
+
164
+ describe('Functional Runtime - Serialization', () => {
165
+ test('serializeState produces valid JSON', () => {
166
+ const ast = parse('let x = "hello"');
167
+ const state = createInitialState(ast);
168
+
169
+ const json = serializeState(state);
170
+ expect(() => JSON.parse(json)).not.toThrow();
171
+ });
172
+
173
+ test('deserializeState restores state', () => {
174
+ const ast = parse('let x = "hello"');
175
+ const original = createInitialState(ast);
176
+
177
+ const json = serializeState(original);
178
+ const restored = deserializeState(json);
179
+
180
+ expect(restored.status).toBe(original.status);
181
+ expect(restored.callStack).toHaveLength(original.callStack.length);
182
+ expect(restored.instructionStack).toHaveLength(original.instructionStack.length);
183
+ });
184
+
185
+ test('state can be serialized mid-execution and resumed', () => {
186
+ const ast = parse(`
187
+ model m = { name: "test", apiKey: "key", url: "http://test" }
188
+ let response = vibe "prompt" m default
189
+ response
190
+ `);
191
+ let state = createInitialState(ast);
192
+ state = runUntilPause(state);
193
+
194
+ expect(state.status).toBe('awaiting_ai');
195
+
196
+ // Serialize paused state
197
+ const json = serializeState(state);
198
+
199
+ // Simulate restart - deserialize and resume
200
+ let restored = deserializeState(json);
201
+ restored = resumeWithAIResponse(restored, 'AI response');
202
+ restored = runUntilPause(restored);
203
+
204
+ expect(restored.status).toBe('completed');
205
+ // AI results are AIResultObject - access .value for primitive
206
+ expect(restored.lastResult.value).toBe('AI response');
207
+ });
208
+
209
+ test('cloneState creates independent copy', () => {
210
+ const ast = parse('let x = "hello"');
211
+ const original = createInitialState(ast);
212
+
213
+ const cloned = cloneState(original);
214
+
215
+ // Modify original
216
+ original.lastResult = 'modified';
217
+
218
+ // Clone should be unaffected
219
+ expect(cloned.lastResult).toBeNull();
220
+ });
221
+
222
+ test('getStateSummary provides useful debug info', () => {
223
+ const ast = parse(`
224
+ let x = "hello"
225
+ let y = "world"
226
+ `);
227
+ let state = createInitialState(ast);
228
+ state = runUntilPause(state);
229
+
230
+ const summary = getStateSummary(state);
231
+
232
+ expect(summary.status).toBe('completed');
233
+ expect(summary.currentFrame).toBe('<entry>');
234
+ expect(summary.variables).toHaveProperty('x');
235
+ expect(summary.variables).toHaveProperty('y');
236
+ });
237
+ });
238
+
239
+ describe('Functional Runtime - Execution Log', () => {
240
+ test('execution log tracks variable declarations', () => {
241
+ const ast = parse(`
242
+ let x = "hello"
243
+ let y = "world"
244
+ `);
245
+ let state = createInitialState(ast);
246
+ state = runUntilPause(state);
247
+
248
+ const letDeclarations = state.executionLog.filter(
249
+ (e) => e.instructionType === 'let_declaration'
250
+ );
251
+ expect(letDeclarations).toHaveLength(2);
252
+ expect(letDeclarations[0].details?.name).toBe('x');
253
+ expect(letDeclarations[1].details?.name).toBe('y');
254
+ });
255
+
256
+ test('execution log tracks AI requests', () => {
257
+ const ast = parse(`
258
+ model m = { name: "test", apiKey: "key", url: "http://test" }
259
+ let response = vibe "test prompt" m default
260
+ `);
261
+ let state = createInitialState(ast);
262
+ state = runUntilPause(state);
263
+
264
+ const aiRequests = state.executionLog.filter(
265
+ (e) => e.instructionType === 'ai_vibe_request'
266
+ );
267
+ expect(aiRequests).toHaveLength(1);
268
+ expect(aiRequests[0].details?.prompt).toBe('test prompt');
269
+ });
270
+
271
+ test('execution log tracks AI responses', () => {
272
+ const ast = parse(`
273
+ model m = { name: "test", apiKey: "key", url: "http://test" }
274
+ let response = vibe "test prompt" m default
275
+ `);
276
+ let state = createInitialState(ast);
277
+ state = runUntilPause(state);
278
+ state = resumeWithAIResponse(state, 'AI response');
279
+ state = runUntilPause(state);
280
+
281
+ const aiResponses = state.executionLog.filter(
282
+ (e) => e.instructionType === 'ai_vibe_response'
283
+ );
284
+ expect(aiResponses).toHaveLength(1);
285
+ expect(aiResponses[0].result).toBe('AI response');
286
+ });
287
+ });
288
+
289
+ describe('Functional Runtime - Complex Programs', () => {
290
+ test('function calls work with step execution', () => {
291
+ const ast = parse(`
292
+ function greet(name: text): text {
293
+ return "Hello, {name}!"
294
+ }
295
+ let result = greet("World")
296
+ `);
297
+ let state = createInitialState(ast);
298
+ state = runUntilPause(state);
299
+
300
+ expect(state.status).toBe('completed');
301
+ expect(state.callStack[0].locals['result'].value).toBe('Hello, World!');
302
+ });
303
+
304
+ test('if statements work correctly', () => {
305
+ const ast = parse(`
306
+ let x = true
307
+ let result = ""
308
+ if x {
309
+ result = "yes"
310
+ } else {
311
+ result = "no"
312
+ }
313
+ `);
314
+ let state = createInitialState(ast);
315
+ state = runUntilPause(state);
316
+
317
+ expect(state.status).toBe('completed');
318
+ expect(state.callStack[0].locals['result'].value).toBe('yes');
319
+ });
320
+
321
+ test('nested object literals work', () => {
322
+ const ast = parse(`
323
+ let data: json = {
324
+ user: {
325
+ name: "Alice",
326
+ settings: {
327
+ theme: "dark"
328
+ }
329
+ }
330
+ }
331
+ `);
332
+ let state = createInitialState(ast);
333
+ state = runUntilPause(state);
334
+
335
+ expect(state.status).toBe('completed');
336
+ const data = state.callStack[0].locals['data'].value as any;
337
+ expect(data.user.name).toBe('Alice');
338
+ expect(data.user.settings.theme).toBe('dark');
339
+ });
340
+
341
+ test('array literals work', () => {
342
+ const ast = parse(`
343
+ let items = ["a", "b", "c"]
344
+ `);
345
+ let state = createInitialState(ast);
346
+ state = runUntilPause(state);
347
+
348
+ expect(state.status).toBe('completed');
349
+ const items = state.callStack[0].locals['items'].value as any;
350
+ expect(items).toEqual(['a', 'b', 'c']);
351
+ });
352
+ });
353
+
354
+ describe('Functional Runtime - Pause at Any Instruction', () => {
355
+ test('getNextInstruction returns next instruction', () => {
356
+ const ast = parse(`
357
+ let x = "hello"
358
+ const y = "world"
359
+ `);
360
+ let state = createInitialState(ast);
361
+
362
+ const next = getNextInstruction(state);
363
+ expect(next).not.toBeNull();
364
+ expect(next?.op).toBe('exec_statement');
365
+ });
366
+
367
+ test('getNextInstruction returns null when complete', () => {
368
+ const ast = parse('let x = "hello"');
369
+ let state = createInitialState(ast);
370
+ state = runUntilPause(state);
371
+
372
+ const next = getNextInstruction(state);
373
+ expect(next).toBeNull();
374
+ });
375
+
376
+ test('stepN executes exactly N instructions', () => {
377
+ const ast = parse(`
378
+ let a = "1"
379
+ let b = "2"
380
+ let c = "3"
381
+ `);
382
+ let state = createInitialState(ast);
383
+
384
+ // Step 3 times from initial
385
+ state = stepN(state, 3);
386
+
387
+ // Should still be running (not complete yet)
388
+ expect(state.status).toBe('running');
389
+ expect(state.instructionStack.length).toBeGreaterThan(0);
390
+ });
391
+
392
+ test('stepUntilStatement pauses before specific statement type', () => {
393
+ const ast = parse(`
394
+ let x = "hello"
395
+ const y = "world"
396
+ let z = "!"
397
+ `);
398
+ let state = createInitialState(ast);
399
+
400
+ // Step until we're about to execute a ConstDeclaration
401
+ state = stepUntilStatement(state, 'ConstDeclaration');
402
+
403
+ // We should be paused just before the const declaration
404
+ const next = getNextInstruction(state);
405
+ expect(next?.op).toBe('exec_statement');
406
+ if (next?.op === 'exec_statement') {
407
+ expect(next.stmt.type).toBe('ConstDeclaration');
408
+ }
409
+
410
+ // x should already be declared
411
+ expect(state.callStack[0].locals['x']).toBeDefined();
412
+ expect(state.callStack[0].locals['x'].value).toBe('hello');
413
+
414
+ // y should NOT be declared yet
415
+ expect(state.callStack[0].locals['y']).toBeUndefined();
416
+ });
417
+
418
+ test('stepUntilOp pauses before specific instruction op', () => {
419
+ const ast = parse(`
420
+ let x = "hello"
421
+ `);
422
+ let state = createInitialState(ast);
423
+
424
+ // Step until we're about to execute declare_var
425
+ state = stepUntilOp(state, 'declare_var');
426
+
427
+ const next = getNextInstruction(state);
428
+ expect(next?.op).toBe('declare_var');
429
+
430
+ // Variable should not exist yet
431
+ expect(state.callStack[0].locals['x']).toBeUndefined();
432
+
433
+ // Now step once more to declare it
434
+ state = step(state);
435
+ expect(state.callStack[0].locals['x']).toBeDefined();
436
+ });
437
+
438
+ test('stepUntilCondition with custom predicate', () => {
439
+ const ast = parse(`
440
+ let a = "first"
441
+ let b = "second"
442
+ let target = "found"
443
+ let c = "third"
444
+ `);
445
+ let state = createInitialState(ast);
446
+
447
+ // Step until we're about to declare a variable named "target"
448
+ state = stepUntilCondition(state, (_state, next) => {
449
+ if (next?.op === 'declare_var') {
450
+ return next.name === 'target';
451
+ }
452
+ return false;
453
+ });
454
+
455
+ const next = getNextInstruction(state);
456
+ expect(next?.op).toBe('declare_var');
457
+ if (next?.op === 'declare_var') {
458
+ expect(next.name).toBe('target');
459
+ }
460
+
461
+ // Previous variables should be declared
462
+ expect(state.callStack[0].locals['a']?.value).toBe('first');
463
+ expect(state.callStack[0].locals['b']?.value).toBe('second');
464
+
465
+ // target should not be declared yet
466
+ expect(state.callStack[0].locals['target']).toBeUndefined();
467
+ });
468
+
469
+ test('can pause before AI call and inspect pending prompt', () => {
470
+ const ast = parse(`
471
+ model m = { name: "test", apiKey: "key", url: "http://test" }
472
+ let setup = "done"
473
+ let response = vibe "my prompt" m default
474
+ `);
475
+ let state = createInitialState(ast);
476
+
477
+ // Step until we're about to do the AI call
478
+ state = stepUntilOp(state, 'ai_vibe');
479
+
480
+ // We're paused just before the AI call
481
+ const next = getNextInstruction(state);
482
+ expect(next?.op).toBe('ai_vibe');
483
+
484
+ // setup should be done
485
+ expect(state.callStack[0].locals['setup']?.value).toBe('done');
486
+
487
+ // The prompt should be in lastResult (already evaluated)
488
+ expect(state.lastResult).toBe('my prompt');
489
+ });
490
+
491
+ test('serialization works at any pause point', () => {
492
+ const ast = parse(`
493
+ let a = "first"
494
+ const b = "second"
495
+ let c = "third"
496
+ `);
497
+ let state = createInitialState(ast);
498
+
499
+ // Pause before const declaration
500
+ state = stepUntilStatement(state, 'ConstDeclaration');
501
+
502
+ // Serialize at this point
503
+ const json = serializeState(state);
504
+
505
+ // Deserialize and continue
506
+ let restored = deserializeState(json);
507
+ restored = runUntilPause(restored);
508
+
509
+ expect(restored.status).toBe('completed');
510
+ expect(restored.callStack[0].locals['a']?.value).toBe('first');
511
+ expect(restored.callStack[0].locals['b']?.value).toBe('second');
512
+ expect(restored.callStack[0].locals['c']?.value).toBe('third');
513
+ });
514
+ });