@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,344 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { parse } from '../../parser/parse';
3
+ import { createInitialState, resumeWithAIResponse, resumeWithAsyncResults } from '../state';
4
+ import { runUntilPause, step } from '../step';
5
+ import type { RuntimeState, VibeValue } from '../types';
6
+ import { createVibeValue } from '../types';
7
+ import { completeAsyncOperation } from '../async';
8
+
9
+ // Helper function to process scheduled async operations
10
+ function processScheduledAsync(s: RuntimeState, response: unknown): RuntimeState {
11
+ if (!s.pendingAsyncStarts || s.pendingAsyncStarts.length === 0) {
12
+ return s;
13
+ }
14
+
15
+ const starts = s.pendingAsyncStarts;
16
+ s = { ...s, pendingAsyncStarts: [] };
17
+
18
+ // Complete each operation with the mock response
19
+ const frame = s.callStack[s.callStack.length - 1];
20
+ const newLocals = { ...frame.locals };
21
+
22
+ for (const start of starts) {
23
+ const operation = s.asyncOperations.get(start.operationId);
24
+ if (operation) {
25
+ // Get existing variable to preserve its properties (isConst, isPrivate, etc.)
26
+ const existingVar = operation.variableName ? newLocals[operation.variableName] : null;
27
+ const result = createVibeValue(response, {
28
+ source: 'ai',
29
+ isConst: existingVar?.isConst,
30
+ isPrivate: existingVar?.isPrivate,
31
+ typeAnnotation: existingVar?.typeAnnotation,
32
+ });
33
+
34
+ operation.status = 'completed';
35
+ operation.result = result;
36
+ operation.endTime = Date.now();
37
+ s.pendingAsyncIds.delete(start.operationId);
38
+
39
+ // Update the variable with the result (preserving declaration properties)
40
+ if (operation.variableName) {
41
+ newLocals[operation.variableName] = result;
42
+ }
43
+ }
44
+ }
45
+
46
+ // Update the frame with new locals
47
+ return {
48
+ ...s,
49
+ callStack: [
50
+ ...s.callStack.slice(0, -1),
51
+ { ...frame, locals: newLocals },
52
+ ],
53
+ };
54
+ }
55
+
56
+ // Helper to run with mock AI responses (handles both sync and async AI calls)
57
+ function runWithMockAI(state: RuntimeState, response: unknown): RuntimeState {
58
+ let s = runUntilPause(state);
59
+
60
+ // Process any scheduled async ops immediately (even if completed)
61
+ s = processScheduledAsync(s, response);
62
+
63
+ // Loop to handle all types of pauses
64
+ while (s.status !== 'completed' && s.status !== 'error') {
65
+ // Handle awaiting_async (variable access needs pending result)
66
+ if (s.status === 'awaiting_async' && s.awaitingAsyncIds.length > 0) {
67
+ const results = new Map<string, VibeValue>();
68
+ for (const opId of s.awaitingAsyncIds) {
69
+ const op = s.asyncOperations.get(opId);
70
+ if (op?.result) {
71
+ results.set(opId, op.result);
72
+ } else {
73
+ // If operation not complete yet, complete it with mock response
74
+ const result = createVibeValue(response, { source: 'ai' });
75
+ results.set(opId, result);
76
+ if (op) {
77
+ op.status = 'completed';
78
+ op.result = result;
79
+ s.pendingAsyncIds.delete(opId);
80
+ }
81
+ }
82
+ }
83
+ s = resumeWithAsyncResults(s, results);
84
+ s = runUntilPause(s);
85
+ s = processScheduledAsync(s, response);
86
+ continue;
87
+ }
88
+
89
+ // Handle sync AI calls
90
+ if (s.status === 'awaiting_ai' && s.pendingAI) {
91
+ s = resumeWithAIResponse(s, response);
92
+ s = runUntilPause(s);
93
+ s = processScheduledAsync(s, response);
94
+ continue;
95
+ }
96
+
97
+ // Handle scheduled async operations
98
+ if (s.pendingAsyncStarts && s.pendingAsyncStarts.length > 0) {
99
+ s = processScheduledAsync(s, response);
100
+ s = runUntilPause(s);
101
+ continue;
102
+ }
103
+
104
+ // No more work to do
105
+ break;
106
+ }
107
+
108
+ return s;
109
+ }
110
+
111
+ describe('Async Execution', () => {
112
+ describe('async let declarations', () => {
113
+ test('async let with do expression executes', () => {
114
+ const ast = parse(`
115
+ model m = { name: "test", apiKey: "key", url: "http://test" }
116
+ async let x = do "prompt" m default
117
+ `);
118
+ let state = createInitialState(ast);
119
+ state = runWithMockAI(state, 'async result');
120
+
121
+ expect(state.status).toBe('completed');
122
+ expect(state.callStack[0].locals['x'].value).toBe('async result');
123
+ });
124
+
125
+ test('async let with type annotation executes', () => {
126
+ const ast = parse(`
127
+ model m = { name: "test", apiKey: "key", url: "http://test" }
128
+ async let x: text = do "prompt" m default
129
+ `);
130
+ let state = createInitialState(ast);
131
+ state = runWithMockAI(state, 'typed result');
132
+
133
+ expect(state.status).toBe('completed');
134
+ expect(state.callStack[0].locals['x'].value).toBe('typed result');
135
+ });
136
+
137
+ test('async let with private modifier executes', () => {
138
+ const ast = parse(`
139
+ model m = { name: "test", apiKey: "key", url: "http://test" }
140
+ async let private secret = do "get secret" m default
141
+ `);
142
+ let state = createInitialState(ast);
143
+ state = runWithMockAI(state, 'secret value');
144
+
145
+ expect(state.status).toBe('completed');
146
+ expect(state.callStack[0].locals['secret'].value).toBe('secret value');
147
+ expect(state.callStack[0].locals['secret'].isPrivate).toBe(true);
148
+ });
149
+
150
+ test('async let with ts block schedules async operation', () => {
151
+ const ast = parse(`
152
+ async let x = ts() { return 42; }
153
+ `);
154
+ let state = createInitialState(ast);
155
+ state = runUntilPause(state);
156
+
157
+ // Async TS blocks now schedule for parallel execution
158
+ // The program may complete but have pending async operations
159
+ expect(state.pendingAsyncIds.size).toBeGreaterThan(0);
160
+ // The variable should have a pending marker
161
+ expect(state.callStack[0].locals['x'].asyncOperationId).toBeDefined();
162
+ });
163
+ });
164
+
165
+ describe('async const declarations', () => {
166
+ test('async const with do expression executes', () => {
167
+ const ast = parse(`
168
+ model m = { name: "test", apiKey: "key", url: "http://test" }
169
+ async const x = do "prompt" m default
170
+ `);
171
+ let state = createInitialState(ast);
172
+ state = runWithMockAI(state, 'const result');
173
+
174
+ expect(state.status).toBe('completed');
175
+ expect(state.callStack[0].locals['x'].value).toBe('const result');
176
+ expect(state.callStack[0].locals['x'].isConst).toBe(true);
177
+ });
178
+
179
+ test('async const with type annotation executes', () => {
180
+ const ast = parse(`
181
+ model m = { name: "test", apiKey: "key", url: "http://test" }
182
+ async const x: number = do "give number" m default
183
+ `);
184
+ let state = createInitialState(ast);
185
+ state = runWithMockAI(state, 42);
186
+
187
+ expect(state.status).toBe('completed');
188
+ expect(state.callStack[0].locals['x'].value).toBe(42);
189
+ });
190
+ });
191
+
192
+ describe('async destructuring declarations', () => {
193
+ test('async let destructuring executes', () => {
194
+ const ast = parse(`
195
+ model m = { name: "test", apiKey: "key", url: "http://test" }
196
+ async let {name: text, age: number} = do "get person" m default
197
+ `);
198
+ let state = createInitialState(ast);
199
+ state = runWithMockAI(state, { name: 'Alice', age: 30 });
200
+
201
+ expect(state.status).toBe('completed');
202
+ expect(state.callStack[0].locals['name'].value).toBe('Alice');
203
+ expect(state.callStack[0].locals['age'].value).toBe(30);
204
+ });
205
+
206
+ test('async const destructuring executes', () => {
207
+ const ast = parse(`
208
+ model m = { name: "test", apiKey: "key", url: "http://test" }
209
+ async const {x: number, y: number} = do "get coords" m default
210
+ `);
211
+ let state = createInitialState(ast);
212
+ state = runWithMockAI(state, { x: 10, y: 20 });
213
+
214
+ expect(state.status).toBe('completed');
215
+ expect(state.callStack[0].locals['x'].value).toBe(10);
216
+ expect(state.callStack[0].locals['y'].value).toBe(20);
217
+ });
218
+ });
219
+
220
+ describe('async standalone statements (fire-and-forget)', () => {
221
+ test('async do statement executes', () => {
222
+ const ast = parse(`
223
+ model m = { name: "test", apiKey: "key", url: "http://test" }
224
+ async do "log something" m default
225
+ `);
226
+ let state = createInitialState(ast);
227
+ state = runWithMockAI(state, 'logged');
228
+
229
+ expect(state.status).toBe('completed');
230
+ });
231
+
232
+ test('async vibe statement executes', () => {
233
+ const ast = parse(`
234
+ model m = { name: "test", apiKey: "key", url: "http://test" }
235
+ async vibe "process data" m default
236
+ `);
237
+ let state = createInitialState(ast);
238
+ state = runWithMockAI(state, 'processed');
239
+
240
+ expect(state.status).toBe('completed');
241
+ });
242
+
243
+ test('async ts block statement schedules async operation', () => {
244
+ const ast = parse(`
245
+ async ts() { console.log("fire and forget"); }
246
+ `);
247
+ let state = createInitialState(ast);
248
+ state = runUntilPause(state);
249
+
250
+ // Fire-and-forget async should schedule operation for parallel execution
251
+ // Once implemented, this will have pending async ops
252
+ // For now, it still blocks (TODO: implement fire-and-forget)
253
+ expect(state.pendingAsyncIds.size).toBeGreaterThanOrEqual(0);
254
+ });
255
+ });
256
+
257
+ describe('multiple async declarations', () => {
258
+ test('multiple async lets schedule in parallel', () => {
259
+ const ast = parse(`
260
+ model m = { name: "test", apiKey: "key", url: "http://test" }
261
+ async let a = do "1" m default
262
+ async let b = do "2" m default
263
+ async let c = do "3" m default
264
+ `);
265
+ let state = createInitialState(ast);
266
+
267
+ // Run until pause - all three async lets should schedule without blocking
268
+ state = runUntilPause(state);
269
+
270
+ // Should complete (not block on awaiting_ai) with 3 pending async operations
271
+ expect(state.status).toBe('completed');
272
+ expect(state.pendingAsyncStarts.length).toBe(3);
273
+ expect(state.asyncOperations.size).toBe(3);
274
+ expect(state.pendingAsyncIds.size).toBe(3);
275
+
276
+ // Variables should have pending markers
277
+ expect(state.callStack[0].locals['a'].asyncOperationId).toBeDefined();
278
+ expect(state.callStack[0].locals['b'].asyncOperationId).toBeDefined();
279
+ expect(state.callStack[0].locals['c'].asyncOperationId).toBeDefined();
280
+ });
281
+
282
+ test('multiple async lets get results when processed', () => {
283
+ const ast = parse(`
284
+ model m = { name: "test", apiKey: "key", url: "http://test" }
285
+ async let a = do "1" m default
286
+ async let b = do "2" m default
287
+ async let c = do "3" m default
288
+ `);
289
+ let state = createInitialState(ast);
290
+
291
+ // Use the helper which processes scheduled async operations
292
+ state = runWithMockAI(state, 'parallel_result');
293
+
294
+ expect(state.status).toBe('completed');
295
+ expect(state.callStack[0].locals['a'].value).toBe('parallel_result');
296
+ expect(state.callStack[0].locals['b'].value).toBe('parallel_result');
297
+ expect(state.callStack[0].locals['c'].value).toBe('parallel_result');
298
+ });
299
+
300
+ test('mixed async and sync declarations execute correctly', () => {
301
+ const ast = parse(`
302
+ model m = { name: "test", apiKey: "key", url: "http://test" }
303
+ let sync1 = "hello"
304
+ async let async1 = do "prompt" m default
305
+ const sync2 = 42
306
+ `);
307
+ let state = createInitialState(ast);
308
+ state = runWithMockAI(state, 'async value');
309
+
310
+ expect(state.status).toBe('completed');
311
+ expect(state.callStack[0].locals['sync1'].value).toBe('hello');
312
+ expect(state.callStack[0].locals['async1'].value).toBe('async value');
313
+ expect(state.callStack[0].locals['sync2'].value).toBe(42);
314
+ });
315
+ });
316
+
317
+ describe('async state tracking', () => {
318
+ test('maxParallel is set from options', () => {
319
+ const ast = parse(`let x = 1`);
320
+ const state = createInitialState(ast, { maxParallel: 8 });
321
+ expect(state.maxParallel).toBe(8);
322
+ });
323
+
324
+ test('default maxParallel is 4', () => {
325
+ const ast = parse(`let x = 1`);
326
+ const state = createInitialState(ast);
327
+ expect(state.maxParallel).toBe(4);
328
+ });
329
+
330
+ test('asyncOperations map is initialized', () => {
331
+ const ast = parse(`let x = 1`);
332
+ const state = createInitialState(ast);
333
+ expect(state.asyncOperations).toBeInstanceOf(Map);
334
+ expect(state.asyncOperations.size).toBe(0);
335
+ });
336
+
337
+ test('pendingAsyncIds set is initialized', () => {
338
+ const ast = parse(`let x = 1`);
339
+ const state = createInitialState(ast);
340
+ expect(state.pendingAsyncIds).toBeInstanceOf(Set);
341
+ expect(state.pendingAsyncIds.size).toBe(0);
342
+ });
343
+ });
344
+ });